コンパイラが特定のC++コードを最適化する方法に関する質問をしたところ、コンパイラが特定の最適化を実行したことを確認する方法についての質問を探していました。g ++()で生成されたアセンブリリストを調べて、内部でg++ -c -g -O2 -Wa,-ahl=file.s file.c
何が起こっているかを確認しようとしましたが、出力がわかりにくいです。この問題に取り組むために人々はどのようなテクニックを使用しますか?また、この問題について話しているGCCツールチェーンに固有の最適化されたコードまたは記事のアセンブリリストを解釈する方法に関する良い参考資料はありますか?
8 に答える
GCC の最適化パスは、 GIMPLEと呼ばれる形式のコードの中間表現で機能します。
-fdump-*
options ファミリを使用して、GCC にツリーの中間状態を出力するように指示できます。
たとえば、これをgcc -c -fdump-tree-all -O3
unsigned fib(unsigned n) {
if (n < 2) return n;
return fib(n - 2) + fib(n - 1);
}
単純な指数アルゴリズムから複雑な多項式アルゴリズムに徐々に変換される様子を観察してください。(本当!)
有用な手法は、優れたサンプリング プロファイラーでコードを実行することです。たとえば、Linux では Zoom を、Mac OS X では Instruments (Time Profiler インストゥルメントを使用) を使用します。これらのプロファイラーは、コード内のホットスポットを表示するだけでなく、ソース コードを分解されたオブジェクトにマップします。コード。ソース行を強調表示すると、そのソース行にマップされる (必ずしも連続している必要はない) 生成コードの行が表示されます (逆も同様です)。オンラインのオペコード リファレンスと最適化のヒントは、うれしいおまけです。
- 楽器: developer.apple.com
- ズーム: www.rotateright.com
gcc ではありませんが、Visual Studio でデバッグする場合、アセンブリとソースを散在させるオプションがあり、これにより、どのステートメントに対して何が生成されたかがよくわかります。しかし、場合によっては、正しく整列されていないことがあります。
gcc ツール チェーンの出力でありobjdump -dS
、同じ粒度ではありません。 gcc を出力ソースとアセンブリに取得する方法に関するこの記事には、使用しているのと同じオプションがあります。
適用された最適化を出力するには、次を使用できます。
-fopt-info-optimized
適用されていないものを見るには
-fopt-info-missed
出力が標準エラーストリームに送信されることに注意してください。実際にそれを確認するには、それをリダイレクトする必要があります: (ヒント 2>&1 )
これは良い例です:
g++ -O3 -std=c++11 -march=native -mtune=native
-fopt-info-optimized h2d.cpp -o h2d 2>&1
h2d.cpp:225:3: note: loop vectorized
h2d.cpp:213:3: note: loop vectorized
h2d.cpp:198:3: note: loop vectorized
h2d.cpp:186:3: note: loop vectorized
-g
で適用すると、インターリーブされた出力を確認できますがobjdump -dS|c++filt
、そこまでは行きません。お楽しみください!
-L
オプション (例: )を追加すると、gcc -L -ahl
もう少し分かりやすいリストが提供される場合があります。
同等の MSVC オプションは次のとおりです/FAcs
(ソース、機械語、およびバイナリが点在し、いくつかの役立つコメントが含まれているため、少し優れています)。
About one third of my job consists of doing just what you're doing: juggling C code around and then looking at the assembly output to make sure it's been optimized correctly (which is preferred to just writing inline assembly all over the place).
Game-development blogs and articles can be a good resource for the topic since games are effectively real-time applications in constant memory -- I have some notes on it, so does Mike Acton, and others. I usually like to keep Intel's instruction set reference up in a window while going through listings.
The most helpful thing is to get a good ground-level understanding of assembly programming generally first -- not because you want to write assembly code, but because having done so makes reading disassembly much easier. I've had a hard time finding a good modern textbook though.
RotateRight ( http://rotateright.com ) からのズームは別の回答で言及されていますが、それを拡張すると、「コードブラウザー」と呼ばれるものでソースからアセンブリへのマッピングが表示されます。アセンブリのドキュメントもアプリに統合されているため、asm の専門家でなくても非常に便利です。また、アセンブリ リストには、いくつかの CPU タイプのコメントとタイミングの注釈が付けられています。
オブジェクトまたは実行可能ファイルを Zoom で開くだけで、コンパイラがコードに対して行った処理を確認できます。
ビクター、あなたの場合、探している最適化は、スタック上のローカルメモリの割り当てが小さいだけです。空のクラスによって使用されるスペースが最適化されて取り除かれると、関数の入り口での割り当てが小さくなり、関数の出口での割り当ての解除が小さくなるはずです。
一般的な質問に関しては、私はアセンブリ言語を 30 年以上 (一気に!) 読んで (そして書いて) いますが、特にコンパイラの出力を読むには練習が必要だということしか言えません。
アセンブラー・ダンプを読み取ろうとする代わりに、デバッガー内でプログラムを実行してください。実行を一時停止したり、命令を 1 ステップ実行したり、チェックするコードにブレークポイントを設定したりできます。多くのデバッガーは、元の C コードを生成されたアセンブリと一緒に表示できるため、コンパイラーがコードを最適化するために行ったことをより簡単に確認できます。
また、特定のコンパイラの最適化をテストしようとしている場合は、関心のある最適化に適合するコードのタイプを含む短いダミー関数を作成できます (それ以外はあまり重要ではありません。単純であるほど、アセンブリが読みやすくなります)。 )。最適化をオンにしてプログラムを 1 回コンパイルし、最適化をオフにしてプログラムを 1 回コンパイルします。ダミー関数用に生成されたアセンブリ コードをビルド間で比較すると、コンパイラのオプティマイザが何を行ったかがわかります。