第一に、この[種類の]質問はよく聞かれることを知っているので、私はできる限り読んだと言ってこれを前置きさせてください、そして私はまだ取引が何であるかを知りません。
大規模なアウターforループを並列化しました。ループの反復回数はさまざまで、通常は20〜150の間ですが、ループ本体は膨大な量の作業を行い、多くのローカル集中線形代数ルーチンを呼び出します(コードはソースの一部であり、外部の依存関係ではありません) 。ループ本体内には、これらのルーチンへの1000以上の呼び出しがありますが、それらはすべて互いに完全に独立しているため、並列処理の最有力候補になると思いました。ループコードはC++ですが、Cで記述された多くのサブルーチンを呼び出します。
コードは次のようになります。
<declare and initialize shared variables here>
#ifdef _OPENMP
#pragma omp parallel for \
private(....)\
shared(....) \
firstprivate(....) schedule(runtime)
#endif
for(tst = 0; tst < ntest; tst++) {
// Lots of functionality (science!)
// Calls to other deep functions which manipulate private variables only
// Call to function which has 1000 loop iterations doing matrix manipulation
// With no exaggeration, there are probably millions
// of for-loop iterations in this body, in the various functions called.
// They also do lots of mallocing and freeing
// Finally generated some calculated_values
shared_array1[tst] = calculated_value1;
shared_array2[tst] = calculated_value2;
shared_array3[tst] = calculated_value3;
} // end of parallel and for
// final tidy up
同期はまったくないはずです。スレッドが共有変数にアクセスするのは、だけであり、スレッドは、shared_arrays
によってインデックス付けされた、それらの配列内の一意のポイントにアクセスしますtst
。
(マルチコアクラスターで!)スレッドの数を増やすと、表示される速度(このループを5回呼び出す場合)は次のようになります。
Elapsed time System time
Serial: 188.149 1.031
2 thrds: 148.542 6.788
4 thrds: 309.586 424.037 # SAY WHAT?
8 thrds: 230.290 568.166
16 thrds: 219.133 799.780
注目に値するのは、2スレッドから4スレッドの間のシステム時間の大幅なジャンプであり、経過時間は2スレッドから4スレッドに移動すると2倍になり、その後ゆっくりと減少します。
OMP_SCHEDULE
さまざまなパラメータを試してみましたが、うまくいきませんでした。これは、各スレッドがmalloc/newとfree/deleteを頻繁に使用しているという事実に関連していますか?これは一貫して8GBのメモリで実行されていますが、それは問題ではないと思います。率直に言って、システム時間の大幅な増加により、スレッドがブロックされているように見えますが、なぜそれが発生するのかわかりません。
UPDATE 1 偽共有が問題になると本当に思ったので、ループが計算値をスレッドローカル配列に格納するようにコードを書き直し、最後にこれらの配列を共有配列にコピーします。残念ながら、私自身はほとんど信じていませんが、これは何の影響も及ぼしませんでした。
@cmeerwのアドバイスに従って、strace -fを実行しました。すべての初期化の後、数百万行の行があります。
[pid 58067] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58065] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 57684] <... futex resumed> ) = 0
[pid 58067] <... futex resumed> ) = 0
[pid 58066] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58067] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = 0
[pid 57684] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58065] <... futex resumed> ) = 0
[pid 58067] <... futex resumed> ) = 0
[pid 57684] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58066] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 57684] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] <... futex resumed> ) = 0
[pid 58066] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 57684] <... futex resumed> ) = 0
[pid 58067] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58066] <... futex resumed> ) = 0
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58067] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58066] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 57684] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] <... futex resumed> ) = 0
[pid 58067] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 57684] <... futex resumed> ) = 0
[pid 58067] <... futex resumed> ) = 0
[pid 58066] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58066] <... futex resumed> ) = 0
[pid 58065] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58066] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 57684] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58067] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 57684] <... futex resumed> ) = 0
誰かが何を意味するのか考えがありますか?スレッドがコンテキスト切り替えを頻繁に行っているように見えますか、それとも単にブロックとブロック解除を行っているだけですか?strace
同じ実装OMP_NUM_THREADS
を0に設定すると、これはまったく得られません。比較のために、1スレッド使用時に生成されるログファイルは486 KBであり、4スレッド使用時に生成されるログファイルは266MBです。
言い換えると、並列バージョンは、追加の4170104行のログファイルを呼び出します。
更新2
トムが提案したように、私はスレッドを特定のプロセッサにバインドしようとしましたが、役に立ちませんでした。OpenMP 3.1を使用しているので、を使用して環境変数を設定しますexport OMP_PROC_BIND=true
。同じサイズのログファイルと同じ時間枠。
更新3
プロットが厚くなります。これまでクラスターでプロファイルを作成しただけで、Macportsを介してGNU GCC 4.7をインストールし、Macbookで初めてコンパイルしました(AppleのGCC-4.2.1は、OpenMPが有効になっているときにコンパイラーのバグをスローします。そのため、私はこれまで、ローカルでコンパイルして並列実行していませんでした)。Macbookでは、基本的にあなたが期待する傾向が見られます
C-code time
Serial: ~34 seconds
2 thrds: ~21 seconds
4 thrds: ~14 seconds
8 thrds: ~12 seconds
16 thrds: ~9 seconds
このテストデータで反復しているいくつかのデータセットのメンバーが16未満であるため、これは驚くことではありませんが、終わりに向かってリターンが減少していることがわかります(したがって、たとえばfor-loop
7回の反復で16個のスレッドを生成しています) 。
それで、今、疑問が残ります-なぜクラスターのパフォーマンスがそれほどひどく低下するのですか?今夜は別のクアッドコアlinuxboxを試してみます。クラスターはGNU-GCC4.6.3でコンパイルされますが、それ自体がそのような違いを生むとは信じられませんか?
どちらltrace
もGDB
クラスターにインストールされていません(そして、さまざまな理由でそれらをインストールできません)。linuxboxがクラスターのようなパフォーマンスを提供する場合は、そこで対応するltrace
分析を実行します。
更新4
ああ、私の。Macbook ProをUbuntu(12.04)でデュエルブートし、コードを再実行しました。すべて実行されますが(これは多少安心です)、クラスターで見られるのと同じ、奇妙なパフォーマンスの悪い動作と、何百万ものfutex
呼び出しの同じ実行が見られます。UbuntuとOSXのローカルマシンの唯一の違いはソフトウェアです(そして私は同じコンパイラとライブラリを使用しています-おそらくglibc
OSXとUbuntuの実装に違いはありません!)これは何かLinuxがスレッドをスケジュール/配布する方法を使用します。いずれにせよ、ローカルマシンを使用すると、すべてが100万倍簡単になるので、先に進んltrace -f
で、何が見つかるかを確認します。クラスターの回避策を作成しました。forks()
別のプロセスをオフにして、ランタイムで完璧な1/2を提供するので、並列処理を実行することは間違いなく可能です...