正しくベンチマークする方法の学習2:コンパイラーがどのように後押しするか

適切なベンチマーク値を取得することは戦いの半分ですが、後半は正しく解釈され、新しいことを学び、適用することができます。 通常のビルドと通常のビルドの100倍の違いに驚いたので、さらに掘り下げることにしました。 その結果、デバッグで何が起こっているかをよりよく学びました。 2005年と2008年の違いをスタジオが探しました(見つかりません)。 数分でビルドビルドを3倍高速化する方法を見つけました(背後のヒットに対してブロックを配置しました)。 「テイクアンドラン」メソッドを使用すると、著作権の結果とは3.5倍の結果が得られました(実際にx64が実行されています!)。 そして、笑いのために、私は悪い、価値のないアンダーベクトルと良いものを測定しました(悪いものは100倍速いことが判明しました)。 カットの下の詳細。



並べ替えへの参照から始めます。そうしないと、後で検索するのが難しくなります。 開始投稿彼らのテストコード私のテストコード



震えを解消して、元の質問に戻りました。100倍以上のブレーキは、どこから来るのか非常に遅いです。 他の人のテストは良いが、彼のより身近な、書いた、立ち上げた。 プロジェクトを作成するのは面倒でしたが、comstringからのコンパイルははるかに高速です。



cl2005 /O2 /EHsc 1.cpp

std it++ res=49995000, 28.5 msec

std ++it res=49995000, 28.6 msec

my res=49995000, 19.0 msec



cl2005 /EHsc 1.cpp

std it++ res=49995000, 534.2 msec

std ++it res=49995000, 437.6 msec

my res=49995000, 69.9 msec









おっと ただし、結果は異なります。それらの人は30倍遅く、私は10人しかいません。それらの人については、ポストインクリメントが3倍遅くなり、約20%になります。 私は何を間違えていますか? コンパイラと禁止されたライブラリは本当に激しいですか? さて、私たちは今それを入れて、チェックします。



cl2008 /O2 /EHsc 1.cpp

std it++ res=49995000, 64.3 msec

std ++it res=49995000, 63.4 msec

my res=49995000, 19.0 msec



cl2008 /EHsc 1.cpp

std it++ res=49995000, 732.1 msec

std ++it res=49995000, 678.8 msec

my res=49995000, 70.0 msec









イムディア 実際には違いがあり、反対方向にのみあります。VS2005リリースのビルドは3倍高速でした。 改善点は明らかです。 スタジオ番号2008のSCLは、明らかに3倍のセキュリティになっています。 _SECURE_SCL 0では、速度は同じです。 (先を見ると、他のテストのほとんどはほとんど同じです。)他に何が間違っていますか? コンパイラは同じになり、std :: vectorは同じように見え、出力は明確になりました:正しくコンパイルしていません。 つまり、コンパイラキーが分岐します。 私たちは解決策を検討しますが、かなり広まっています。



/Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc90.pdb" /W3 /nologo /c /ZI /TP /errorReport:prompt









革新的な本能とブルートフォースは、重要でないキーをすぐに破棄しますが、重要なキーは残します。 3つの重要なキーがあります:a)/ MDd、b)/ RTC1、c)/ ZI。 結果は次のようになります。



cl2008 /Od /EHsc /MDd 1.cpp

std it++ res=49995000, 6026.1 msec

std ++it res=49995000, 1953.9 msec

my res=49995000, 66.5 msec



cl2008 /Od /EHsc /MDd /RTC1 1.cpp

std it++ res=49995000, 7572.0 msec

std ++it res=49995000, 2385.3 msec

my res=49995000, 101.8 msec



cl2008 /Od /EHsc /MDd /RTC1 /ZI 1.cpp

std it++ res=49995000, 18722.0 msec

std ++it res=49995000, 7131.8 msec

my res=49995000, 511.9 msec









さて、今ではすべてが人々のようになっています。ベクターのトラバースはろくでなしのように遅くなり、ポストインクリメントは遅くなり始めました。 ニシュチャク! 何が問題なのか理解できます。 cl /?を読んで数分で 3つの単純なわかりやすいもの(キーごとに1つ)が見つかりましたが、それらはまだ新しく(忘れられていた古いものを参照)、驚くべきものです。



/ MDdはデバッグライブラリを認証し(これは重要ではありません)、自動的に有効にします/ D _DEBUG(これは重要です)。 このことから、SCLには、_SECURE_SCLに含まれているものに加えて、追加のチェックパックが含まれていました。これにより、ポストインクリメントが8倍、プリインクリメントが3倍遅くなりました。



