NUMAポリシー
NUMA環境で,スレッドやメモリの配置を明示的に指示する方法を調べたので,メモしておく. 1つの筐体に複数のマルチコアCPUを搭載する環境では,メモリはCPUごとに接続される. このような環境では, バスを介して直接つながったメモリ(ローカルメモリ)とCPUの間で,高速にデータを転送できる. 一方,直接つながっていないメモリ(リモートメモリ)にデータを転送するためには, QPI(QuickPath Interconnect)などのインタコネクトを介して, 他のCPUソケットを経由する必要がある. このようにメモリアクセスの仕組みが複数存在する環境は, NUMA(Nun Uniform Memory Access)と呼ばれる.
NUMA環境で,マルチスレッドプログラムをチューイングするためには, スレッドをどのコアに割り当てるか,データをどのメモリに配置するかが重要になる. これらの制御は,numactlコマンドあるいはlibnumaライブラリによって実現できる.
numactlによる制御
ソースコードを修正できない状況(あるいは面倒くさい状況)では,
numactlコマンドを使って,アフィニティを設定する.
--cpubind=<nodemask>
オプションでスレッドをどのノードで実行するか指示し,
--membind=<nodemask>
オプションでデータをどのノードのメモリに配置するかを指示する.
<nodemask>
には,--membind=0,1
のようにノード番号をカンマ区切りで記述する.
他にも,
優先してメモリを割り当てるノードを指示する--preferred=<nodenumber>
オプションや
複数のノードにインタリーブでメモリを割り当てる--interleave=<nodemask>
オプションがある.
ノード番号などハードウェア情報は,--hardware
オプションで確認できる.
また,プロセスに割り当てられたポリシーを確認するには,--show
オプションを使う.
|
|
libnumaによる制御
ソースコードを修正できる状況では,libnumaを使う.
numactlはプログラム全体のメモリ割付けを制御するが,
libnumaは個々のメモリ領域を個別に制御する.
libnumaを利用するには,コードにnuma.h
をヘッダを追加し,
共有ライブラリをリンクする(-lnuma
).
ノード番号の集合<nodemask>
は,nodemask_t
型変数に格納する.
メモリの確保では,どのようにメモリを確保するのかに従って,
適切なnuma_alloc_*
ファミリの関数を利用する.
メモリの解放には,共通してnuma_free
関数を利用する.
さらに,numa_run_on_node
あるいはnuma_run_on_node_mask
関数を使うことで,
スレッドをどのノードで実行するかを明示できる.
|
|
OpenMPによる制御
OpenMPを使ってマルチスレッドを実現している場合には,環境変数によってアフィニティを制御する.
制御方法は,コンパイラごとに異なり,例えば,
PGIコンパイラであればMP_BIND
およびMP_BLIST
を設定する.