18

非常に大きな (L2 キャッシュよりも大きい) メモリ ブロックがあり、すべてゼロに設定しなければならない場合があります。memset はシリアル コードでは有効ですが、パラレル コードではどうでしょうか。同時スレッドから memset を呼び出すと実際に大きな配列の速度が上がる場合、誰か経験がありますか? または、単純な openmp parallel for loops を使用していますか?

4

2 に答える 2

24

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 にlibgompGOMP_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$sse42libc.dylibMOVDQAVMOVDQA

ここでの重要なメッセージは、ベクトル化とマルチスレッド化は、操作を高速化する上で直交していない場合があるということです。

于 2012-07-20T13:04:57.917 に答える
1

まあ、L3キャッシュは常にあります...

ただし、これはすでにメイン メモリの帯域幅によって制限される可能性が非常に高いです。並列処理を追加しても、状況が改善される可能性はほとんどありません。

于 2012-07-20T10:06:03.157 に答える