リンクとノードを持つグラフ構造でシミュレーションを行うコードを最適化して並列化しようとしています。私は、各スレッド場所のため、このクラスのインスタンスを作成しますこのソルバーコードをどのように並列化できますか
class myDemoClass
{
bool volatile *isDone;
public:
void ExecuteSlice()
{
for(long i = TotalCount() - mThreadIndex; i >= 1; i -= threadCount)
{
long k = linkOrder[i];
Execute(k);
}
}
void Execute(long k)
{
long link = firstLink[k];
if (link == 0)
{
isDone[k] = true;
return;
}
double d = 0.;
for(; link != 0; link = nextLink[link])
{
long kk = getNode(link);
for(int x = 0; ! isDone[kk]; x++)
{} // Wait until data is ready. Time too short for sleep or event
d += fak[link]*res[kk];
}
d += res[k];
double d2 = d/fak2[k];
res[k] = d2;
res2[k] += d2;
isDone[k] = true;
}
}
:
void ExecuteAll()
{
for(long i = TotalCount(); i >= 1; i--)
{
long k = linkOrder[i];
long link = firstLink[k];
if (link == 0)
continue;
double d = 0.;
for(; link != 0; link = nextLink[link])
{
long kk = getNode(link);
d += fak[link]*res[kk];
}
d += res[k];
double d2 = d/fak2[k];
res[k] = d2;
res2[k] += d2;
}
}
私はこのようなクラスを実装することにより、複数のスレッドで動作するようにこれを作り直さ:メインのホットスポットは、このようなループがあります各スレッドはi
のすべての値のスライスで動作します。私は新しい配列bool volatile *isDone
を導入しました。処理されていないノードの結果を使用してはならないからです。 for(;...;){}
ループの代わりにスリープまたはイベントを挿入しようとしましたが、待機状態がこれに対して短すぎることが判明しました。
これはうまくいくようです。グラフが開始点からますます展開し、結果が正しいので、Execute()へのすべての呼び出しの10%だけが待機ループに入る必要があります。
しかし、驚くべきことに、8つのスレッドで新しいコードを使用すると、8コアのXEONマシンで測定可能なパフォーマンスの向上(または損失)はありません。私はこのコードがキャッシュ無効化に関して最適ではなく、主にisDone
とres
が複数のコアから書き込まれて読み込まれることを知っています。しかし、ほとんどの場合、逆参照されたインデックスは互いにかなり離れています。グラフには約1.000.000のノードとリンクがあります。
このコードの並列性を改善するにはどうすればよいですか?
スレッドコードはどこですか? – Mark
サイレントループの代わりにmutexまたはatomic compare_and_swapを使用する必要があります(コンパイラはおそらく削除します)。 –
@マーク。スレッディングコードが大きすぎてここに表示されません。しかし、主に各CPUのスレッドを開始し、ExecuteSlice()関数を起動します。 @Joel iSDoneがvolatileとしてマークされているため、サイレントルーピングは最適化されていません。 –