26 回の反復の後、Linux は CPU を最大クロック速度まで上げます。これは、プロセスが連続して数回フル タイム スライスを使用するためです。
ウォールクロック時間の代わりにパフォーマンス カウンターをチェックすると、遅延ループごとのコア クロック サイクルが一定のままであることがわかり、DVFS (最新のすべての CPU がより多くのエネルギーで実行するために使用する) の単なる効果であることを確認できます。ほとんどの場合、効率的な周波数と電圧)。
新しい電源管理モード (ハードウェアがクロック速度を完全に制御するモード) をサポートするカーネルを備えた Skylake でテストした場合、ランプアップははるかに高速になります。
Turbo を搭載した Intel CPU でしばらく実行したままにしておくと、熱制限によりクロック速度を最大持続周波数まで下げる必要があると、反復あたりの時間が再びわずかに増加することがわかります。 (Turbo が高電力ワークロードを維持できるよりも高速に CPU を実行できるようにする方法の詳細については、CPU が HPC でピーク パフォーマンスを維持できない理由を参照してください。)
09
の紹介 最小周波数でもプロセスが 100% の負荷を生成していないため、Linux の CPU 周波数ガバナーがクロック速度を上げないようにします。 (つまり、カーネルのヒューリスティックにより、実行中のワークロードに対して CPU が十分に高速に実行されていると判断されます。)
他の理論に関するコメント :
re:潜在的なコンテキストが 18
から切り替わるという David の理論 キャッシュを汚染する可能性があります:それは一般的に悪い考えではありませんが、このコードの説明には役立ちません.
キャッシュ / TLB 汚染は、この実験ではまったく重要ではありません .基本的に、スタックの最後以外のメモリに触れるタイミング ウィンドウ内には何もありません。ほとんどの時間は、1 つの 26
にしか触れない小さなループ (1 行の命令キャッシュ) で費やされます。 スタックメモリの。 30
中の潜在的なキャッシュ汚染 このコードの時間はごくわずかです (実際のコードは異なります)!
x86 の詳細:
48
の呼び出し それ自体がキャッシュ ミスする可能性がありますが、コード フェッチ キャッシュ ミスは、測定対象の一部ではなく、開始時間の測定を遅らせます。 56
への 2 回目の呼び出し キャッシュ内でまだホットであるため、遅延はほとんどありません。
69
関数は 71
とは異なるキャッシュ ラインにある可能性があります (gcc は 88
をマークするため 「コールド」として、最適化が少なくなり、他のコールド関数/データと一緒に配置されます)。 1 つまたは 2 つの命令キャッシュ ミスが予想されます。ただし、それらはおそらくまだ同じ 4k ページにあるため、98
プログラムの時間領域に入る前に潜在的な TLB ミスを引き起こします。
gcc -O0 は、OP のコードを次のようなものにコンパイルします (Godbolt コンパイラー エクスプローラー):ループ カウンターをスタック上のメモリに保持します。
空のループはループ カウンターをスタック メモリに保持するため、典型的な Intel x86 CPU では、ループは 105の一部であるストア転送レイテンシーのおかげで、OP の IvyBridge CPU で ~6 サイクルごとに 1 回の反復で実行されます。コード> メモリ宛先 (読み取り-変更-書き込み)。
112
600,000 サイクルであり、最大で 2 つのキャッシュ ミスの寄与を支配します (コード フェッチ ミスごとに最大 200 サイクルで、解決されるまでそれ以上の命令の発行が妨げられます)。
アウト オブ オーダー実行とストア フォワーディングは、スタックにアクセスする際のキャッシュ ミスの可能性をほとんど隠す必要があります (128
の一部として)。 命令)
ループ カウンターがレジスターに保持されていたとしても、100k サイクルは大量です。
132
への呼び出し コンテキストスイッチが発生する場合と発生しない場合があります。そうであれば、そうでない場合よりも時間がかかります。