10

編集:私の元の例にはばかげた間違いがありました。ただし、修正した後も奇妙な結果が得られます。


私のCPU速度を「ブルートフォース」の方法で測定するという素朴な試みで、以下のプログラムを作成しました。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#pragma comment(linker, "/entry:mainCRTStartup")
#pragma comment(linker, "/Subsystem:Console")

int mainCRTStartup()
{
    char buf[20];
    clock_t start, elapsed;
    unsigned long count = 0;
    start = clock();
    __asm
    {
        mov EAX, 0;
    _loop:
        add EAX, 3; // accounts for itself and next 2 instructions
        cmp EAX, 0xFFFFFFFF - 0x400;
        jb _loop;
        mov count, EAX;
    }
    elapsed = clock() - start;
    _gcvt(count * (long long)CLOCKS_PER_SEC / (elapsed * 1000000000.0), 3, buf);
    puts(buf);
}

次のようなものに分解されます。

mainCRTStartup:
  push   ebp
  mov    ebp,esp
  sub    esp,28h
  mov    dword ptr [count],0
  call   dword ptr [_clock]
  mov    dword ptr [start],eax
  mov    eax,0

_loop:
  add    eax,03h
  cmp    eax,0FFFFFBFFh
  jb     _loop

  mov    dword ptr [count],eax
  call   dword ptr [_clock]
  sub    eax,dword ptr [start]

  ...    // call _gcvt, _puts, etc.

  mov    esp,ebp
  pop    ebp
  ret

ループは 3 命令なので、 の最終値はeax命令の総数になるはずです。

これを実行すると 4.2 になるのはなぜですか?

4

4 に答える 4

12

命令レベルの並列処理スーパースカラー アーキテクチャにより、パイプライン化された単一のクロック サイクルで複数の命令を実行できるためです。

たとえば、コードでは、分岐予測 は次のようにして、最後の反復以外のすべての命令を効果的に排除します。cmp_loop

  1. 並行して実行cmpし、jb
  2. 常にjbブランチを取ります。

もちろん、(2) は最後の反復でスローされるため、パイプラインがクリアされます。ループが 10^9 命令のオーダーであるため、余分な ~20 サイクル (20 ステージのパイプラインの場合) は無視できます。

コンパイラはこれを最適化すべきではありません

プロセッサ ハードウェアは常にデータパスで最適化の機会を探しています。コンパイラは、特定のアーキテクチャのパターンを利用するための命令を編成しようとします。たとえば、ハードウェア パイプラインは、ソフトウェア パイプラインなしで IPC を増加させることができます。特に、例のような比較的危険のないコードの場合です。

于 2011-08-10T03:47:52.980 に答える
9

CPU 速度は 1 秒あたりのバイト数ではなく、1 秒あたりの命令サイクルで測定されるため、特に x86 では、一部の命令は 1 サイクル以上かかります。

命令のタイミングについては、このページを参照してください。(実際には、これは 486 までしかありません。最新のプロセッサの適切なリファレンスを探しています)。

于 2011-08-10T03:42:09.633 に答える
2

命令の実行にかかるサイクル数は、バイト単位のサイズとは直接関係ありません。さらに、複数の実行ユニットや投機的実行などの最新の CPU 機能では、コードの特定のチャンクを正確に実行するのにかかる時間を事前に決定することは実際には不可能です。

于 2011-08-10T03:49:11.877 に答える
1

CPU の内部周波数のサイクルをカウントするrdtsc命令を使用して、ループを測定できます。2 つの読み取り値の差は、経過したサイクル数です。コードで 1000 ループを実行し、3 を掛けて (ループ内の命令)、経過したサイクルで割ります。これにより、サイクルごとに指示が表示されます。その後、CPU 自体の周波数に合わせてスケーリングできます。

コードが非常に短いため、キャッシュ レベル 1 (またはプリフェッチャー内?) から実行される可能性が高いことに注意してください。パイプライン処理で価値のあるものを作成するには短すぎる可能性があります。

指示のタイミングに関しては、このページは AShelly が提案したものよりも最新のようです。これは、スウェーデン王立工科大学のTorbjörn Granlundによって定期的に再計算されています。

于 2011-08-11T08:01:01.247 に答える