7239376c.jpgA. NUMAの概要

NUMAとはNon-Uniform Memory Accessの略でヌマと読みます。
1ホストマシンの中に複数の演算コアやメモリバンクが搭載されている場合に、それらの利用方法を指定できるシステムです。
ここではNUMAの概念と利用方法を説明します。
*説明の中ではQuad-Opteronを4ソケット、メモリを64GB搭載したマシンが2台接続されている環境を想定しています。またそれぞれのホスト名をtest1、test2としています。

MPIを利用して4並列で単一ドメインのWRFを実行する場合を想定してみましょう。
WRFのドメインはMPIによって4等分に分割され、4つの演算コアに処理が渡されます。このときの渡し方は通常、図1の様になります。
各ノードの1つのコアを使用し、メモリ使用量が全てのバンクで均一になるようにメモリ使用が割り振られています。
これがNUMAをしてしない“Uniform”な状態の割り当てになります。負荷が分散して一見効率が良さそうに見えます。

ところが、どんなに早いHyperTransportでもノード1のCPUからバンク2のメモリへのアクセスは時間がかかります(特に対角方向のバンク)。
以下に示すのは、ノード0のCPUとバンク2のメモリを用いて計算した場合とノード0のCPUとバンク0のメモリを用いて計算した場合のWRFの1タイムステップ所要時間の比較です。

CPU0 + Bank2 → domain 2: 7.55220 elapsed seconds.
CPU0 + Bank0 → domain 2: 6.35240 elapsed seconds.

Bank2のメモリを使用した場合に比べてBank0のメモリを使用した場合には約1.2秒計算が速くなっています。
これを解決するためにNUMAポリシーを設定してやることで図2の様にCPUとメモリを割り当てをほぼ任意に指定することができます。
ノード0に演算コアを集中させ、メモリをバンク0に限定することで負荷は偏っていますが通信には都合のよい状況になっています。
このように並列計算時に計算内容に合わせてホスト上のCPUとメモリの割り当て方針を決める設定がNUMAです。
以降、NUMAの設定方法を説明します。


B. NUMAポリシーの設定方法

NUMAポリシーの設定方法は実行コマンドの前に“numactl --option”と付け足すだけです。
要するにtimeコマンドなどと同じように使えばよいわけです。まずは実例を示します。
*以下のコマンドのなかで“\\”は書式上の改行を表し、実際は必要ありません。

例1). フルコアを使ってwrf.exeを実行する場合
$numactl --cpunodebind=0,1,2,3 --interleave=all mpiexec -n 16 -host test1 ./wrf.exe \\
: -n 16 -host test2 ./wrf.exe >/dev/null 2>&1 &
この例では全てのノードにおいてCPUとメモリの割り当てをノード内に集中させるように指定しています。

例2). test1で8コア、test2で12コア使ってwrf.exeを実行する場合
$numactl --cpunodebind=0,1,2 --interleave=0,1,2 mpiexec -n 8 -host test1 ./wrf.exe \\
: -n 12 -host test2 ./wrf.exe >/dev/null 2>&1 &
この例ではノード0、1、2においてCPUとメモリの割り当てをノード内に集中させるように指定しています。
したがって、この計算においてどのホスト上でもノード3は使用しません。

例3). 12コアずつ使ってrealを実行しながらtest1で解凍、test2で圧縮を行う場合
test1$numactl --cpunodebind=0,1,2 --interleave=0,1,2 mpiexec -n 12 -host test1 ./real.exe \\
: -n 12 -host test2 ./real.exe >/dev/null 2>&1 &
test1$numactl --cpunodebind=3 --membind=3 bunzip2 -v met_em*.bz2
----------------------------------------------------------------
test2$numactl --cpunodebind=3 --membind=3 bzip2 -v wrfout*
この例では、まずノード0、1、2においてCPUとメモリの割り当てをノード内に集中させるように指定してMPIでreal.exeを走らせています。
したがって、この時点でどのホスト上でもノード3は使用しません。
その後、test1上のノード3を使用してファイルの解凍、test2上のノード3を使用してファイルの圧縮を行っています。
すこし複雑ですがこのように設定すると、WRF系の計算とその他の計算のCPUとメモリの棲み分けができ、他のプログラムによってWRF計算のトラフィックを増やすことも無く、同時に他のプログラムがトラフィックの多い経路を使用する回数を減らすこともできます。

ここでコマンドオプションの詳細について説明します。さらなる詳細はnumactlのmanページを参照してください。

--physcpubind=0,1,2…
CPUをコア番号で指定します。ノード0から順に0番をスタートにCPU番号が振られており、SSRRは16コアあるので0〜15までの数字となります。並列計算の場合、MPIで指定するスレッド数とnumactlで指定するコア数が一致するようにしてください。

--cpunodebind=0,1,2…
CPUをノード番号(ソケット番号)で指定します。ノードは0〜3まであります。並列計算の場合、MPIで指定するスレッド数をノード数×4で表されるコア数がカバーできるようにしてください。

--membind=0,1,2…
メモリバンクをバンク番号(ノード番号)で指定します。指定するバンク数×16GBで計算に必要なメモリ量をカバーできるようにしてください。

--interleave=0,1,2…
メモリバンクをバンク番号(ノード番号)で指定します。指定するバンク数×16GBで計算に必要なメモリ量をカバーできるようにしてください。

--show
デフォルト設定や各オプションで選択可能な番号を表示します。

--hardware
メモリのバンク毎の最大容量と現在の使用容量を表示します。

*membindとinterleaveの違いはメモリアドレスの与え方です。
membindではあるバンクがいっぱいになるまでアドレスを割り当て次のバンクへ移りますが、interleaveでは全てのバンクにラウンドロビン的にアドレスを割り当てます。
そのためinterleaveではDDRチャネル数を超えて並列的にメモリアクセスができるようになり、メモリバンド幅が向上することがあります。


以上で説明終了です。
条件によって最適設定は異なりますので、いろいろ試してみてください。