ループの反復は、キャッシュの有効利用のために最適化するようスレッドに割り当てるべきであることを示すヒント。
#include "tbb/partitioner.h"
class affinity_partitioner;
affinity_partitioner は、ループ・テンプレートを実行する際に、同じ affinity_partitioner オブジェクトを使用するそのループ (または別のループ) の以前の実行で使用したワークの分割に同じタスク・アフィニティー・パターンを使用すべきであることを示します。
ほかのパーティショナーとは異なり、アフィニティーを最適化するループ・テンプレートに同じ affinity_partitioner オブジェクトを渡すことが重要です。アフィニティーの影響の詳細は、チュートリアルの「帯域幅とキャッシュの有効利用」セクションを参照してください。
affinity_partitioner は、一般に次の場合にパフォーマンスを向上させます。
namespace tbb {
class affinity_partitioner {
public:
affinity_partitioner();
~affinity_partitioner();
};
}
次のサンプルは、キャッシュの有効利用により恩恵が得られます。このサンプルは、加法的な 1 次元オートマトンをシミュレートします。
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include "tbb/partitioner.h"
using namespace tbb;
const int N = 1000000;
typedef unsigned char Cell;
Cell Array[2][N];
int FlipFlop;
struct TimeStepOverSubrange {
void operator()( const blocked_range<int>& r ) const {
int j = r.end();
const Cell* x = Array[FlipFlop];
Cell* y = Array[!FlipFlop];
for( int i=r.begin(); i!=j; ++i )
y[i] = x[i]^x[i+1];
}
};
void DoAllTimeSteps( int m ) {
affinity_partitioner ap;
for( int k=0; k<m; ++k ) {
parallel_for( blocked_range<int>(0,N-1),
TimeStepOverSubrange(),
ap );
FlipFlop ^= 1;
}
}
各時間ステップでは、オートマトンの古い状態が Array[FlipFlop] から読み取られ、新しい状態が Array[!FlipFlop] に書き込まれます。そして、FlipFlop は新しい状態を古い状態にします。両方の状態を合わせたサイズは約 2MB で、これはほとんどの近年のプロセッサーのキャッシュに収まります。このサンプルは、auto_partitioner を使用する場合と比較して、8 コアのマシンで 50% ~ 200% の向上が計測されています。
affinity_partitioner は、ループの反復間を通して有効でなければなりません。そのため、このサンプルでは、すべての反復を実行するループの外側で宣言しています。別の方法として、affinity partitioner をファイルスコープで宣言することもできます。その場合、DoAllTimeSteps が同時に呼び出されない限り、動作します。affinity_partitioner の同じインスタンスを、同時に呼び出される並列アルゴリズム・テンプレートに渡すべきではありません。代わりに、別々のインスタンスを使用します。
| メンバー | 説明 |
|---|---|
| affinity_partitioner() |
affinity_partitioner を構築します。 |
| ~affinity_partitioner() |
この affinity_partitioner を破棄します。 |