4

OpenMP で並列化したプログラムを Cluster に移行しています。クラスタはスケジューラとして Lava 1.0 を使用しており、各ノードに 8 つのコアがあります。ジョブ スクリプトで MPI ラッパーを使用して、複数ホストの並列処理を行いました。

ジョブ スクリプトは次のとおりです。

#BSUB -q queue_name
#BSUB -x

#BSUB -R "span[ptile=1]"
#BSUB -n 1

#BSUB -J n1p1o8
##BSUB -o outfile.email
#BSUB -e err

export OMP_NUM_THREADS=8

date
/home/apps/bin/lava.openmpi.wrapper -bynode -x OMP_NUM_THREADS \
    ~/my_program ~/input.dat ~/output.out 
date

1つのホストのみでいくつかの実験を行いました。しかし、結果の一部を説明する方法がわかりません。

1.
-nOMP_NUM_THREADStime
1      4      21:12      
2      4      20:12      

ここで MPI が並列処理を行わないということですか? 2 番目のケースでは、すべての MPI プロセスに 4 つの OMP スレッドがあるため、最初のプロセスよりも高速な 800% の CPU 使用率を使用する必要があると考えました。

それを証明する別の結果は、
-nOMP_NUM_THREADStime
2      2      31:42      
4      2      30:47      

また、実行時間もかなり近いです。

2.
この場合、簡単な方法でこのクラスター内のこのプログラムを合理的な最適化速度で並列処理したい場合、すべてのホストに 1 つの MPI プロセス (1 つのコアを使用することを LFG に伝える) を配置するのが合理的ですか? OMP_NUM_THREADS = 8 を設定します。排他的に実行しますか?したがって、MPI はクロスノード ジョブでのみ機能し、OpenMP は内部ノード ジョブで機能します。(-n = ホストの数; ptile = 1; OMP_NUM_THREADS = 各ホストの最大コア数)

更新: プログラムは mpicc なしで gfortran -fopenmp によってコンパイルされます。MPI は、実行可能ファイルの配布にのみ使用されます。

UPDATE Mar.3: プログラムメモリ使用量モニター

ローカル環境:Mac 10.8 / 2.9 Ghz i7 /8GB メモリー

OpenMP なし

  • 実メモリサイズ:8.4MB
  • 仮想メモリ サイズ: 2.37 GB
  • 共有メモリ サイズ: 212 KB
  • プライベート メモリ サイズ: 7.8 Mb
  • 仮想プライベート メモリ: 63.2 MB

OpenMP あり (4 スレッド)

  • 実メモリサイズ:31.5MB
  • 仮想メモリ サイズ: 2.52 GB
  • 共有メモリ サイズ: 212 KB
  • プライベート メモリ サイズ: 27.1 Mb
  • 仮想プライベート メモリ: 210.2 MB

クラスタ ハードウェアの概要情報

各ホストには、ノードあたり 8 コアと 8GB メモリのデュアル クワッド チップが含まれています。このクラスター内のホストは、インフィニバンドで接続されています。

4

1 に答える 1

6

コメントで指定した情報を考慮して、最善の選択肢は次のとおりです。

  • 排他的なノード アクセスを要求します-x(既に実行しています)。
  • でノードごとに 1 つのスロットをリクエストし-n 1ます (すでに行っています)。
  • OMP_NUM_THREADSノードあたりのコア数に設定します (既に行っています)。
  • OpenMP スレッドのバインドを有効にします。
  • 実行可能ファイルを直接起動します。

ジョブ スクリプトは次のようになります。

#BSUB -q queue_name
#BSUB -x
#BSUB -n 1

#BSUB -J n1p1o8
##BSUB -o outfile.email
#BSUB -e err

export OMP_NUM_THREADS=8
export OMP_PROC_BIND=true

date
~/my_program ~/input.dat ~/output.out
date

OMP_PROC_BINDOpenMP 3.1 仕様の一部です。標準の古いバージョンに準拠するコンパイラを使用する場合は、GOMP_CPU_AFFINITYGCC やKMP_AFFINITYIntel コンパイラなどのベンダー固有の設定を使用する必要があります。スレッドをコアにバインドすると、オペレーティング システムが異なるプロセッサ コア間でスレッドを移動するのを防ぎます。これにより、特にデータの局所性が非常に重要な NUMA システム (複数の CPU ソケットと各ソケットに個別のメモリ コントローラーを備えたマシンなど) での実行が高速化されます。 .

異なる入力ファイルに対してプログラムの多くのコピーを実行したい場合は、配列ジョブを送信します。LSF では (Lava でもそうだと思いますが)、これはジョブ スクリプトを変更することによって行われます。

#BSUB -q queue_name
#BSUB -x
#BSUB -n 1

#BSUB -J n1p1o8[1-20]
##BSUB -o outfile.email
#BSUB -e err_%I

export OMP_NUM_THREADS=8
export OMP_PROC_BIND=true

date
~/my_program ~/input_${LSF_JOBINDEX}.dat ~/output_${LSF_JOBINDEX}.out
date

これにより、20 個のサブジョブの配列ジョブが送信されます ( -J n1p1o8[1-20])。%Iin-eはジョブ番号に置き換えられるため、err各ジョブから個別のファイルが取得されます。LSF_JOBINDEX環境変数は現在のジョブ インデックスに設定されます。つまり、1最初のジョブ、22 番目のジョブなどになります。


あなたのプログラムのメモリ使用量に関する私の質問は、それがどれだけのメモリを消費するかということではありませんでした。それは、単一の OpenMP ループで処理される典型的なデータセットの大きさについてでした。データセットが CPU の最終レベルのキャッシュに収まるほど小さくない場合は、メモリ帯域幅が考慮されます。コードが各データ項目に対して大量のローカル処理を行う場合、スレッド数に応じてスケーリングされる可能性があります。一方、単純で軽い処理を行う場合、特にコードが適切にベクトル化されている場合は、メモリ バスが 1 つのスレッドでも飽和する可能性があります。通常、これは、FLOPS/バイト単位のいわゆる運用強度によって測定されます。次のデータ要素がメモリからフェッチされる前に発生するデータ処理の量を示します。操作の集中度が高いということは、CPU で大量の計算処理が発生し、データがメモリとの間でほとんど転送されないことを意味します。このようなプログラムは、メモリ帯域幅に関係なく、スレッド数にほぼ比例してスケーリングします。一方、操作強度が非常に低いコードはメモリに拘束され、CPU を十分に活用できません。

メモリを大量に消費するプログラムは、スレッド数ではなく、使用可能なメモリ帯域幅でスケーリングします。たとえば、新しい Intel または AMD システムでは、各 CPU ソケットに独自のメモリ コントローラとメモリ データ パスがあります。このようなシステムでは、メモリ帯域幅は単一ソケットの帯域幅の倍数になります。たとえば、2 つのソケットを持つシステムは、単一ソケット システムの 2 倍のメモリ帯域幅を提供します。この場合、両方のソケットが使用されるたびに、コード実行時間の改善が見られる場合があります。たとえば、OMP_NUM_THREADSをコアの総数と等しくなるように設定OMP_NUM_THREADSした場合、または と等しくなるように設定し2てランタイムに両方のスレッドを異なるソケットに配置するように指示した場合です。 (これは、スレッドがベクトル化されたコードを実行していて、単一のスレッドがローカル メモリ バスを飽和させることができる場合に考えられるシナリオです)。

于 2013-03-03T20:02:19.943 に答える