4

私は (かなり大規模な) 既存のモノスレッド C アプリケーションに取り組んでいます。このコンテキストでは、特別な関数を呼び出すたびにカウンターをインクリメントするという非常に少数の追加作業を実行するようにアプリケーションを変更しました (この関数は ~ 80.000 回呼び出されます)。アプリケーションは、64 ビットの Linux カーネル 3.2.0-31-generic を -O3 オプションで実行する Ubuntu 12.04 でコンパイルされます。

驚くべきことに、インストルメント化されたバージョンのコードはより高速に実行されており、その理由を調査しています。実行時間を測定しclock_gettime(CLOCK_PROCESS_CPUTIME_ID)、代表的な結果を得るために、100 回の実行を超える平均実行時間の値を報告しています。さらに、外界からの干渉を避けるために、他のアプリケーションが実行されていないシステムでアプリケーションを起動することをできる限り試みました (ちなみに、CLOCK_PROCESS_CPUTIME_ID は実時間ではなくプロセス時間を返すため、他のアプリケーションは「すべき」です。理論はキャッシュにのみ影響し、プロセスの実行時間には直接影響しません)

私は「命令キャッシュ効果」を疑っていました。おそらく、少し大きい (数バイト) インストルメント化されたコードは、キャッシュ内で異なる方法でより適切に収まります。この仮説は考えられますか? valegrind --tool=cachegrind を使用していくつかのキャッシュ調査を試みましたが、残念ながら、インストルメント化されたバージョンでは (論理的なように) 初期バージョンよりも多くのキャッシュ ミスが発生しています。

このテーマに関するヒントや、インストルメント化されたコードがより高速に実行される理由を見つけるのに役立つ可能性のあるアイデアを歓迎します (一部の GCC 最適化は、あるケースでは利用でき、別のケースでは利用できません。なぜですか?, ...)

4

2 に答える 2

4

質問には詳細があまり含まれていないため、問題を調査する際に考慮すべきいくつかの要因のみをお勧めします.

いくつかの追加作業 (カウンターのインクリメントなど) によって、最適化を適用するかどうかに関するコンパイラーの決定が変わる可能性はほとんどありません。コンパイラは、完全な選択を行うのに十分な情報を常に持っているわけではありません。ボトルネックがコードサイズである場合、速度を最適化しようとする場合があります。処理するデータがあまりない場合、計算を自動ベクトル化しようとする場合があります。コンパイラは、処理するデータの種類や、コードを実行する CPU の正確なモデルを認識していない場合があります。

  1. カウンターをインクリメントすると、一部のループのサイズが大きくなり、ループの展開が妨げられる場合があります。これにより、コード サイズが減少する可能性があります (また、コードの局所性が向上します。これは、命令またはマイクロコード キャッシュ、またはループ バッファーに適しており、CPU が命令をすばやくフェッチ/デコードできるようにします)。
  2. カウンターをインクリメントすると、一部の関数のサイズが大きくなり、インライン展開が妨げられる場合があります。これにより、コード サイズも減少する可能性があります。
  3. カウンターをインクリメントすると、自動ベクトル化が妨げられる可能性があり、これによりコード サイズが減少する可能性があります。

この変更がコンパイラの最適化に影響しない場合でも、CPU によるコードの実行方法が変わる可能性があります。

  1. 分岐ターゲットでいっぱいのカウンターインクリメントコードを所定の位置に挿入すると、分岐ターゲットの密度が低くなり、分岐予測が改善される場合があります。
  2. 特定の分岐ターゲットの前にカウンターインクリメント コードを挿入すると、分岐ターゲットのアドレスがより適切に配置され、コードのフェッチが速くなる場合があります。
  3. 一部のデータが書き込まれた後、同じデータが再度ロードされる前にカウンター インクリメント コードを配置すると (ストアからロードへの転送が何らかの理由で機能しなかった場合)、ロード操作が早く完了する可能性があります。
  4. カウンター インクリメント コードを挿入すると、L1 データ キャッシュ内の同じバンクに対する 2 つの競合するロード試行を防ぐことができます。
  5. カウンター インクリメント コードを挿入すると、一部の CPU スケジューラの決定が変更され、一部の実行ポートがパフォーマンス クリティカルな命令にちょうど間に合うように使用可能になる場合があります。

コンパイラーの最適化の影響を調べるために、生成されたアセンブラー コードをカウンター インクリメント コードの追加前と追加後に比較できます。

CPU の影響を調査するには、プロセッサのパフォーマンス カウンターを検査できるプロファイラーを使用します。

于 2012-10-03T13:06:50.890 に答える
1

組み込みコンパイラでの私の経験から推測すると、コンパイラの最適化ツールは再帰的なタスクを探します。おそらく、追加のコードにより、コンパイラーはより再帰的なものを認識し、マシンコードの構造が異なったものになったのでしょう。コンパイラは、最適化のためにいくつかの奇妙なことを行います。一部の言語 (Perl だと思いますか?) では、「not not」条件は「true」条件よりも高速に実行されます。デバッグ ツールでは、コードとアセンブリの比較をシングル ステップで実行できますか? これにより、コンパイラが追加のタスクで何を行うことにしたかについての洞察が得られる可能性があります。

于 2012-10-03T11:39:46.720 に答える