/ RTC1には、スタック障害チェックと初期化されていない変数が含まれます。 dysasmデバッガーを調べると、内部ループにさまざまな関数の呼び出しのかかとが現れていることがわかります(++ 、! =、*、および2つのデストラクタ)。 各呼び出しをチェックする必要があります:彼がそれを拾ったらどうなるか、そしてそれがどのようにスタックを壊すか! これを行うには、STLでオーバーロードされた1つまたは別の演算子の(各)呼び出しごとに、256バイト弱のマーカーグッドがスタックにプッシュされます。 さようなら、さらに20%は、最高のパフォーマンスではありません。



/ ZIは、しかし、誰よりも勝って銀行を家に持ち帰ります。 これが編集と続行です。 その場でプログラムを修正して再起動するのがおそらく便利です(私は知らないので、使用しません)。 利便性のために支払う必要があり、価格はさらにブレーキの3倍です。



デバッグでの合計ポストインクリメントは、リリースと比較してエピックを291倍(!!!)遅くします。 これはすぐに疑問を提起します:なぜ私は291回もあり、それらの人は95しか持っていないのですか?! 私は元のテストを収集していますが、終了を待つだけでは十分な忍耐力がなく、カウントを10倍減らし、頭の時間を10倍します。



it++, x86, release: 1.00

++it, x86, release: 1.00

it++, x86, debug: 275.7

++it, x86, debug: 101.9



it++, x64, release: 0.87779

++it, x64, release: 0.87753

it++, x64, debug: 83.2849

++it, x64, debug: 27.1557









数字から判断すると、私のx86から​​のリリースでは13%の所得税がかかり、デバッグでは下着、靴下、スリッパのみが奪われました。 どうやら残念なことですが、何もありません。 とにかく、私はXPをあきらめません。私にとっては7つは難しく、空力があまりにも透明で、さらにサービスパックの2番目を待っています。



結果によると、デバッグビルドの地獄のようなブレーキの原因は誰にあるのかは明らかです。 _SECURE_SCLを2回食べ、最適化を無効にする5回、3回(++)から8回(it ++)が_DEBUGの下でチェックをガズリング、1.2回/ RTC、最後に/ ZIを最大3回、さらに2回* 5 * 8 * 1.2 * 3 = 288、一粒の鶏肉、コード全体が...チェックにあり、ブレーキは300(300)回です。 もちろん手は鶏肉を食べます。 永遠の3つのうち、残る質問は1つだけです。



一般に、多くのことを行うことができますが、長い間です。 ただし、2分で2つの便利なことができます。 最初に、編集と続行(プロジェクトでまったく動作する場合)を使用しないという強い意思決定を行い、通常のソビエトPDBを生成すると、5回ではなくても3倍速く動作します。 Â2、/ RTCチェックは、もちろん、無効になります。 しかし! MSDNを読むと、興味深いプラグマruntime_checksが明らかになります。 それら。 これらのチェックは、プロジェクトで特に頻繁に使用される機能に対して個別に無効にしたり、STL全体に対して無効にしたりできます。 外部インクルードセクションを2本の線で囲みます。



#pragma runtime_checks("",off)

...

#pragma runtime_checks("",restore)









++を使用すると18秒ではなく6秒、++を使用すると7秒ではなく2秒になります。 ++を使用する必要性に関する結論は説得力をもって確認されています。 販売ビルドは約3倍加速されます。 利益! 既知のことを元のテストに適用しようとしますが、同様に判明します。 少なくとも古き良きx86では。



vanilla

it++, x86, debug: 275.7

++it, x86, debug: 101.9



#pragma runtime_checks, /Zi instead of /ZI

it++, x86, debug: 102.2

++it, x86, debug: 30.0









まったく何もできない、悪い、曲がった、価値のない、原始的なアンダーベクターは、デバッグでの正直な0.066秒を示しています、それら。 it ++バリアントの90倍、または++ itバリアントの30倍高速です。 投稿の冒頭で約100回、丸くつきました。 彼にはチェックが1つしかありません。アクセス時のインデックスの正確さ(ただし、ベクターから必要なチェックの約99%をカバーしています)。 イテレータとともに、プリインクリメントとポストインクリメントによるフォーカスはありません。 それは何の意味もありません。 著者は何も示唆していません。 ベンチマークは明らかに合成です。 ベクターは文字通り3分で記述され、対応する機能はそれらです。 STLと比較して存在しません。 デバッグのビルド速度は必ずしも重要ではありません。 不要な検証が重要になる場合があります。 開発速度は誰にとっても常に重要です。 真実は常にプロファイラーに伝えます。



_SECURE_SCL、_DEBUG、コンパイラキー、#pragma runtime_checksについて覚えておいてください。 効果、ニー、素晴らしいです。 (個人的には違いは300回私を殺しました。そして、/ Ziの代わりに/ ZIの後で3から5回の違いは死体を食べました。「私はそれについて知っていましたが、私は知りませんでした。」)



あなたにぴったりのベンチマーク。 迅速なデバッグ。



All Articles