前の記事がめっちゃ良くまとまっていて、 自分の動画(以後「並列プログラムの動画」と呼ぶ)なのに自分が語る資格があるのだろうか、とか考えてしまう。 相対的に一発撮りで何も考えずに作った自分の動画の粗が目立ってしまったが、メッセージ自体には価値はあると思いたい所。 なお、なんでいまさらC++?は、おっさんだからです…
歴史的な事とかコンテキストの説明には特に訂正したり付け足す事も無いので、自身の話をしてみます。
目の前のターゲットはiPad ProとMac Book
まず並列プログラムの動画を録っていた時、自分が頭で思い浮かべていた環境はなんだったのか。自分の目の前の開発のターゲットは、iPad ProとMacBook用アプリでした。特にメインはiPad Pro。
iPadでは、既にプロフェッショナルユースの制作に使うような物をユーザーがいろいろ激しく使い込んでいて、もっと大きなデータでもっと速く動いてほしいと強く思われている。 そうしたアプリはMac版もある場合が結構あって、iPadのアプリをMacBookでの動きと比較している。 新しいiPadを買ったら、よりMacBookに近づいて欲しいという期待がある。
そんなMacBookの方は16コアというか16HTで、プログラムから見れば16コア。 iPadは8コア。4コア+遅い4コア を8と呼ぶのはどうなのか、という向きもあるだろうけれど、 コーディングの難しさという視点では立派に8コア。
そもそもそういう用途で使うなら 4コア+遅い4コアという構成はどうなの?とか言いたい事はあるのかもしれないけれど、 ユーザーの期待は16HTにどこまで近づけるか、という事なので、アプリ開発者の我々は、与えられた物を最大限に使うしか無い、 というか使っても足りていない。
対応すべきコア数という観点では、現時点で8と16、近い将来にも動くと期待すると32コアも無いとは言えないくらいのゾーンに居ると思っているので、 今からアプリを作る時にはそのくらいの範囲はadaptiveに対応出来るように作る必要がある、と思っている。
4コアと8コアの違い
4コアまでは、Concurrentなプログラムの延長で書いても、それほどコアを無駄にしている感じは無かった。 何かOSが1.5コアくらい使って、自分たちが2コアくらい使って、たまに1コア余る事もあるけれど、使っているのは3/4だ。まぁそんなもんだろう、みたいな。 大きくあいた所をちょっと再構成してキュッキュッと重そうな物を裏にやれば、だいたい埋まっている感じにはなる。 よくよく見るとまだ結構詰められるのだけど、見なかった事にしてまぁいいか、という気分でいた。 morritaさんの普段の仕事の話を見ているともっと頑張って詰めてそうなので立場に依るのだろうけれど。
4コア時代の主な関心は、いわゆる「ジャンク」を取る事、つまりフレームレートを出す事だったと言えると思う。 実際2コアでジャンクを取り切るのは厳しかった事からも、4コア時代の関心事として割と妥当な所だった。
でも「8コア使ってMacBookに近づいたパフォーマンスをプロフェッショナルに届けよ」とか言われるようになると、もうちょっとCPUインテンシブな事もやっていかないといけない。
でも、8コア使い切るにはConcurrentにちょっと毛がはえたくらいでは厳しい。 8個って結構多くて、コードの構成が最初からそういう前提じゃないとすぐやる事が無いコアが出てきてしまって、全然スケールしてくれない。 リアルにアムダールの法則状態というか。
「iPadが8コアになった」というのは、自分的には大きなニュースで、 morritaさんが言ってた所のSEDA的にプログラムを変えなきゃいけない、 という大きなジャンプを強制された訳です。
この4コアと8コアのギャップみたいなのって、皆はどう思っていますかね? ビルドの時にninjaが全部使ってくれれば、他は気にしてないっすよ、とかそんな感じだったりします?
自分たちはどうしているか的な話
教科書的な話はまぁいいだろう、という事で、実際に自分がどうしているかの話も少し。
まず、もともと簡単に並列化出来る所はスレッドで並列化してある所が結構ある。 関数の外から見て区別無い形で並列化出来て、それなりにパフォーマンス的に重要な所は、結構頑張って並列化してある。 内部はまぁまぁスレッド同士で依存があるような物を、いろいろと工夫してちゃんとパフォーマンスを出している。
これまではそうやっていたのだけれど、最近は関数単位で閉じた形での並列化が難しい大物がだんだんと目立つようになってきて、 これをどうにかしていかないとなぁ、というのがプロジェクトを始めた時に置かれていた状況。 やはり8コア時代はもうちょっと真面目にやらないといけないと結論づけて、道具を頑張って揃える所から始めている。
道具立ての基礎となるライブラリはfuture-promise。 基本的には、follyのfuture-promiseを真似ているがもうちょっと単純化したものを自分たちでスクラッチから実装していて、これを基本に並列化を進めていっている。 現在は他と依存が少ない新規コードでfuture化したコードを書いていって、ちゃんとパフォーマンスが出る事を確認していっている段階。
下のスレッドプールはQtとかWindowsはシステムのものを、iOSはGCDを、それ以外はとりあえず現在はpthreadで自前実装した物につなげている。 STLじゃなくてpthreadなのは完全に歴史的事情で、STLに直してもいいんだけどなぁ、と思いながらpthreadを使っている。 スレッドプールのAPIはGCDを真似ているけれどもうちょっと単純な感じにしていて、コア数全部使うキューと、シリアルな事が保証されているキューがある感じにしている。 この辺はいろんな環境のいろんなスレッドプール事情を調べて、どれにもつなげられるように作ったつもり。
ちょっとモバイルが特殊なのはGUIスレッドが特別扱いな所。 自分たちはfutureを最終的にGUIスレッドで待てるようにしていて、 この待っている時にプログレスバーを回したり、イベントループに戻せるシステムでは戻したりしている。 他のスレッドからもworkitem的な物をポスト出来るようにはなっていて、待っている時にwork itemが来た場合はGUIスレッドで処理出来るような仕組みはある。 この辺はGUI環境ごとにいろいろあるので、うまくそれらに合わせらるように頑張ってはいる。
Android以外はシステムのスレッドプールがあるのでいいのだけれど、Androidはどうするもんですかねぇ。 AndroidもJava側には十分その辺の道具は揃っているのだけれど、JNI側ではどうするのがいいのだろうか。 あんまりボトルネックになってないので自作の奴でこのまま行くのでもいいのだけれど、この令和の時代に自作のスレッドプール使うのもなぁ…
みんなって意外とみんなじゃなかった
そんな訳でiPadとMacBookを頭に浮かべつつ「並列プログラムの動画」を作ったつもりなのだけれど、そうはいってもだいたい「みんな」が対象となるだろう、と思っていた。
最近8コアになった、というのはモバイル特有の事情であって、 サーバーなどでは8コアなんてはるか昔に通った所だ。 morritaさんのポストの最初の方に挙がってるような例で、8コア時代をどうすべきかなんてとっくに結論も出ている。 動画はモバイル向けに作ったものではあるけれど、基本的なアイデアはサーバー時代の結論を元にしているので、サーバーサイドの人でもそう違った事は考えていないんじゃないか、と。
モバイルとサーバーサイド両方って言ったらもう「みんな」と言っていいんじゃないか? GPUを使いまくる機械学習とか一部の分野は違うだろうけれど、それらは例外という事で。こんな風に考えていた。
でも、「Switch買ってよ!みんな持ってるよ!」とオカンに言うと、「みんなって誰よ!?」と問い詰められるものです。 動画を挙げた時も「どこの環境向けの話?」というフィードバックを受けました。 「そんなのみんなだろう」と思ったのだけれど、具体的に「みんな」とは誰かをまじめに考えてみると、 実はそれほど「みんな」でも無い気がしてきた。
パフォーマンスを突き詰めていけばサーバーサイドもモバイルもだいたい同じ話になる、 という認識は今でも変わってないけれど、パフォーマンスを突き詰めていく、 というのが、言われてみるとそれほど一般的では無い気もしてきた。 その辺が重要になってくるのは勝負がついた後の話であって、 一番重要なバリバリ競争をしているフェーズでは、まだProcessExecutorでお茶をにごしたりコンテナ増やして対応したりで凌いだりするよなぁ。
クラウドなんかだとグルーコード的なのが多かったり、Dataflowとか…はそんな詳しくないけれど、 自分の知ってるMapReduce世代を振り返るとシャッフルやReduceで詰まらないようにとかMapでなるべく各ノードで済ますとか、そういう頑張りの方が主で、そんなにコアを使い切るという話にはなってなかった気がする(突き詰めればなるはずだけれど)。
また機械学習も、例外と切り捨てるほどマイナーな分野とも言い切れない。 C++でクロスプラットフォームなライブラリ書いてアプリ作ってる人よりは多いかもしれない…
一方でサーバーサイドでRustとか流行るのは意外とCPUリソースが希少なのでは?という気もするのでこの辺の感覚は全然わからないのだけど、どうなんですかね?
なんにせよ、あまり対象外の分野を、どんどん「それは例外な分野だ」として切り離していくと、例外の方が多いんじゃないか、という気分になってきました。あんまりコアを使い切るのに悩むのは一般的では無いのかもしれない。
という事で最初の動画の対象は、大規模アクセスをさばく複雑なロジックを持つサービスと、 プロフェッショナル向けのiPad Proハイエンドアプリくらいなのかもしれない。
普通のプログラマ視点の GPU vs CPU
Parallelな話というとすぐGPUの話になりがちに思うのだけれど、その辺には結構不満がある。 熱とか電力とかも含めた計算能力という点では、GPUの勝利で勝負は随分昔に済んでいて、 使えるならGPUを有効活用する方が、CPUコアを使い切るよりも断然良い、という事に異論は無いのですが。
でも、一アプリプログラマとして見た時には、自分のアプリでGPUを活用するのは、なかなかに難しい。 いろいろなデバイスで動く汎用のアプリで、ユーザーから様々なデータがやってくる状況で、 GPUを有効活用する、というのは、 業界全体のグランドチャレンジというか、出来たらいい夢の話ではあるが、自分の手には余る。 なんとかしたいとは思っているのだけれど…
一方で、CPUのコアは複雑なアプリであればだいたい使い切る余地があるし、 自分程度の腕でも頑張ればちゃんと使い切れる。 そういう点ではCPUコアを使い切るのは今目の前にある現実であって、 日々の仕事の話と思っている。
コンペとかベンチとかではGPUがメインになっちゃうけれど、ユーザーが使う実際のアプリではやっぱりCPUがメインなんじゃないの? と思ってしまう。 GPUを有効活用出来ているゲームとか機械学習も、有効活用出来るまでにはいろいろな困難を乗り越えて来たのだろうけれど、 それでもやはりそれらの分野限定なんじゃないか。まぁうまく解決した人が居ると、もともとそういう分野だったからに見えるものですが。
とにかく、現状ではまずCPUコア使い切る方向で頑張るのが現実的なんじゃないか。 だから僕ら普通のプログラマの現代的な課題として、もっと普段からこのトピックの話を見かけてもいいのになぁ、と思うのだけれど。
その辺、皆はどう思いますかね?