ホーム>source

ウィズウィズ

私は、WBメモリーへの定期的および非一時的な書き込みを伴うメモリーコピールーチンの例について、手動で(ベンチマークフレームワークを使用せずに)単一のショットでベンチマークを書き込む方法を学習しようとしています。何らかのレビューをお願いしたいと思います。


宣言:

<前>ウィズウィズ

定義:

<前>ウィズウィズ

ベンチマークコード:

<前>ウィズウィズ

結果

ウィズウィズ中央値:44479368コアサイクル

ウィズウィズ中央値:24053086コアサイクル

UPD: Whiskey Lake i7-8565U でベンチマークを実行すると結果が得られました

したがって、メモリコピールーチンの実装間でコアサイクルにほぼ2倍の違いがありました。 IOM/3.6.12で指定されているバス帯域幅で競合するRFO要求があるWBメモリへの通常のストアの場合と同様に解釈します(鉱山を強調):

ウィズウィズ

質問1: シングルショットの場合のベンチマーク分析を行う方法は? perf起動オーバーヘッドとウォームアップ反復オーバーヘッドのため、perfカウンターは役に立たないようです。

質問2: そのようなベンチマークは正しいですか。私は void *avx_memcpy_forward_llss(void *restrict, const void *restrict, size_t); void *avx_nt_memcpy_forward_llss(void *restrict, const void *restrict, size_t); を説明しました最初は、前の飛行中の指示によるストールを回避するために、クリーンなCPUリソースで測定を開始します。コンパイルバリアと avx_memcpy_forward_llss: shr rdx, 0x3 xor rcx, rcx avx_memcpy_forward_loop_llss: vmovdqa ymm0, [rsi + 8*rcx] vmovdqa ymm1, [rsi + 8*rcx + 0x20] vmovdqa [rdi + rcx*8], ymm0 vmovdqa [rdi + rcx*8 + 0x20], ymm1 add rcx, 0x08 cmp rdx, rcx ja avx_memcpy_forward_loop_llss ret avx_nt_memcpy_forward_llss: shr rdx, 0x3 xor rcx, rcx avx_nt_memcpy_forward_loop_llss: vmovdqa ymm0, [rsi + 8*rcx] vmovdqa ymm1, [rsi + 8*rcx + 0x20] vmovntdq [rdi + rcx*8], ymm0 vmovntdq [rdi + rcx*8 + 0x20], ymm1 add rcx, 0x08 cmp rdx, rcx ja avx_nt_memcpy_forward_loop_llss ret としてメモリクロバーを追加しました #include <stdio.h> #include <inttypes.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <immintrin.h> #include <x86intrin.h> #include "memcopy.h" #define BUF_SIZE 128 * 1024 * 1024 _Alignas(64) char src[BUF_SIZE]; _Alignas(64) char dest[BUF_SIZE]; static inline void warmup(unsigned wa_iterations, void *(*copy_fn)(void *, const void *, size_t)); static inline void cache_flush(char *buf, size_t size); static inline void generate_data(char *buf, size_t size); uint64_t run_benchmark(unsigned wa_iteration, void *(*copy_fn)(void *, const void *, size_t)){ generate_data(src, sizeof src); warmup(4, copy_fn); cache_flush(src, sizeof src); cache_flush(dest, sizeof dest); __asm__ __volatile__("mov $0, %%rax\n cpuid":::"rax", "rbx", "rcx", "rdx", "memory"); uint64_t cycles_start = __rdpmc((1 << 30) + 1); copy_fn(dest, src, sizeof src); __asm__ __volatile__("lfence" ::: "memory"); uint64_t cycles_end = __rdpmc((1 << 30) + 1); return cycles_end - cycles_start; } int main(void){ uint64_t single_shot_result = run_benchmark(1024, avx_memcpy_forward_llss); printf("Core clock cycles = %" PRIu64 "\n", single_shot_result); } static inline void warmup(unsigned wa_iterations, void *(*copy_fn)(void *, const void *, size_t)){ while(wa_iterations --> 0){ copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); copy_fn(dest, src, sizeof src); } } static inline void generate_data(char *buf, size_t sz){ int fd = open("/dev/urandom", O_RDONLY); read(fd, buf, sz); } static inline void cache_flush(char *buf, size_t sz){ for(size_t i = 0; i < sz; i+=_SC_LEVEL1_DCACHE_LINESIZE){ _mm_clflush(buf + i); } } を避けるためにOoOを実行します。

