非常に大きな (L2 キャッシュよりも大きい) メモリ ブロックがあり、すべてゼロに設定しなければならない場合があります。memset はシリアル コードでは有効ですが、パラレル コードではどうでしょうか。同時スレッドから memset を呼び出すと実際に大きな配列の速度が上がる場合、誰か経験がありますか? または、単純な openmp parallel for loops を使用していますか?
2 に答える
HPC の関係者は通常、1 つのスレッドでは 1 つのメモリ リンクを飽和させるのに十分ではないと言いますが、これは通常、ネットワーク リンクにも当てはまります。これは、2 GiB のメモリの 2 倍のゼロで埋める、私が書いた迅速で汚れた OpenMP 対応 memsetter です。そして、異なるアーキテクチャで異なる数のスレッドを使用して GCC 4.7 を使用した結果を次に示します (報告された複数の実行からの最大値)。
GCC 4.7、以下でコンパイルされたコード-O3 -mtune=native -fopenmp
:
クアッドソケット Intel Xeon X7350 - Nehalem より前のクアッドコア CPU、独立したメモリコントローラーとフロントサイドバスを搭載
シングルソケット
threads 1st touch rewrite
1 1452.223 MB/s 3279.745 MB/s
2 1541.130 MB/s 3227.216 MB/s
3 1502.889 MB/s 3215.992 MB/s
4 1468.931 MB/s 3201.481 MB/s
(スレッド チームがゼロから作成され、オペレーティング システムが物理ページを によって予約された仮想アドレス空間にマッピングしているため、最初のタッチは遅くなりますmalloc(3)
)
1 つのスレッドが、単一の CPU <-> NB リンクのメモリ帯域幅を既に飽和させています。(NB = ノースブリッジ)
ソケットあたり 1 スレッド
threads 1st touch rewrite
1 1455.603 MB/s 3273.959 MB/s
2 2824.883 MB/s 5346.416 MB/s
3 3979.515 MB/s 5301.140 MB/s
4 4128.784 MB/s 5296.082 MB/s
NB <-> メモリ リンクの全メモリ帯域幅を飽和させるには、2 つのスレッドが必要です。
Octo-socket Intel Xeon X7550 - オクトコア CPU を搭載した 8-way NUMA システム (CMT 無効)
シングルソケット
threads 1st touch rewrite
1 1469.897 MB/s 3435.087 MB/s
2 2801.953 MB/s 6527.076 MB/s
3 3805.691 MB/s 9297.412 MB/s
4 4647.067 MB/s 10816.266 MB/s
5 5159.968 MB/s 11220.991 MB/s
6 5330.690 MB/s 11227.760 MB/s
1 つのメモリ リンクの帯域幅を飽和させるには、少なくとも 5 つのスレッドが必要です。
ソケットあたり 1 スレッド
threads 1st touch rewrite
1 1460.012 MB/s 3436.950 MB/s
2 2928.678 MB/s 6866.857 MB/s
3 4408.359 MB/s 10301.129 MB/s
4 5859.548 MB/s 13712.755 MB/s
5 7276.209 MB/s 16940.793 MB/s
6 8760.900 MB/s 20252.937 MB/s
帯域幅は、スレッド数にほぼ比例して増加します。シングル ソケットの観察に基づいて、8 つのメモリ リンクすべてを飽和させるには、ソケットあたり 5 スレッドとして分散された少なくとも 40 のスレッドが必要であると言えます。
NUMA システムの基本的な問題は、ファーストタッチ メモリ ポリシーです。メモリは、特定のページ内の仮想アドレスに最初にアクセスするスレッドが実行される NUMA ノードに割り当てられます。スレッド ピニング (特定の CPU コアへのバインド) は、スレッドの移行によってリモート アクセスが遅くなるため、このようなシステムでは不可欠です。ピンニングのサポートは、ほとんどの OpenMP ランタイムで利用できます。GCC にlibgomp
はGOMP_CPU_AFFINITY
環境変数があり、Intel にはKMP_AFFINITY
環境変数があります。また、OpenMP 4.0 では、ベンダー中立の場所の概念が導入されました。
編集:完全を期すために、 Intel Core i5-2557M (デュアルコア Sandy Bridge CPU with HT および QPI) を搭載した MacBook Air で 1 GiB アレイを使用してコードを実行した結果を次に示します。コンパイラは GCC 4.2.1 (Apple LLVM ビルド)
threads 1st touch rewrite
1 2257.699 MB/s 7659.678 MB/s
2 3282.500 MB/s 8157.528 MB/s
3 4109.371 MB/s 8157.335 MB/s
4 4591.780 MB/s 8141.439 MB/s
なぜシングルスレッドでも高速なのか? OS X コンパイラーによって変換され、 SSE4.2対応のベクトル化された という名前のバージョンが によって提供され、実行時に使用されることをgdb
示しています。一度に 16 バイトのメモリをゼロにする命令を使用します。そのため、スレッドが 1 つでもメモリ帯域幅はほぼ飽和状態になります。を使用するシングルスレッドの AVX 対応バージョンは、一度に 32 バイトをゼロにすることができ、おそらくメモリ リンクを飽和させます。memset(buf, 0, len)
bzero(buf, len)
bzero$VARIANT$sse42
libc.dylib
MOVDQA
VMOVDQA
ここでの重要なメッセージは、ベクトル化とマルチスレッド化は、操作を高速化する上で直交していない場合があるということです。
まあ、L3キャッシュは常にあります...
ただし、これはすでにメイン メモリの帯域幅によって制限される可能性が非常に高いです。並列処理を追加しても、状況が改善される可能性はほとんどありません。