これに答えるには:
私の質問は、これらのケースでは一度に1つの連続した配列を直線的に反復していないので、この方法でコンポーネントを割り当てることによるパフォーマンスの向上をすぐに犠牲にするのですか?C ++で2つの異なる連続した配列を繰り返し、各サイクルで両方のデータを使用する場合、問題がありますか?
いいえ(少なくとも必ずしもそうではありません)。ほとんどの場合、キャッシュコントローラーは、複数の連続した配列からの読み取りを効率的に処理できる必要があります。重要な部分は、各配列に直線的にアクセスするために可能な限り試すことです。
これを実証するために、小さなベンチマークを作成しました(通常のベンチマークの注意事項が適用されます)。
単純なベクトル構造体から始めます。
struct float3 { float x, y, z; };
2つの別々の配列の各要素を合計し、結果を3番目に格納するループは、ソースデータが1つの配列にインターリーブされ、結果が3番目に格納されるバージョンとまったく同じように実行されることがわかりました。ただし、結果をソースとインターリーブすると、パフォーマンスが低下します(約2倍)。
データにランダムにアクセスすると、パフォーマンスは10〜20倍低下しました。
タイミング(10,000,000要素)
線形アクセス
- 個別の配列0.21s
- インターリーブされたソース0.21s
- インターリーブされたソースと結果0.48s
ランダムアクセス(random_shuffleのコメントを外す)
- 個別のアレイ2.42
- インターリーブされたソース4.43s
- インターリーブされたソースと結果4.00
ソース(Visual Studio 2013でコンパイル):
#include <Windows.h>
#include <vector>
#include <algorithm>
#include <iostream>
struct float3 { float x, y, z; };
float3 operator+( float3 const &a, float3 const &b )
{
return float3{ a.x + b.x, a.y + b.y, a.z + b.z };
}
struct Both { float3 a, b; };
struct All { float3 a, b, res; };
// A version without any indirection
void sum( float3 *a, float3 *b, float3 *res, int n )
{
for( int i = 0; i < n; ++i )
*res++ = *a++ + *b++;
}
void sum( float3 *a, float3 *b, float3 *res, int *index, int n )
{
for( int i = 0; i < n; ++i, ++index )
res[*index] = a[*index] + b[*index];
}
void sum( Both *both, float3 *res, int *index, int n )
{
for( int i = 0; i < n; ++i, ++index )
res[*index] = both[*index].a + both[*index].b;
}
void sum( All *all, int *index, int n )
{
for( int i = 0; i < n; ++i, ++index )
all[*index].res = all[*index].a + all[*index].b;
}
class PerformanceTimer
{
public:
PerformanceTimer() { QueryPerformanceCounter( &start ); }
double time()
{
LARGE_INTEGER now, freq;
QueryPerformanceCounter( &now );
QueryPerformanceFrequency( &freq );
return double( now.QuadPart - start.QuadPart ) / double( freq.QuadPart );
}
private:
LARGE_INTEGER start;
};
int main( int argc, char* argv[] )
{
const int count = 10000000;
std::vector< float3 > a( count, float3{ 1.f, 2.f, 3.f } );
std::vector< float3 > b( count, float3{ 1.f, 2.f, 3.f } );
std::vector< float3 > res( count );
std::vector< All > all( count, All{ { 1.f, 2.f, 3.f }, { 1.f, 2.f, 3.f }, { 1.f, 2.f, 3.f } } );
std::vector< Both > both( count, Both{ { 1.f, 2.f, 3.f }, { 1.f, 2.f, 3.f } } );
std::vector< int > index( count );
int n = 0;
std::generate( index.begin(), index.end(), [&]{ return n++; } );
//std::random_shuffle( index.begin(), index.end() );
PerformanceTimer timer;
// uncomment version to test
//sum( &a[0], &b[0], &res[0], &index[0], count );
//sum( &both[0], &res[0], &index[0], count );
//sum( &all[0], &index[0], count );
std::cout << timer.time();
return 0;
}