이것에 대답하려면 :
내 질문은,이 경우 한 번에 하나의 연속 배열을 선형으로 반복하지 않기 때문에이 방법으로 구성 요소를 할당하여 성능 이점을 즉시 희생하고 있습니까? C ++에서 두 개의 서로 다른 연속 배열을 반복하고 각주기마다 두 데이터를 모두 사용할 때 문제가 있습니까?
아니오 (적어도 반드시 그런 것은 아닙니다). 대부분의 경우 캐시 컨트롤러는 하나 이상의 연속 어레이에서 효율적으로 읽기를 처리 할 수 있어야합니다. 중요한 부분은 가능하면 각 어레이에 선형으로 액세스하는 것입니다.
이를 입증하기 위해 작은 벤치 마크를 작성했습니다 (일반적인 벤치 마크 경고가 적용됨).
간단한 벡터 구조체로 시작 :
struct float3 { float x, y, z; };
두 개의 개별 배열의 각 요소를 합산하고 결과를 세 번째로 저장하는 루프는 소스 데이터가 단일 배열로 인터리브되고 결과가 세 번째로 저장된 버전과 정확히 동일하게 수행됩니다. 그러나 소스와 결과를 인터리브하면 성능이 약 2 배 떨어졌습니다.
데이터에 무작위로 액세스하면 성능이 10에서 20 사이로 떨어졌습니다.
타이밍 (10,000,000 요소)
선형 액세스
- 별도의 배열 0.21s
- 인터리브 된 소스 0.21s
- 인터리브 된 소스 및 결과 0.48 초
랜덤 액세스 (uncomment random_shuffle)
- 별도의 어레이
- 인터리브 된 소스 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;
}