3

私は高精度タイマーで遊んでいましたが、最初のテストの1つは、rdtscを使用してprintfを測定することでした。以下は私のテストプログラムとその出力です。私が気付いたのは、printfを初めて実行すると、最初の印刷では、後続の印刷よりも一貫して約25倍の時間がかかるということです。何故ですか?

#include <stdio.h>
#include <stdint.h>

// Sample code grabbed from wikipedia
__inline__ uint64_t rdtsc(void)
{
    uint32_t lo, hi;
    __asm__ __volatile__ (
            "xorl %%eax,%%eax \n        cpuid"
            ::: "%rax", "%rbx", "%rcx", "%rdx");
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    return (uint64_t)hi << 32 | lo;
}

int main(int argc, const char *argv[])
{
    unsigned int i;
    uint64_t counter[10];
    uint64_t sum = 0;
    for (i = 0; i < 10; i++)
    {
        counter[i] = rdtsc();
        printf("Hello, world\n");
        counter[i] = rdtsc() - counter[i];
    }

    for (i = 0; i < 10; i++)
    {
        printf("counter[%d] = %lld\n", i, counter[i]);
        sum += counter[i];
    }
    printf("avg = %lld\n", sum/10);
    return 0;
}

そして出力:

Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
Hello, world
counter[0] = 108165
counter[1] = 6375
counter[2] = 4388
counter[3] = 4388
counter[4] = 4380
counter[5] = 4545
counter[6] = 4215
counter[7] = 4290
counter[8] = 4237
counter[9] = 4320
avg = 14930

(参考までに、これはOSXでgccを使用してコンパイルされました)

4

5 に答える 5

5

私の推測では、printfの最初の呼び出しでは、stdoutリソースはキャッシュになく、呼び出しはそれをキャッシュに入れる必要があるため、速度が遅くなります。以降のすべての呼び出しでは、キャッシュはすでにウォームです。

2番目に考えられる説明は、これがLinux上にある場合(OSXにも当てはまる可能性がありますが、よくわかりません)、プログラムでストリームの方向を設定する必要があるということです。(ASCIIとUNICODE)これは、そのストリームを使用する関数への最初の呼び出しで実行され、ストリームが閉じるまで静的です。この向きを設定するオーバーヘッドが何であるかはわかりませんが、それは1回限りのコストです。

私が完全に間違っていると誰かが思ったら、遠慮なく私を訂正してください。

于 2011-09-02T14:35:56.380 に答える
5

おそらく最初は、 のコードprintfが命令キャッシュにないため、メイン メモリからロードする必要があります。後続の実行では、既にキャッシュにあります。

于 2011-09-02T14:36:53.757 に答える
4

それは約 50 マイクロ秒です。おそらくキャッシングの問題でしょうか?ハードドライブからのロードとは関係がありませんが、RAM から CI/O ライブラリの大きなチャンクをロードするには十分です。

于 2011-09-02T14:37:51.993 に答える
4

ある種の遅延初期化である可能性があります。

于 2011-09-02T14:38:44.117 に答える
1

ハードウェアとソフトウェアの両方の設計には、100万回実行されたものの実行速度が、1回実行されたものの実行速度よりもはるかに重要であることを示唆する最優先の原則があります。これの当然の結果として、何かが100万回行われた場合、最初に何かを行うために必要な時間は、他の999,999に必要な時間よりもはるかに重要ではありません。今日のコンピューターが25年前よりもはるかに高速である最大の理由のひとつは、1回限りの操作のパフォーマンスが低下する可能性がある場合でも、設計者が繰り返し操作を高速化することに重点を置いていることです。

ハードウェアの観点からの簡単な例として、メモリ設計への2つのアプローチを考えてみましょう。(1)単一のメモリストアがあり、すべての操作が完了するまでに60ナノ秒かかります。(2)キャッシュにはいくつかのレベルがあります。キャッシュの第1レベルに保持されているワードをフェッチするには、1ナノ秒かかります。そこにはないが、第2レベルで保持されている単語は5つかかります。そこにないが第3レベルにある単語は10を取り、そこにない単語は60を取ります。すべてのメモリアクセスが完全にランダムである場合、最初の設計は2番目の設計よりも単純であるだけでなく、パフォーマンスも向上します。ほとんどのメモリアクセスにより、CPUは、キャッシュ内のデータを検索してメインメモリからフェッチする前に10ナノ秒を浪費します。一方で、メモリアクセスの80%が最初のキャッシュレベルで満たされ、16%が2番目で、3%が3番目で満たされる場合、100分の1だけがメインメモリに送信される必要がある場合、これらのメモリアクセスの平均時間は次のようになります。 2.5nsになります。これは、より単純なメモリシステムの平均で40倍の速度です。

プログラム全体がディスクからプリロードされている場合でも、「printf」のようなルーチンを初めて実行するときは、プログラムも必要なデータも、どのレベルのキャッシュにも存在しない可能性があります。したがって、最初に実行するときは、低速のメモリアクセスが必要になります。一方、コードとその必要なデータの多くがキャッシュされると、将来の実行ははるかに高速になります。コードが最速のキャッシュにある間にコードの繰り返し実行が発生した場合、速度の差は簡単に桁違いになります。高速の場合に最適化すると、多くの場合、コードの1回の実行が他の場合よりもはるかに遅くなりますが(上記の例で提案されているよりもさらに大幅に)、多くのプロセッサは多くの時間を小さなピースの実行に費やします。コードの数百万または数十億の時間、

于 2011-09-02T14:58:48.537 に答える