3

arrayfire を使用して、GPU (OpenCL) の助けを借りて一部の C++ コードを高速化します。私は 600MB 以上の af::array を持っており、列の次元に沿って反転させて転置する必要があります。

これまでのところ、C++ ルーチンを使用してこれらの操作をほぼ適切に実行しました。しかし、AFでそれをやりたいのですが、AFライブラリの過度のメモリ使用に気付きました. これには2つの問題があります:

1) 300MB 配列に対する操作 (flip や T など) が 900MB をはるかに超えるメモリを使用する理由がまったくわかりません。2) 配列 foo のコピーを作成しないようにする方法を知りたいです。操作を別の関数内にカプセル化することで、コピーを取り除くことができると考えました。

次のようなコードがあります。

void prepare_array(af::array &a) {
  af::array b = af::flip(a, 1);              // ~1400MB
  a = b.T();                                 // ~3000MB
}

af::array foo = af::randn(768,16384,3,1,c64); // ~300MB
prepare_array(foo);
af::deviceGC();                              // ~600MB

この操作は一度だけ実行する必要があるため、速度はメモリ使用量ほど重要ではありませんが、AF フレームワーク内でこの操作を実行することをお勧めします。

(すべてのメモリ使用統計は、debian の NVIDIA カーネル ドライバー パッケージから gpustat で読み取られます。)

CPU バックエンドのメモリ使用量も過剰です。


返信 umar-arshad のおかげです: 前回 mem-usage のプロファイリングを行ったとき、CPU でコードを実行しました - それが同じように動作すると仮定して。GPU で測定値を再確認し、gpustat と nvidia-smi の両方を使用しました。実際、コードは測定値が異なり、説明したとおりです。少なくとも GPU の部分については、今では完全に理にかなっています。

おそらく CPU 上の foo は最初は f64 だけです。これは、実部のみが使用され、フリップまたは転置のいずれかを使用して c64 になるためです。

「割り当てにより、特定のプラットフォームのすべてのキューで暗黙的なデバイス同期がトリガーされる」という事実と、この Web サイト: http://forums.accelereyes.com/forums/viewtopic.php?f=17&t=43097&p=61730&hilit=copy+host+ memory+into+an+array#p61727 および af::printMemInfo(); AFのメモリ処理のほとんどを最終的に理解するのに役立ちました。プログラムを大幅に高速化します。

ただし、現時点では、これら 2 つの操作をインプレースで (または可能な限り少ないオーバーヘッドで) 実行する唯一の代替手段は、次を使用することです。

// Generate/store data in std::array<af::cdouble> foo_unwrap = new af::cdouble[768*16384*3*1];

// Flip/Transpose foo_unwrap in plain C/C++, like in:
// for(column = 0; column < max_num_column/2; column++)
//   swap column with max_num_column-1-column
//
// http://www.geeksforgeeks.org/inplace-m-x-n-size-matrix-transpose/
//   but for Column-Major Order matrices
//
// and afterwards whenever needed do ad-hoc:
af::cdouble* first_elem = (af::cdouble*) &(foo_unwrap[0]); // to ensure correct type detection via AF
af::array foo = af::array(768,16384,3,1, first_elem, afDevice);

ただし、Row-/Column-Major 形式とインデックス マジックを気にしたくなかったので、これは非常に面倒です。だから私はまだここで提案を探しています。

4

1 に答える 1

3

ArrayFire はメモリ マネージャーを使用して、不要な割り当てと割り当て解除を回避します。これが必要なのは、すべての割り当てが、特定のプラットフォームのすべてのキューで暗黙的なデバイス同期をトリガーするためです。これは非常にコストがかかる可能性があるため、ArrayFire はaf::array範囲外の s を追跡し、必要に応じてそれらを再利用します。ArrayFire も起動時にメモリを割り当てます。

あなたの場合、randu呼び出しで〜600MBを割り当てています(c64複雑なdoubleであるため、各要素は16バイトです)。に格納されているフリップ操作用に別の 600MB が割り当てられていbます。Transpose は 600MB を割り当てますが、再利用のために古い値を予約します。この時点で、これらの操作のために約 1800 MB のメモリが割り当てられています。

prepared_array関数呼び出しから戻ると、 b はスコープ外になり、削除のマークが付けられます。この時点で、それぞれ 600MB の 3 つのバッファーがあります。2 つのバッファは未使用ですが、ArrayFire はこれらのバッファを将来の操作で使用できます。関数を呼び出すと、未使用の配列は両方とも解放されますdeviceGCが、同様のサイズの配列を割り当てる可能性があるため、これらを保持しておくと便利です。

関数を使用して、ArrayFire によって行われた割り当てを追跡できaf::printMemInfo()ます。

免責事項: 私は ArrayFire の開発者の 1 人です。

于 2016-12-04T05:07:43.633 に答える