2013-03-04 31 views
10

私は並列化できるC++プログラムを持っています。私はVisual Studio 2010、32ビットコンパイルを使用しています。プログラムの構造は、各some_computations()ため並列タスクは、pplやOpenMPよりもboost :: threadのほうがパフォーマンスが良い

#define num_iterations 64 //some number 

struct result 
{ 
    //some stuff 
} 

result best_result=initial_bad_result; 

for(i=0; i<many_times; i++) 
{ 
    result *results[num_iterations]; 


    for(j=0; j<num_iterations; j++) 
    { 
     some_computations(results+j); 
    } 

    // update best_result; 
} 

に従っている要するに

私はインナーfor - ループを並列化(いくつかのグローバル変数を読み取る、ないグローバル変数は変更されない)は無関係です。

私の最初の試みは、ブースト::スレッド、結果は良かった

thread_group group; 
for(j=0; j<num_iterations; j++) 
{ 
    group.create_thread(boost::bind(&some_computation, this, result+j)); 
} 
group.join_all(); 

とあったが、私はより多くをしようとすることを決めました。

私は結果がboost::threadのものより悪かった

#pragma omp parallel for 
for(j=0; j<num_iterations; j++) 
{ 
    some_computations(results+j); 
} 

のOpenMPライブラリを試してみました。

は、それから私は、 皆さんライブラリを試してみましたが、 parallel_for()を使用:

Concurrency::parallel_for(0,num_iterations, [=](int j) { 
    some_computations(results+j); 
}) 

結果は最悪でした。

この現象は非常に驚くべきことです。 OpenMPとpplは並列化用に設計されているので、boost::threadより良い結果が期待できます。私が間違っている?

boost::threadはなぜ私に良い結果をもたらしますか?

+2

"より良い"と評価してください。実行時間とスレッド数を比較する'boost :: thread'では64スレッドを作成しています。 OpenPMはワーカースレッドのチームを使用し、その数はデフォルトで仮想CPUの数になります。 PPLはスレッドプールも使用し、ワークバランシングも実装されているため、OpenMPより高いオーバーヘッドを持ちます。 –

+0

OpenMPとpplを使用して、試行ごとに同じ番号(32または64)を使用しました。おそらく指摘したように、スレッド数をコア数と同じに設定するとより良い結果が得られます。私が試してみます。 – 888

+1

質問に答えるのはほとんど不可能です。 'some_computations'は何をしていますか? OpenMPが実際にオーバーヘッドを下げているが、共有キャッシュラインへの書き込みがたくさんあると、結果的にキャッシュ無効化の狂気が実際に遅くなる可能性があります)。 'some_operation'が機能するために、バリアントごとに並列化されたブロックを実行するにはどれくらいの時間がかかりますか? – Grizzly

答えて

9

OpenMPまたはPPLは悲観的なことをしません。彼らは言われた通りにやっていますが、ループのパラレル化を試みるときに考慮すべきいくつかのことがあります。

これらの機能を実装していないと、実際の原因が何であるかは言い難いです。

また、各ループ内の操作が同じループ内の他の繰り返しに依存する場合は、競合が発生し、処理が遅くなります。あなたのsome_operation関数が実際に何をしているのかわからないので、データの依存関係があるかどうかは分かりません。

本当に並列化できるループは、反復のいずれにおいても共有メモリがアクセスされないように、各反復を他のすべての反復とは無関係に実行できる必要があります。ですから、ローカル変数に書き込んでから、最後にコピーしてください。

すべてがループで並列化できるわけではありません。実行される作業の種類によって大きく異なります。

たとえば、並列化に適しているものは、画面バッファの各ピクセルで処理が行われているものです。各ピクセルは他のすべてのピクセルとは完全に独立しているため、スレッドはループの1回の反復を実行し、反復間のループ内の共有メモリまたはデータ依存性を待つことなく作業を行うことができます。

また、連続した配列を持つ場合、この配列は部分的にキャッシュ行にある可能性があります。スレッドAで要素5を編集し、スレッドBで要素6を変更すると、キャッシュ競合が発生する可能性があります。また、同じキャッシュラインに存在するため、処理速度が遅くなります。 と偽の共有である現象

ループの並列化を行う際には、多くの点を考慮する必要があります。

+0

' some_operation'はオフセットを配列にとり、配列は複数のスレッドで共有されます。私は、PPLまたはOpenMPのいずれかがあなたがその配列に書き込んでいないガーベージを作ることができるか、または何かがその配列に書き込んでいることを知りません。したがって私の答えは変わらない。 –

+1

最初の段落は真ではありません。 OpenMPもPPLも、あなたが変数を共有することを心配するものではなく、彼らが働く方法では悲観的でも楽観的でもありません。両方とも命令型プログラミングの概念です。つまり、式をヒントとして扱うのではなく、コンパイラがコードを並列化します。共有変数の適切な扱いは、プログラマーだけに任せます。 –

2

つまり、openMPは主に共有メモリに基づいており、タスク管理とメモリ管理の追加コストがかかります。 pplは、一般的なデータ構造およびアルゴリズムの汎用パターンを処理するように設計されているため、複雑なコストがかかります。どちらも追加のCPUコストがかかりますが、単純な落ち込みのあるboostスレッドはありません(boostスレッドは単なるAPIラッピングです)。そのため、両方ともあなたのboostバージョンよりも遅いのです。そして、試算された計算は同期なしで互いに独立しているので、openMPboostバージョンに近いはずです。

単純なシナリオで発生しますが、複雑なシナリオでは、複雑なデータレイアウトとアルゴリズムでは、コンテキストに依存する必要があります。

+2

OpenMPはメッセージパッシング用に設計されていませんが、MPIはマシージを渡すものです。 – Moss

+1

@Moss、ありがとう、私はOpenMPとMPIを混ぜた。 OpenMPは共有メモリに基づいています。 –

関連する問題