avx_memcpy_forward_llss
あなたの答え
  • 解決した方法 # 1

    可能な限り、ベンチマークは可能な限り「健全性チェック」を可能にする方法で結果を報告する必要があります。この場合、そのようなチェックを有効にするいくつかの方法は次のとおりです。

    メインメモリの帯域幅が関係するテストの場合、システムの既知のピークDRAM帯域幅と直接比較できる単位で結果を提示する必要があります。 Core i7-8565Uの一般的な構成の場合、これは2チャネル* 8バイト/転送* 24億転送/秒= 38.4 GB /秒(下記の(6)の項目も参照)。

    メモリ階層内の任意の場所へのデータ転送を伴うテストの場合、結果には、「メモリフットプリント」のサイズ(アクセスされた個別のキャッシュラインアドレスの数とキャッシュラインサイズの積)の明確な説明と、転送。コードはここで読みやすく、メインメモリテストのサイズは完全に妥当です。

    時限テストでは、タイミングのもっともらしいオーバーヘッドと比較できるように、絶対時間を含める必要があります。 CORE_CYCLES_UNHALTEDカウンターのみを使用すると、経過時間を直接計算することができなくなります(ただし、テストが明らかに長いので、タイミングのオーバーヘッドは無視できます)。

    その他の重要な「ベストプラクティス」の原則:

    RDPMC命令を使用するテストは、単一の論理プロセッサにバインドする必要があります。結果は、そのようなバインディングが使用されたことを読者に確認する方法で提示する必要があります。 Linuxでこのようなバインディングを実施する一般的な方法には、「taskset」または「numactl --physcpubind = [n]」コマンドの使用、または単一の許可された論理プロセッサを使用した「sched_setaffinity()」のインライン呼び出しの組み込み、または環境変数の設定が含まれます。これにより、ランタイムライブラリ(OpenMPなど)がスレッドを単一の論理プロセッサにバインドします。

    ハードウェアパフォーマンスカウンターを使用する場合、カウンターのすべての構成データが利用可能であり、正しく記述されていることを確認するために、特別な注意が必要です。上記のコードは、RDPMCを使用して、CPU_CLK_UNHALTEDというイベント名を持つIA32_PERF_FIXED_CTR1を読み取ります。イベント名の修飾子は、IA32_FIXED_CTR_CTRL(MSR 0x38d)ビット7:4のプログラミングに依存します。すべての可能な制御ビットからイベント名修飾子にマッピングする一般的に受け入れられている方法はありません。そのため、結果とともにIA32_FIXED_CTR_CTRLの完全なコンテンツを提供するのが最善です。

    CPU_CLK_UNHALTEDパフォーマンスカウンターイベントは、L1およびL2キャッシュのみを含む命令の実行やデータ転送など、動作がプロセッサコアの周波数に直接比例するプロセッサの部分のベンチマークに使用する適切なイベントです。メモリー帯域幅には、パフォーマンスに影響するプロセッサーの部分が含まれますない プロセッサー周波数に直接対応します。特に、固定周波数動作を強制せずにCPU_CLK_UNHALTEDを使用すると、経過時間を計算できなくなります(上記の(1)および(3)で必要)。あなたの場合、RDTSCPはRDPMCよりも簡単でした-RDTSCではプロセスを単一の論理プロセッサにバインドする必要がなく、他の構成MSRの影響を受けず、経過時間を秒単位で直接計算できます。

    詳細:メモリ階層でのデータ転送を伴うテストの場合、キャッシュの内容とキャッシュの内容の状態(クリーンまたはダーティ)を制御し、「前」と「後」の状態を明示的に説明すると便利です結果と。配列のサイズを考えると、コードは、ソース配列と宛先配列の一部の組み合わせでキャッシュのすべてのレベルを完全に満たし、それらのアドレスをすべてフラッシュして、(ほぼ)完全に無効なキャッシュ階層を残す必要があります(クリーン)エントリ。

    上級:CPUIDをシリアル化命令として使用することは、ベンチマークでほとんど役に立ちません。順序付けは保証されますが、実行に長い時間がかかります。AgnerFogの「Instruction Tables」は、100〜250サイクルでレポートします(おそらく入力引数によって異なります)。

    上級:ベンチマークでのLFENCEの使用は、非常に細かい粒度(数百サイクル未満)で測定する場合にのみ関連します。 http://sites.utexas.edu/jdm4372/2018/07/23/comments-on-timing-short-code-sections-on-intel-processors/にあるこのトピックに関する詳細情報

    テスト中にプロセッサが最大ターボ周波数4.6 GHzで動作していたと仮定すると、報告されたサイクル数はそれぞれ9.67ミリ秒と5.23ミリ秒に対応します。これらを「健全性チェック」に接続すると、次のようになります。

    最初のケースが1回の読み取り、1回の割り当て、1回のライトバック(それぞれ128MiB)を実行するとすると、対応するDRAMトラフィックレートは27.8GB /秒+ 13.9 GB /秒= 41.6 GB /秒==ピークの108%になります。

    2番目のケースが1つの読み取りと1つのストリーミングストア(各128MiB)を実行すると仮定すると、対応するDRAMトラフィックレートは25.7 GB /秒+ 25.7 GB /秒= 51.3 GB /秒=ピークの134%です。

    これらの「健全性チェック」の失敗は、周波数を4.6 GHzまで高くすることはできなかった(おそらく3.0 GHz以下であった)ことを示していますが、ほとんどの場合、経過時間を明確に測定する必要があることを示しています。

    ストリーミングストアの非効率性に関する最適化マニュアルからの引用は、完全なキャッシュライン転送に統合できない場合にのみ適用されます。コードは、「ベストプラクティス」の推奨に従って出力キャッシュラインのすべての要素に格納します(同じラインに書き込むすべてのストア命令は連続して実行され、ループごとに1つのストアストリームのみを生成します)。ハードウェアがストリーミングストアを解体するのを完全に防ぐことは不可能ですが、あなたの場合、それは非常にまれであるはずです-おそらく数百万のうちのいくつか。部分的なストリーミングストアの検出は非常に高度なトピックであり、「アンコア」で十分に文書化されていないパフォーマンスカウンターの使用や、DRAM CASカウントの増加(他の原因が原因である可能性があります)を探すことによる部分的なストリーミングストアの間接的な検出を必要とします。ストリーミングストアに関するその他のメモは、http://sites.utexas.edu/jdm4372/2018/01/01/notes-on-non-temporal-aka-streaming-stores/にあります。

  • 前へ java - JPAクエリ:サブクエリをグループ化条件に結合する
  • 次へ node.js - mongoose:ミドルウェアpre deleteOneオプションが機能しない