2

関連情報を尋ねる質問をたくさん検索しましたが、答えは私が答えたいと思っていたものと完全には一致しませんでした。できる限り問題を説明しようと思います。

基本的に、リリースモードでコードを実行すると、コンパイラは冗長またはデッドコードであるほとんどのコードを削除するように見えます。したがって、何もチェックしないことになります。いくつかの修正は、コードをいくつかの変数に格納することでしたが、コンパイルはループを削除し、最後の増分を格納するように見えます。

今、使用するコードを改善する最適化を行いたいのですが、それでも元々行っていたすべてのことを望んでいます。たとえば、コードを100,000回ループさせた場合、実際にコードを100,000回実行することを期待しています。Visual Studio 2010でコンパイラを変更して、リリースモードでコンパイルするときに最小限の最適化が行われるようにする方法がわかりません。正確に時間を計りたいのですが、正確に時間を計る方法がわかりません。

最初は、デバッグなしでデバッグを実行すると問題が解決する可能性があり、結果がJavaアプリケーションの結果と一致したため、非常にそう思われましたが、リリースモードで実行すると、結果がめちゃくちゃ速くなり、混乱します。最適化においてC++がはるかに優れているのか、それとも大量のコードが変更されているのかはわかりません。

おそらくコードを分解して、コンパイルがコードをコンパイルしたものを表示する方法はありますか?これは私が見たいもう一つのテストですが、私はこのことについてあまり知りません、そして正しい方向に何かが大いにありがたいです。私が何を求めているのかをある程度理解できる人に感謝します。当面の質問に対する誤解や不確実性に関するご質問には、喜んでお答えいたします。

4

5 に答える 5

4

したがって、コンパイラがすべてのコードを最適化するのを避けるには、コードで実行した結果を確実に「使用」する必要があります。

もう1つのトリックは、テスト対象のコードを別のファイルに入れることです。これにより、コンパイラは「関数がファイルの外にあります」をインライン化できません(「プログラム全体の最適化」を有効にしない限り)。

私はよく関数ポインタを使用します - 最適化を妨げるからというわけではありませんが (しばしばそうなりますが)、同じ基本的な「かかった時間を測定し、結果を出力する」といういくつかのテストを行うための良い基礎を与えてくれるからです。次のようなテーブルがあります。

 typedef void (*funcptr)(void);

 #define FUNC(f) { f, #f }

 struct func_entry
 {
      funcptr func;
      const char *name;
 };
 func_entry func_table[] = 
 {
      FUNC(baseline),
      FUNC(better1),
      FUNC(worse1),
 };

 void do_benchmark()
 {
     for(int i = 0; i < sizeof(func_table)/sizeof(func_table[0]); i++)
     {
          timestamp t = now();
          func_table[i].func();
          t = now() - t;

          printf("function %s took %8.5fs\n", func_table[i].name, 
                 timestamp_to_seconds(t));
     }
 }

now()明らかに、適切な時間取得関数、timestampその関数に関連する型、および機能するものに置き換える必要がありますtimestamp_to_seconds...

于 2013-02-04T01:07:04.457 に答える
0

私がしているのはbenchmark()、DLL 内にある関数を持ち、それに関数ポインタを渡すことです。コンパイラがベンチマーク ループを最適化するのを防ぐ最善の方法は、それを不可能にすることであり、それを別の DLL に入れることで、これを完全に防ぐことができます。LTCG が一般的になりつつあるため、個別の翻訳単位はもはや機能しません。

最初に: 簡単なセットアップ。スレッドのアフィニティをシングル コアに設定し、高い優先度を与えます。これにより、コンテキストの切り替えやキャッシュのスラッシングによる多くの変動を防ぐことができます。また、タイミングなどの高精度タイマーを使用することを忘れないでくださいQueryPerformanceCounter。これは、システム時間に依存しないものです。

次に、関数ポインタを 2 秒経過するまでループで呼び出します。これにより、キャッシュ内のコード/データがウォームアップされ、その速度の大まかなアイデアが得られ、実用的なループ カウントを自動的に導き出すことができます。1 秒で終了するループ カウントを選択します。

