NUMA OpenMP MPbind PGIコンパイラ
PGI コンパイラは、AMD 社の Opteron やインテル社の Nehalem 以降の NUMA アーキテクチャ上での OpenMP スレッド並列を制御するための環境変数を提供します。これをマルチプロセッサ環境変数と言い、特にデュアルコア・プロセッサを含むシステムのスレッド並列制御に有効です。ここでは、この環境変数の機能について紹介します。なお、PGI コンパイラは、AMD64/intel®64 のプロセッサのマイクロ・アーキテクチャに応じた最適化だけでなく、UMA/NUMA アーキテクチャにも「最適化」する機能を有しています。(2005年11月24日 初稿、2011年8月修正)
-mp コンパイルオプション
コンパイラオプションとして PGI 6.0-5 より、-mp=numa (numa サブオプション)が追加されております。これは、NUMA アーキテクチャを採用する AMD並びにインテルのマルチ(コア)プロセッサシステム用のオプションです。インテルRのプロセッサシステムでは、Hehalem アーキテクチャ以降で NUMA を採用しています。
-mp オプションは、OpenMP の指示行を含むプログラムの並列処理モジュールを生成するオプションですが、その性能の向上のために、NUMA システムライブラリをリンクし、process(thread)-to-CPU スケジューリングの CPU affinity (親和度)等の機能を有効にするオプションとなります。NUMA アーキテクチャのシステムにおいて有効であり、現在、Linux やWindows の OS 下でこの NUMA ライブラリをリンクすることが可能です。これによって、NUMAアーキテクチャに対するユーザ側での制御自由度が向上されております。
マルチコア上でのスレッド制御を行うためのマルチプロセッサ(mp)用環境変数
マルチコア環境でのスレッド制御を行うためのマルチプロセッサ(mp) 環境変数(MP_BIND、MP_BLIST、MP_SPIN) が PGI 6.0-5 から追加されました。これらの環境変数は、スレッドの実行を別の CPU に移行(切り替え)させるかどうかの設定 (MP_BIND)、スレッドを物理的な CPU にバインドして固定させるための設定 (MP_BLIST)、バリア同期の待ち状態に入ったと時に、アイドル・スレッドがどの程度、プロセス・サイクルを消費するかを制御する設定 (MP_SPIN) を行うものです。
NUMAアーキテクチャでの実行スレッド制御
Intel Nahalem や AMD Opteron システムのような共有メモリ型に属する NUMA アーキテクチャの構成を簡単に言うと、プロセッサそれぞれに直結したメモリシステムを持つ構成で、さらにプロセッサ間を HyperTransport と言う高速な通信路で直結してマルチ・プロセッサシステムを構成したものと考えればよい。各プロセッサに直結したメモリは、ノード内の全プロセッサからアクセス可能であり、この意味で共有メモリ型として機能します。しかし、当然ながら、プロセッサ自身に直結したメモリアクセス(ローカル・アクセス)の性能と他のプロセッサに直結したメモリをアクセス(リモート・アクセス)する性能とは異なり、リモートアクセスの方が多少性能が劣化します。とは言え、MUMA は、従来のバス型構成でメモリにアクセスする方法に比べ、メモリ帯域のスケーラビリティが良いという特長を持ちます。
プログラムを実行する際、実行モジュールをメモリにロードし、プログラムの配列データ等をアロケートすることは NUMA カーネルの役目ですが、これとは別にユーザレベルで各並列スレッドがどの物理的な CPU コアで実行させるかを制御できると、実行性能の最適化の自由度が増します。一般的には、カーネルが指定する CPU(Core) 割り当て、メモリインタリーブ方法をそのまま使用しても何ら問題ないですが、例えば、プログラムでの論理スレッド 0 のタスクを物理的な CPU 0 に常に割り当てて計算したいと言うこともあります。また、Linux カーネルのデフォルトは、非常に短い時間で CPU 割り当てスケジューリングを行うように設定されており、プログラム実行中、論理スレッドが各 CPU に渡り歩くと言った現象が見られることがあります。HPC の計算のように、最初からスレッドタスク 0 は CPU 0、スレッド 1 は、CPU 1 にと言った固定して使えるようにすれば、CPU スケジューリングのオーバヘッドの軽減や、 NUMA であればプロセッサ自身のローカルメモリに直アクセスする機会が増えると言った低オーバヘッドの計算ができます。スレッド(プロセス)を固定した CPU に固定させるような機能を process-to-CPU(core) スケジューリングにおける affinity と言うが、この機能がまさに上述した機能と言えます。また、特に マルチ・プロセッサの場合、性能を考慮して二つのCPUコアのうち、常に一つだけ使用すると言ったことにおいても、CPUバインド機能は有効である。PGI 6.0-5 以降のバージョンでは、この CPU バインドする機能を使用できる。
MP_BINDとMP_BLISTの使用例
この機能を使用するためには、以下のことを行う必要があります。
上記の設定を行った後、その OpenMP 並列実行で CPU を自由にバインドできる状況を以下に示します。これは、過去の PGI 6.x バージョンを利用して行った例です。ここでは、実験機材がデュアルコアの 2 CPUコアのシステムで簡単な例を示します。こうした使い方は、実際には 2way, 4way 等のシステムの場合、有効な活用ができます。
【コンパイル オプション】 pgf95 -fastsse -Minfo -mp=numa xx.f (-mp は OpenMP 並列を行うオプション) 【環境変数の設定】 export MP_BIND=yes (OpenMPスレッドを物理 CPU(core) にバインドすることを宣言) export OMP_NUM_THREADS=2 (スレッド数を2個使用することを宣言) この後、環境変数を変えて実行する
プロセッサの動きを xosview で観察する。左図は、並列実行前の状況。グリーンがアイドル状態を示す。CPU0、CPU1 は 0% あるいはカーネルで使用されている使用率が表示されている。
● MP_BLIST=0,1を設定する
次に、MP_BLIST 変数でCPUバインドの方法を指定する。 並列スレッド 0 を CPU0、 並列スレッド 1 を CPU1 に固定して使用する場合の実行を行う。NPB3.2 OpenMP バージョンの FT ベンチマークを題材とした。性能は 2 スレッドで 1232 MFLOPS となる。(この性能値は、2005年当時の AMD Athlon プロセッサ上の性能である)
$ export MP_BLIST=0,1 $ ./ft.A.numa NAS Parallel Benchmarks (NPB3.2-OMP) - FT Benchmark Size : 256x 256x 128 Iterations : 6 Number of available threads : 2 class = A FT Benchmark Completed. Class = A Size = 256x 256x 128 Iterations = 6 Time in seconds = 5.79 Total threads = 2 Avail threads = 2 Mop/s total = 1232.69 Mop/s/thread = 616.35 Operation type = floating point Verification = SUCCESSFUL Version = 3.2 Compile date = 23 Nov 2005 Compile options: F77 = pgf90 FLINK = $(F77) FFLAGS = -mp=numa -fastsse -Minfo -Mipa=fast FLINKFLAGS = -fastsse -mp=numa -Mipa=fast
2CPU を使用して実行しているので、二つの CPU 0, 1 が100% 使用されていることが分かる。
● MP_BLIST=0,0を設定する
並列スレッド 0 を CPU0、 並列スレッド 1 を CPU0 に同じ CPU に固定して使用する場合の実行を行う。性能は 2 スレッドで 684 MFLOPS となる。同じCPUを使用するため、並列性能は劣化する。
$ export MP_BLIST=0,0 $ ./ft.A.numa NAS Parallel Benchmarks (NPB3.2-OMP) - FT Benchmark Size : 256x 256x 128 Iterations : 6 Number of available threads : 2 T = 1 Checksum = 5.046735008193D+02 5.114047905510D+02 T = 2 Checksum = 5.059412319734D+02 5.098809666433D+02 T = 3 Checksum = 5.069376896287D+02 5.098144042213D+02 T = 4 Checksum = 5.077892868474D+02 5.101336130759D+02 T = 5 Checksum = 5.085233095391D+02 5.104914655194D+02 T = 6 Checksum = 5.091487099959D+02 5.107917842803D+02 Result verification successful class = A FT Benchmark Completed. Class = A Size = 256x 256x 128 Iterations = 6 Time in seconds = 10.43 Total threads = 2 Avail threads = 2 Mop/s total = 684.00 Mop/s/thread = 342.00 Operation type = floating point Verification = SUCCESSFUL Version = 3.2 Compile date = 23 Nov 2005 Compile options: F77 = pgf90 FLINK = $(F77) FFLAGS = -mp=numa -fastsse -Minfo -Mipa=fast FLINKFLAGS = -fastsse -mp=numa -Mipa=fast
2CPスレッド実行であるが、 CPU0 だけを使用するように指示したため、CPU0 が100% 使用されていることが分かる。
● MP_BLIST=1,1を設定する
並列スレッド 0 を CPU1、 並列スレッド 1 を CPU1 に同じ CPU に固定して使用する場合の実行を行う。性能は 2 スレッドで 684 MFLOPS となる。同じCPUを使用するため、並列性能は劣化する。
$ export MP_BLIST=1,1 $ ./ft.A.numa NAS Parallel Benchmarks (NPB3.2-OMP) - FT Benchmark Size : 256x 256x 128 Iterations : 6 Number of available threads : 2 class = A FT Benchmark Completed. Class = A Size = 256x 256x 128 Iterations = 6 Time in seconds = 10.43 Total threads = 2 Avail threads = 2 Mop/s total = 684.38 Mop/s/thread = 342.19 Operation type = floating point Verification = SUCCESSFUL Version = 3.2 Compile date = 23 Nov 2005 Compile options: F77 = pgf90 FLINK = $(F77) FFLAGS = -mp=numa -fastsse -Minfo -Mipa=fast FLINKFLAGS = -fastsse -mp=numa -Mipa=fast
2CPU スレッド実行であるが、 CPU1 だけを使用するように指示したため、CPU1 が100% 使用されていることが分かる。