1

私は3つのメモリブロックを持っています。

char block_a[1600]; // Initialized with random chars
unsigned short block_b[1600]; // Initialized with random shorts 0 - 1599 with no duplication
char block_c[1600]; // Initialized with 0

これに対して次のコピー操作を実行しています

for ( int i = 0; i < 1600; i++ ) {
    memcpy(block_c[i], block_a[block_b[i]], sizeof(block_a[0]); // Point # 1
}

今、ポイント 1 で行っている上記の操作の NS で CPU サイクル + 時間を測定しようとしています。

測定環境

1) プラットフォーム: インテル x86-64。コア i7
2) Linux カーネル 3.8

測定アルゴリズム

0) 完全な制御と正確なデータを取得できるように、実装はカーネル モジュールとして行われます。
1) シリアル化に使用する CPUID + MOV 命令のオーバーヘッドを測定しました。
2) プリエンプション + 割り込みを無効にして、CPU の排他的アクセスを取得する
3) CPUID を呼び出して、パイプラインがこの時点までの順不同の命令をクリアしていることを確認する
4) RDTSC を呼び出して、TSC の初期値を取得し、この値を保存する
5) 実行される上で述べた測定したい操作
6) RDTSCP を呼び出して TSC の終了値を取得し、この値を保存した
7) 再度 CPUID を呼び出して、2 つの RDTSC 呼び出し内に順不同で何も入っていないことを確認した
8) 開始 TSC 値から終了 TSC 値を引いて、この操作を実行するのにかかる CPU サイクルを取得します。
9) 2 つの MOVE 命令によってかかるオーバーヘッド サイクルを減算して、最終 CPU サイクルを取得します。

コード
    ....
    ....
    preempt_disable(); /* Disable preemption to avoid scheduling */
    raw_local_irq_save(flags); /* Disable the hard interrupts */
    /* CPU is ours now */
    __asm__ volatile (
        "CPUID\n\t"
        "RDTSC\n\t"
        "MOV %%EDX, %0\n\t"
        "MOV %%EAX, %1\n\t": "=r" (cycles_high_start), "=r" (cycles_low_start)::
        "%rax", "%rbx", "%rcx", "%rdx"
    );

    /*
     Measuring Point Start
    */
    memcpy(&shuffled_byte_array[idx], &random_byte_array[random_byte_seed[idx]], sizeof(random_byte_array[0]));
    /* 
    * Measuring Point End
    */
    __asm__ volatile (
        "RDTSCP\n\t"
        "MOV %%EDX, %0\n\t"
        "MOV %%EAX, %1\n\t"
        "CPUID\n\t": "=r" (cycles_high_end), "=r" (cycles_low_end)::
        "%rax", "%rbx", "%rcx", "%rdx"
    );

    /* Release CPU */
    raw_local_irq_restore(flags);
    preempt_enable();

    start = ( ((uint64_t)cycles_high_start << 32) | cycles_low_start);
    end   = ( ((uint64_t)cycles_high_end << 32) | cycles_low_end);
    if ( (end-start) >= overhead_cycles ) {
        total = ( (end-start) - overhead_cycles);
    } else {
        // We will consdider last total
    }
質問

私が取得している CPU サイクルの測定値は現実的ではないようです。いくつかのサンプルの結果が与えられています

Cycles Time(NS)
0006 0005
0006 0005
0006 0005
0006 0005
0006 0005
0011 0009
0006 0005
0006 0005
0006 0005
0006 0005
0006 0005
0011 0009
0011 0009
0000 0000
0011 0009
0006 0005
0006 0005
0006 0005
0011 0009
0006 0005
0000 0000
0011 0009
0011 0009
0006 0005
0006 0005
0006 0005
0006 0005
0006 0005
0011 0009
0006 0005
0011 0009
0011 0009
0011 0009
0011 0009
0006 0005
0006 0005
0006 0005
0006 0005
0011 0009
0011 0009
0011 0009

モジュールを再度ロードすると、結果が得られます。

Cycles Time(NS)
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0006 0005
0006 0005
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0011 0009
0011 0009
0011 0009
0011 0009
0011 0009
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0017 0014
0011 0009
0011 0009
0000 0000
0000 0000
0000 0000
0011 0009
0000 0000
0000 0000
0011 0009
0011 0009
0011 0009
0000 0000
0022 0018
0006 0005
0011 0009
0006 0005
0006 0005
0104 0086
0104 0086
0011 0009
0011 0009
0011 0009
0006 0005
0006 0005
0017 0014
0017 0014
0022 0018
0022 0018
0022 0018
0017 0014
0011 0009
0022 0018
0011 0009
0006 0005
0011 0009
0006 0005
0006 0005
0006 0005
0011 0009
0011 0009
0011 0009
0011 0009
0011 0009
0006 0005
0006 0005
0011 0009
0006 0005
0022 0018
0011 0009
0028 0023
0006 0005
0006 0005
0022 0018
0006 0005
0022 0018
0006 0005
0011 0009
0006 0005
0011 0009
0006 0005
0000 0000
0006 0005
0017 0014
0011 0009
0022 0018
0000 0000
0011 0009
0006 0005
0011 0009
0022 0018
0006 0005
0022 0018
0011 0009
0022 0018
0022 0018
0011 0009
0006 0005
0011 0009
0011 0009
0006 0005
0011 0009
0126 0105
0006 0005
0022 0018
0000 0000
0022 0018
0006 0005
0017 0014
0011 0009
0022 0018
0011 0009
0006 0005
0006 0005
0011 0009

上記のリストでは、CPU サイクルが 0 であるコピー操作が多数あることがわかります。多くの場合、3 サイクル未満です。

memcpy 操作で CPU サイクルが 0 または非常に少ない理由は何だと思いますか? 一般的に memcpy が使用する CPU サイクルの量。

アップデート

以下の変更を試してみた結果が得られ まし た
。コードはすでにシングル コアでのみ実行されていますが、念のため)、結果に 影響はありませんCPU が最大周波数で動作するようにします。


4

1 に答える 1

0

不正確な CPU サイクルの原因はキャッシュにあるようです (実際には不正確な CPU サイクルではありませんが、この場合、正確な結果を得るにはキャッシュ パフォーマンスの測定も考慮する必要があります)。指定されたデータのキャッシュがクリアされていることを確認した後、結果は問題ないように見えます。キャッシュをクリアするために次の関数を追加しました。clflush 関数はカーネル API で使用でき、x86 CLFLUSH 命令を利用します。

static void flush_cache(char random_byte_array[], char shuffled_byte_array[])
{
    unsigned int idx = 0;
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(random_byte_array+(idx*64));
    }
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(shuffled_byte_array+(idx*64));
    }
}
結果

1600 バイトのフル メモリ ブロックでの memcpy
CPU サイクル = 216 - 260 (複数のテストの場合)

1600 バイト ブロックの個々のバイトの memcpy

Cycles Time (ns)
0159 0132
0000 0000
0000 0000
....
....
0049 0040
0049 0040
0049 0040
0000 0000
0000 0000
....
....

最初の要素 (0 番目のインデックス) の memcpy の場合、約 140 ~ 160 サイクルかかります。いくつかの要素を進めるには、0 ~ 10 サイクルかかります (これは、データがキャッシュにロードされているためです)。 140~160要素(おそらくキャッシュミス発生)

データがキャッシュにない限り、良好な CPU サイクルを取得していますが、データがキャッシュにある場合は常に、サイクルは測定に十分ではありません。おそらく、キャッシュ パフォーマンスの測定も考慮する必要があります。

于 2016-02-17T18:42:51.583 に答える