次に、実際のベンチマーク: ループが前のループの速度を改善しないたびにインクリメントされるカウンターを維持します。カウンターが設定された数に達したら、停止して、最適な時間を見つけたと想定します。速度が向上すると、カウンターはリセットされます。

これは、適切なプロファイラーが行うように何を最適化すべきかを示すものではありませんが、あるコードを別のコードと比較することが唯一の目的である場合は、非常に正確な結果が得られるはずです。

于 2013-02-04T01:39:30.630 に答える
0

それは、コードが何をしようとしているのか、どのように動作することを期待しているかによって異なります。

  1. あなたのコードは I/O (ネットワーク、ディスク、オーディオなど) を行っていますか?
  2. マルチスレッドですか?
  3. それは多くのメモリを扱っていますか?
  4. タイム クリティカルなループに陥っていませんか?
  5. プロセッサー集中型ですか?

一般に、リリース モードで実行し、プロファイラーを使用して、有効なユース/テスト ケースでコードがどのように動作するかを確認します。それでも、使用するプロファイラーを知る必要があります。これは、解決しようとしている問題によって異なります。

最も重要なことは、問題がないのに問題を探しに行かないことです。コンパイラを信頼してください。最近、コンパイラの裏をかこうとするのは、まったく無駄な (そして愚かな) 試みです。プロファイラーはデータのみを吐き出します。何を探して解釈するかはあなた次第です。

于 2013-02-04T01:39:55.783 に答える
0

ループでタイミングを計っている呼び出しの結果を使用する必要があるだけでなく、すべての反復の結果を使用する必要があります。ここでは、すべてのループの結果が使用されることを確認しようとしていますが、テストしようとしている以上のオーバーヘッドを課さないようにしています。

典型的なアプローチは、整数値を返すものに対して、すべてのメソッド呼び出しの合計を累積することです。これは、int を返す他のメソッドを呼び出すことで、int を返さないメソッドに拡張できます。たとえば、メソッドが を作成する場合、返された文字列をstd::string呼び出します。これは非常に高速です。size()C++ では、&ほとんどすべてのものを整数に変換する簡単な方法として、address-of 演算子を使用できます。

場合によっては、コンパイラがトリックを見抜き、ループから実際のメソッドを引き出すことができる可能性があります。これは、一連の値の追加や 1 つの大きな乗算にまで発展します。

実行時に生成されたある種の入力を反復処理することで、これを回避できます。コンパイラは、ループを常に折りたたむことができません。関数ポインターを使用することもできますが、追加のオーバーヘッドが追加され、一部のコンパイラー (および将来的にはさらに多くのコンパイラー) がそれらを調べる可能性があります。

これを行っている間ずっと、自分が本当に何を測定しているのかを自問する必要があります。ループで測定される非常に小さなメソッドは、ループがより複雑な場合に実際にどのように実行されるかを必ずしも適切に示すとは限りません。これは、最適化スペクトルの両側に適用されます。たとえば、ベンチマークで回避しようとしている「巻き上げ」が実際のコードで実際に発生している可能性があるため、ベンチマークは悲観的すぎます。逆に、ベンチマークで常に L1 にヒットする 8K ルックアップ テーブルのようなものは、実際のコードでは大量のミスを引き起こす可能性があります。

要約すると、マイクロベンチマークは慎重に使用するツールです -実際には非現実的であると思われる最適化を防ぐ方法を理解すれば、間違いなく何かを測定できます - しかし、サニティチェックとしていくつかの実世界のユースケースを常に評価する必要があります (そのことを理解してください)マイクロベンチマークの大きな違いは、テストされたメソッドが最初はランタイムの小さな部分である大規模なプログラムでは、常にはるかに小さな改善に変換されます)。

于 2013-02-04T01:22:51.990 に答える
-3

Visual Studio (Visual C++) を使用している場合は、プロファイルをデバッグに設定し、プロジェクトを右クリックして [プロパティ] をクリックし、[C/C++] -> [最適化] に移動します。無効になっていることを確認してください。

その後、ストップウォッチまたは UNIX プログラムを使用timeして、プログラムの実行時間をカウントできます。

もちろん、プロファイラーを使用するなど、パフォーマンスを分析するより洗練された方法があります。

于 2013-02-04T00:56:17.257 に答える