パフォーマンスエンジニアリング、プロファイラ、そして見えないものを見ること
原文は Nelson Elhage により に公開されました。 このブログを購読する
最近、ゲイリー・クラインとロバート・ホフマンによる論文「Seeing the Invisible: Perceptual-Cognitive Aspects of Expertise」を知る機会があった。素晴らしい論文で、機会があればぜひ読むことをおすすめする。
クラインとホフマンは、エキスパートが「そこにないものを見る」能力について論じている。環境に現れているデータや手がかりを観察することに加え、エキスパートはそれらの手がかりが持つ含意を知覚するというのだ。たとえば、あるはずの情報や「典型的」な情報が欠けていること、観測されたデータが典型的か非典型的か、そしてある時点のスナップショットや限られた時間の観察から推測されるシステムの過去や未来の時間的推移などである。
本稿では、この論文のいくつかのアイデアを、パフォーマンスエンジニアリングという具体的な文脈で考えてみたい。とりわけ、プロファイラが何をしてくれて、何をしてくれないのかという話だ。この論文は、プロファイラがいかに有用でありながら、同時にパフォーマンスエンジニアリングのすべてではないのかを語る上で、うってつけのレンズになると思う。
プロファイラ
私はしばしば——ときに暗黙のうちに——「ソフトウェアを高速化するためにやることは、結局このループだけだ」という言説に遭遇する。
- プロファイラを実行する
- プロファイルの中で最も時間を食っている箇所を特定する
- そこを高速化する
しかし実際には、このループを文字どおりに実行しようとすると、非常にはやく収穫逓減に陥る。ソフトウェアは依然としていかにも「もっと速くなるはず」「もっと速くできるはず」という速度(あるいは単に自分たちが望む速度)よりもずっと遅いのに、どうすれば改善できるのかがさっぱりわからなくなるのだ。
この乖離にはいくつもの理由があると思うし、この現象だけで一ダースもの論考が書けるだろう。ただ、上述の論文を踏まえて、ここでは次の考えについて論じたい。
プロファイラが示せるのは「そこにあるもの」だけだ。巧みなパフォーマンスエンジニアリングには、プロファイルを形作り、そこに情報を与えているにもかかわらず、プロファイルそのものには「存在しない」情報を理解することが求められる。
見えないものを見る
これはどういうことか。プロファイルに「ある」情報と、「ない」情報とは何か。
プロファイラが示すのは「そこにあるもの」だ。プログラムのある特定の実行がどこで時間を費やしているかを、通常は関数やスタックフレームの単位で、非常に具体的かつ文字どおりに示してくれる。これは確かに有用な情報だ。
しかし、それだけでは足りない! 明白なバグによる分かりやすいホットスポットに偶然出くわすこともあるが、より多くのケースでは——特にプログラムがある程度すでに最適化された後は——次に何をすべきか、どのような最適化を試みるべきかを決めるために、追加の情報が必要になる。どこで時間が費やされているかを知るだけでは不十分で、その時間のうちどれが削減・除去可能で、どれほどの労力やリスクが伴うのかも理解しなければならないのだ。
具体例を挙げよう。プログラムが実行時間の80%をハッシュテーブルのルックアップに費やしているとわかったとする。これはハッシュテーブル自体を最適化すべきということなのか、それともルックアップの回数を減らすように設計を見直すべきということなのか。プロファイル自体は直接答えを教えてくれない。しかし、こうした情報をエキスパートなら、自身の専門知識や背景知識を統合することで、プロファイルから読み取ってしまうことがあるのだ。
別の角度から見てみよう。プロファイルは、プログラムがある操作にどれだけ時間を費やしたかを教えてくれる。しかし、その操作を最適化した場合にどれだけ時間がかかるようになるかは教えてくれない。
ある操作が遅いのは、それがプログラムの「本質的な仕事」を表しており、簡単には削減できない根本的な処理だからかもしれない。一方で、非本質的な理由で遅くなっているだけで、完全に取り除くことさえ可能かもしれない。プロファイラは、プログラムがその操作に時間を費やしていることは教えてくれるが、それがすでに十分最適化されているのか、あるいは除去したり他へ移したりできる類の処理なのかまでは教えてくれない。だがここでも、エキスパートがプロファイルを読めば、そうした問いに答えを得られることがあるのだ。
そもそもプロファイラが提供する分析の「フレーム」自体が、こうした問いを立てる上で最も有用なフレームとは限らない。プロファイラは情報を時間順や関数・スタックフレームごとのグループ化で整理する傾向があるが、別の整理の仕方のほうがはるかに生産的なことも多い。たとえば次のようなものだ。
時間の内訳をI/OとCPU時間で分析する方が重要な場合もある。プログラムは両者を交互に実行したり(あるいは様々な比率で並行に実行したり)するかもしれず、スタックフレームにはきれいに対応しないような、I/OやCPU時間の種別ごとの内訳が知りたいのだ。
上記の一般化とも言えるが、最も有用な軸が「プログラムがどのハードウェアリソースを、どの割合で使っているか」である場合もある。プログラムによっては、ここから考えられる視点は無数にある。
- マイクロアーキテクチャ上のリソース: CPUの実行ユニット、キャッシュ容量、デコーダの処理能力など
- GPU vs CPU vs PCIあるいはNVLinkのバス帯域
- イーサネット帯域 vs ローカルディスク帯域 vs CPUスループット
- その他、多数
物理的なスタックトレースとは別の軸や整理の枠組みが必要になる場合もある。
典型的な例が、CレベルのプロファイラでPythonプログラムをプロファイルしようとするケースだ。その場合、実行時間のほとんどが
_PyEval_EvalFrameで費やされていると表示されることが多い。確かにそのとおりではあるが、あまり役には立たない。有用な理解を得るには、プロファイルをPythonレベルのスタックトレースという領域へと「持ち上げ」る必要があるのだ(これは「コードを操作するコード」という形の自動化ツールに共通する問題でもあり、以前少し論じたことがある)。
専門性との相互作用
クラインの専門性に関する論考は、この状況を理解する上で有用な枠組みを与えてくれると思う。
とりわけ特定のアプリケーションやプログラミング言語、フレームワークに精通したドメインエキスパートは、プロファイルを一見して、上記のような問いに対する答えを「見て」取ることができる。たとえそれが文字どおりにはプロファイルに存在していなくてもだ。
彼らは行間を「読み解き」、プロファイラが文字どおりに答える問いで立ち止まるのではなく、「正しい」あるいは「より良い」問いを立て、答えることさえできる。さらに重要なことに、何らかの理由でそれができないとき、エキスパートはプロファイラが間違った問いを立てていることをはるかに速く認識し、同じプロファイルを眺めてどうにか理解が得られることを期待して頭を抱え続けるのではなく、別のツールを見つけたり作ったりするのだ。
この視点は、パフォーマンスエンジニアリングをめぐる言説や一般的な理解に見られる齟齬についても、説明を与えてくれると思う。熟練したパフォーマンスエンジニアを見ると、確かに彼らは多くの時間をプロファイルを眺めることに費やしている。ここから、「彼らが主にやっていることは、ある意味でそれなのだ」と結論づけたくなる。
しかし、よく見てみると、こうした熟練のパフォーマンスエンジニアは、初心者や素朴な観察者が想像するのとは異なる方法でプロファイラを使っているのだ。そしてその使い方は、プロファイルから異なる情報を見ているという能力に支えられている! 彼らがプロファイルを見るとき、そこに表示された文字どおりの情報だけを見ているのではない。むしろそれをいくつかある入力の一つとして、プログラムに対するモデルを補強・構築し、システムについてのメンタルモデルを更新・発展させ、さらなる最適化や(場合によっては異なるプロファイラでの)さらなるプロファイル実行を通じて検証する仮説を立て、定式化するために使っているのである。
記事をランダムに読む
コメント
ログインしてコメントする