3

COMMON ブロックを使用して、コード全体で使用される配列を格納する mpi バージョンのプログラムがあります。残念ながら、COMMON ブロック サイズで配列を宣言する方法はありません。そのサイズは、実行時にしかわかりません。そこで、回避策として、ALLOCATABLE 配列を受け入れるモジュールにその配列を移動することにしました。つまり、COMMON ブロック内のすべての配列が消去され、代わりに ALLOCATE が使用されました。したがって、これが私のプログラムで変更した唯一のものです。残念ながら、プログラムのパフォーマンスはひどいものでした (COMMON ブロックの実現と比較した場合)。mpi 設定に関しては、各計算ノードに単一の mpi プロセスがあり、各 mpi プロセスには単一のスレッドがあります。てるの見つけたここで質問されますが、考えないでください(理解できません:))私のケース(各プロセスに単一のスレッドがある場合)にどのように適用できるか。助けていただければ幸いです。

これは、私が話していたことを示す簡単な例です (以下は疑似コードです)。

"ソースファイル":

SUBROUTINE ZEROSET()
   INCLUDE 'FILE_1.INC'
   INCLUDE 'FILE_2.INC'
   INCLUDE 'FILE_3.INC'
   ....
   INCLUDE 'FILE_N.INC'

   ARRAY_1 = 0.0
   ARRAY_2 = 0.0
   ARRAY_3 = 0.0
   ARRAY_4 = 0.0
   ...
   ARRAY_N = 0.0
END SUBROUTINE

ご覧のとおり、ZEROSET() には並列または MPI 要素はありません。FILE_1.INC、FILE_2、...、FILE_N.INC は、ARRAY_1、ARRAY_2 ... ARRAY_N が COMMON ブロックで定義されているファイルです。そんな感じ

REAL ARRAY_1
COMMON /ARRAY_1/ ARRAY_1(NX, NY, NZ)

NX、NY、NZ は、PARAMETER ディレクティブを使用して記述された明確に定義されたパラメーターです。モジュールを使用するときは、すべての COMMON ブロックを破棄しただけなので、FILE_I.INC は次のようになります。

REAL, ALLOCATABLE:: ARRAY_I(:,:,:)

そして、上記の「INCLUDE 'FILE_I.INC'」ステートメントを「USE FILE_I」に変更しました。実際、並列プログラムを実行する場合、特定の 1 つのプロセスがドメイン全体 (NX、NY、NZ) を必要としないため、パラメーターを計算してから ARRAY_I を割り当てます (一度だけ!)。

サブルーチン ZEROSET() は、COMMON ブロックで 0.18 秒、モジュールで 0.36 秒実行されます (配列の次元が実行時に計算される場合)。そのため、パフォーマンスは 2 倍悪化しました。

すべてが明確になったことを願っています。大変助かります。

4

3 に答える 3

3

コンパイラはコンパイル時にサイズを認識しないため、モジュールで割付け配列を使用するとパフォーマンスが低下することがよくあります。次のコードを使用すると、多くのコンパイラでパフォーマンスが大幅に向上します。

   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   call Z(A,N)
   end subroutine

   subroutine Z(A,N)
   Integer N
   real A(N,N)
   do stuff here
   end

次に、このコード:

   subroutine X
   use Y  ! Has allocatable array A(N,N) in it
   do stuff here
   end subroutine

コンパイラは、配列が NxN であり、do ループが N を超えていることを認識し、その事実を利用することができます (ほとんどのコードは配列に対してそのように機能します)。また、「do stuff here」のサブルーチン呼び出しの後、コンパイラは、配列「A」のサイズが変更されたか、メモリ内の場所が移動した可能性があると想定して、再チェックする必要があります。それは最適化を殺します。

これにより、パフォーマンスのほとんどが回復するはずです。

共通ブロックもメモリ内の特定の場所に配置されているため、最適化も可能です。

于 2011-09-09T15:55:50.510 に答える
0

実際、ここでの問題は、スタックとヒープメモリの組み合わせであり、実際にはコンパイラの最適化に基づいていると思います。使用しているコンパイラによっては、より効率的なメモリ ブランキングを実行する場合があり、メモリの固定チャンクの場合、サブルーチン内の範囲と場所をチェックする必要さえありません。したがって、固定サイズの配列では、オーバーヘッドはほとんど発生しません。このルーチンは非常に頻繁に呼び出されますか、またはなぜこの 0.18 秒を気にするのですか? それが実際に関連する場合、最良のオプションは 0 設定をまったく取り除くことであり、代わりに、たとえば最初の反復ループを分離して初期化に使用します。この方法では、追加のメモリアクセスを導入する必要はありません。 0 で初期化します。ただし、一部のコードが重複します...

于 2011-09-10T18:48:40.560 に答える
0

配列を使用した Fortran のパフォーマンスに関しては、次の理由が考えられます。

  1. スタック VS ヒープ上の配列ですが、これがパフォーマンスに大きな影響を与える可能性があるとは思えません。
  2. passing arrays to a subroutine, because the best way to do that depends on the array, see this page on using arrays efficiently
于 2011-09-09T19:08:04.163 に答える