はじめに
「◯◯と10年戦ってわかったこと」というポエムを書けば、他の人がその「◯◯」についてちゃんとした記事を書いてくれそうな気がしてきた。僕もポエムを書いてみたいが、10年戦ったものなんてあまりない。僕自身は全くHPCの人ではないのだけれど、HPCの人々と関わる仕事を一応10年くらい続けてきたので、HPCに関するポエムを書いてみる。これはあくまでポエムであって、当然だがフィクションで、いかなる実在の人物、現実のスパコン、メーカ、団体とも関係ないことはご留意されたい。
HPCってなに?
HPCというのは「High Performance Computing」の略で、日本語では「高性能計算」とか言うんですかね。こういう略語にありがちだけれど、HPCが意味する分野は人によって全然違う。データセンターなんかもHPCの分野なんだろうけれど、とりあえずここでは科学技術計算に用いるスパコンと、その周りの技術、人々をとりまとめてHPCと呼ぶことにする。で、多くの人がたぶん共有するであろう認識は「HPC を 研究する人」と「HPC で 研究する人」の間には深くて広い溝があるということ。
「HPC を 研究する人」は、どんなハードを作ればよいか、先進的なハードをどのように利用すればよいか、その性能を使いこなすためのソフトウェア(コンパイラやツール、ミドルウェアなどを含む)はどのようであるべきか、なんかを研究する人々。一言で言えば「スパコンはどうあるべきか」を考える人々。こういう分野は一般に「計算機科学」と呼ばれている。以下、ハードだけじゃなくて、コンパイラ、ライブラリ、ミドルウェア、OSなどのソフトウェアスタックも全て含めた意味で「スパコン」と言うことがあるかも。
「HPC で 研究する人」とは、要するにユーザ側の人。ツールやミドルウェアなんかも必要に応じて作ったりするけれど、あくまで目標はなんか科学的な成果を出すことであって、プログラムを書くことやハードウェアを利用することはその手段にすぎない。こっちは「スパコンはどう使うべきか」を考える人々。こちらの分野は一般に「計算科学」と呼ばれている。僕はどちらかといえばこっち側なので、以下「計算科学」の観点から書いていると承知されたい。
HPCの歴史と現状のようなもの
よく知りません。大雑把にいって古い意味でのベクトル機→分散並列メモリ→演算加速器とかいう流れなんですかね。おそらくメモリバンド幅に比べて計算が律速だった時代から、どこかで計算に比べてメモリバンド幅律速にかわったんだろうけど、僕が物心ついた時には既にほとんどメモリバンド幅律速だった気がする。あと、CPUとしては、いわゆるベクトルレジスタを用いたベクトル演算から、なんかOoOとかスーパースカラとかいろいろでてきたんだろうけれど知りません。とにかく現在ではCPUはマルチコア化が進み、一つの石の中に10個とか20個とかCPUコアがあって、さらにそれが複数のソケットで一つの共有メモリのノードを構成してる。さらに複数のノードをまたぐ計算をするので、必然的に分散メモリ環境を使うことになる。CPUコアの中では、SIMDの幅が広がり続けている。倍精度実数は64ビットレジスタで表現されているので、二つの演算をまとめて実行するなら128ビットの何かが必要になる。最近だとAVX512とかいって512ビットでなんかやってるみたい。所謂「理論ピーク性能」はSIMD幅を使いきった数字なので、SIMDをちゃんと使わないとちっとも性能がでない。なので、現在HPCプログラマは最低でも「分散メモリ(プロセス)並列」「共有メモリ(スレッド)並列」「SIMD並列」という三階層の全く異なる並列パラダイムを扱う必要があって、それだけでもかなりいかついのに、GPGPUみたいな演算加速器がついていると、さらに異なる並列パラダイムが増える、みたいなことになっている。
とっつきやすさ≠書きやすさ
で、三つも四つも階層があると、「そんなものを生で全部書きたくない」と思うのは自然だと思う。例えば分散メモリを普通に扱うならMPIを使って必要なときに必要なデータが必要なノードに存在するように明示的に通信を書いてやる必要があるかと思う。それは面倒なので、あたかも複数ノードにまたがる馬鹿でかいメモリ空間を一つのメモリに見せて、必要に応じて勝手に通信をやってくれるフレームワークを作りたくなるわけで、例えばPGAS(partitioned global address space)と呼ばれる理論モデルがあって、それを実現したフレームワークというかプログラム言語みたいのがいろいろある。
共有メモリ空間なら、例えばコンパイラの自動並列化機能とか、OpenMPなんかがある。こいつらは「このループを並列化してください」と指示するだけで、あとはよしなにやってくれる。
GPGPUでのプログラミングもまともにやるならたとえばCUDAなんかで書くことになって、それはMPIと同様にCPUとGPGPU間のデータの受け渡しを明示的に書いて、GPGPUでやらせる計算も明示的に書いて・・・ということになるんだろうけど、それは面倒だから「この部分をGPGPUでやってください」指示するだけで、あとはよしなにやってくれる、例えばOpenACCみたいなフレームワークがある。
これらはどちらも「ユーザから実際に行われる処理を隠蔽する」ことが目的になっている。理想的にはユーザはどのタイミングで通信が行われているかとか、後ろに何ノードあるかとか気にしなくてもいい。
で、こういうのはわりととっつきやすくて、うまく行けば何も書かずに、せいぜい数行追加するだけで、普通のプログラムがスレッド並列化されたり、分散メモリ環境で動いたり、GPGPUを使って加速したりする。じゃーそれでいいじゃんって思うんだけど、問題はほとんどの場合「うまくいかない」こと。例えばOpenMPなりOpenACCなりで思ったように性能がでなかった場合、まず「コンパイラがプログラムをどう解釈したか」を調べてやんないといけない。で、何かコンパイラが勘違いしていたら、その勘違いをただすためにディレクティブを追加したり、プログラムを書き換えたりしないといけない。で、またコンパイルメッセージみて、想定通りの解釈をしているかチェックしないといけないの。
これやってみるとわかるんだけど、ずっと続けてると「もういい!俺がやる!」って言いたくなるのよ。使えない部下に仕事任せるより、自分でやっちゃった方が早いでしょ?そもそも「これくらいの性能が出るはず」という知識がないと「思ったように性能が出ない」とか思えないはずで、「思ったような性能がでるためにはどういうコードが吐かれていなければならないか」も知っているはずで、逆にそういうコードを吐くように指示したりプログラム書き換えたりしなきゃいけなくて、それなら最初から自分で書いちゃった方が早い気がしてくるじゃないですか。
「MPIはクソ」と言っている人がいて、僕もクソとは思わないまでもあまり気の利いたライブラリだとは思わないけど、それでも「コンパイラなり並列言語なりが自分の思ったようなコードを吐くように指示を出す」よりは「自分が思うようなコードを書く」方が、ステップが一つ減って結局は楽な気がする。
いや、もちろんディレクティブ一発で効果的に性能が出るコードもあるんだろうけど、そういうコードって多分MPIとか使って明示的に書いてもたいして手間じゃないんだよね。
コデザインとか、どうなの?
HPCには「計算機科学」の人と「計算科学の人」がいて、前者がスパコン作る人、後者が使う人なんだけど、この両者が協力してスパコンを作りましょう、というのが「コデザイン」と呼ばれる何か。僕は詳細はよく知らないんだけど、それでも「HPC を 研究する人」と「HPC で 研究する人」の溝って結構深い。多分前者は「ユーザの言うこと聞いてるとスパコン作れない」って思ってるし、後者は「なんでお客さんであるユーザの望むスパコンを作ってくれないんだろう」って思ってる。
まず前提条件として、スパコンを作るなら「計算性能」や「電力性能」に優れたものを作りたくなるじゃないですか。そうすると、例えばCPUコア数が増えたりSIMD幅が増えたり演算加速装置がついたりと、ユーザプログラム側で工夫しないと性能が出ないものになっていく。でも、ユーザの希望は「プログラムをほとんど書き換えず、ハードを乗り換えるだけで計算が早くなって欲しい」と思ってる。この両者の溝は深い。
で、コデザインでその溝を埋めましょう的なことを考えるんだろうけど、それは双方向というよりは「ユーザ側からの要望を作る側の人に伝える」という形になりがち。「ユーザ側からの要望」というのは、「メモリバンド幅増やして下さい」とか「動作周波数を上げて下さい」みたいに、「金額あたりの理論ピーク性能を下げる向き」「性能あたりの電力消費を上げる向き」の提案になる。それらをそのまま聞いてたらなんも発展しないので、作る側の人が「ユーザの言うこと聞いてたらハード作れない」って思うのも無理はないと思う。
結局、こういう「コデザイン」って「私はここを我慢する。だからあなたはここを我慢してください」という双方向の交渉じゃないとうまくいかないんだろうな〜とは思うけど、そのためにはユーザ側にそれなりに計算機科学の知識が必要になるので、なんというか難しそうですね(小並感)。
結局「いまあるもの」を使うしか無い僕たちは
チューニングとか並列化とかやってると、必ずといって良いほど「そのうちちゃんとした並列言語/ライブラリ/フレームワーク/かしこいコンパイラ/ができるから、チューニングや並列化はオワコン」と言う人がいる。あと「MPIがクソっていうならちゃんとしたものを自分で作れば?」「C++コンパイラがクソだっていうならC++使わなきゃいいじゃん」って言ってくる人もいる。
まず、「ちゃんとした並列言語」はでてるのかもしれない。でも使えないのよ全然。使ってるスパコンサイトで対応してないとか、結局性能出すためにはMPIの知識必須とか、そんな感じ。「ちゃんとしたコンパイラ」については、「昔よりはちゃんとしてる」のかもしれないけど、やっぱりこういう欲望って際限ないので、自分が思うようなコード吐いてくれないと「このタコがっ!」って思っちゃうよね。あと、ハードの進展と、太り続ける言語の仕様についていけてないという印象がある。特にC/C++コンパイラは、規格が大きすぎて、実装するので精一杯、追加された言語機能のコンテキスト上での最適化までは手がまわりません、なんてなってる気がする。コンパイラ屋さんに聞いたことないから全く想像だけど。あと、C++はもう言語規格として古いわりに機能がものすごいあって、なんかいろいろガタが来てる印象(あくまで印象ですよ?)なんだけど、「じゃーFortranで書きますか?」と言われると辛いところもあって。
「それじゃ自分で作れよ」といわれるとそれもつらいんだけど、やっぱりあくまで目的は科学的成果を出すことで、そうすると新しい何かを作るコストと、現在あるもので苦労するコストを比較すると、新しい物を作るのはかなり厳しいな、という印象。僕らはぶりぶり文句いいながらもC++とMPIとOpenMPとintrinsic関数とCUDA(OpenCL?)でがんばるしかないんだ。
でも最近だとSciPy、NumPyといったライブラリが充実してきて、Pythonが来てるのかな、という気がする。Fortran/C/C++で書かれたライブラリを、Pythonから呼び出す、みたいなことが流行っていて、性能とかきやすさの両立、ワークライフバランス!みたいなのが実現しつつあるんですかね?スレッド並列やSIMD化、GPGPUについては後ろのライブラリががんばれば良いので、Pythonはインタフェースに徹してればいいんだけど、Pythonをフロントエンドにしたときにどこまで大規模並列ができるのかはどうなんですかね?よくわかりません。
HPCはほどほどに
たまに「どのくらいチューニングすればいいですか」とか「並列化とかどのくらいがんばればいいですか」と聞かれるんだけど、「基本、あんまりやらなくていいと思う」と答えるようにしてる。「計算科学」の目的は科学的な成果を出すことで、チューニングとかそういうのはその手段にすぎないから、というのは表向きの理由で、本当の理由は別にある。
・・・ここだけの話、HPCには人外が棲んでるですよ。
ここでいう人外というのは、要するにとても人間とは思えないプログラマのこと。なんでこんなコード書けるのか?とか、なんか頭の中で石のエミュレートしてんのか?って感じのプログラマ。だいたいにおいて、プログラマの力量って軽く桁で変わってきちゃうんだけど、特にHPCにおいてはそれがわかりやすく出てくる。
何がいいたいかっていうと、どんなにがんばっても人間は人外には勝てないのですよ。あきらめましょう。「人間にしてはよくやりますね」といったところまで来たら、その辺にしておくのがいいんじゃないかな。僕は少なくともそうしてます。
あと、なんかでかいスパコンが稼働開始するたびに「チューニングしないと人に非ず」みたいな雰囲気がでて、偉い先生方が「ベクトル化率」とか「スケーリング」とか口にしはじめて、「あれ?この流れにのらないと計算科学者としてやってけない?」って思う若人がいるかもしれないけど、気にしなくていいです。一年もすれば偉い先生方は「科学が大事」と言い出します。チューニングや並列化ができると武器にはなりますが、それで勝負しても人外にはどうしたって勝てないので、それはほどほどにして、別の複合分野で勝負したほうがいいんじゃないかな。
具体的には、そこそこのノード数(100ノード超えるくらいとか?)で実用的な非自明並列コードが書けて、まぁ理論的にだいたいこんなもんでしょ、というキャッシュ利用率がでるようなメモリまわりのチューニングが済んだら、そのあたりでいいと思う。SIMD化とかGPGPUとかガチでやるのは修羅の道。いや、もちろんその修羅道を登り切った先には何か桃源郷があるのかもしれないけど、僕は登ったことないので知らない。
おわりに
僕は一介のSEで、「HPCは〜」「計算科学は〜」とかえらそうに言える立場じゃないんだけど、それでも10年その分野をみてると思うところがあるのでポエムにしてみました。あくまで個人の意見だし、そもそもこれはフィクションなので、これに意見があっても直接コメントとかしないで、別のところにポエムとか書いてくれるとうれしいなぁ。後はもっとHPCわかってる人が「20年戦ってわかったこと」や「30年戦ってわかったこと」を書いてくれたら幸甚であります。