2012-07-23 6 views
13

나는 수업이 같이 있습니다구조 - 성능 차이

//Array of Structures 
class Unit 
{ 
    public: 
    float v; 
    float u; 
    //And similarly many other variables of float type, upto 10-12 of them. 
    void update() 
    { 
     v+=u; 
     v=v*i*t; 
     //And many other equations 
    } 
}; 

I 단위 유형의 객체의 배열을 만들 수 있습니다. 그리고 그들에 대한 업데이트를 호출하십시오.

int NUM_UNITS = 10000; 
void ProcessUpdate() 
{ 
    Unit *units = new Unit[NUM_UNITS]; 
    for(int i = 0; i < NUM_UNITS; i++) 
    { 
    units[i].update(); 
    } 
} 

일을 가속화하고 루프를 자동 조정하기 위해 AoS를 배열 구조로 변환했습니다.

//Structure of Arrays: 
class Unit 
{ 
    public: 
    Unit(int NUM_UNITS) 
    { 
    v = new float[NUM_UNITS]; 
    } 
    float *v; 
    float *u; 
    //Mnay other variables 
    void update() 
    { 
    for(int i = 0; i < NUM_UNITS; i++) 
    { 
     v[i]+=u[i]; 
     //Many other equations 
    } 
    } 
}; 

루프가 자동 수신에 실패하면 배열 구조가 매우 저하됩니다. 50 대는 SoA의 업데이트가 AoS보다 약간 빠릅니다.하지만 100 대 이상부터 SoA는 AoS보다 느립니다. 300 대에서 SoA는 거의 두 배 더 나 빠졌습니다. 100K 단위에서 SoA는 AoS보다 4 배 느립니다. 캐시가 SoA에 대한 문제 일 수는 있지만 성능 차이가 높을 것으로 기대하지 않았습니다. 캐시 그 린드 (cachegrind)에 대한 프로파일 링은 두 접근법 모두에서 유사한 누락 횟수를 보여줍니다. 단위 개체의 크기는 48 바이트입니다. L1 캐시는 256K, L2는 1MB, L3은 8MB입니다. 내가 여기서 무엇을 놓치고 있니? 이 문제는 실제로 캐시 문제입니까?

편집 : gcc 4.5.2를 사용 중입니다. 컴파일러 옵션은 -o3 -msse4 -ftree-vectorize입니다.

SoA에서 또 다른 실험을했습니다. 동적으로 배열을 할당하는 대신 컴파일 타임에 "v"와 "u"를 할당했습니다. 100K 단위가있을 때 동적으로 할당 된 배열을 사용하는 SoA보다 10 배 빠른 성능을 제공합니다. 여기서 뭐하는거야? 정적 메모리와 동적으로 할당 된 메모리간에 성능 차이가있는 이유는 무엇입니까?

+0

빌드 할 때 어떤 컴파일러 옵션을 사용합니까? –

+0

이것이 차이를 만들지는 모르겠지만 [std :: valarray] (http://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a00738.html)는) 도움. 그것은 전체 배열 (그런 식으로 깨끗한 구문)에 수학 연산을 수행하기 위해 설계되었습니다하지만 나는 implementers 그 작업을 최적화하고 가능하면 지능형 할당 등을하려고 특별한 과부하가 있다고 생각합니다. 그것은 전혀 도움이되지 않을지도 모르지만,보기 가치가 있을지도 모르다. – pstrjds

+1

벤치 마크를 실행하기 전에 데이터 집합을 0으로 만들면 어떻게됩니까? 초기화되지 않은 부동 소수점은 [비정규 화 된] 가능성이 높습니다 (http://stackoverflow.com/a/9314926/922184). 벤치 마크를 망치고 싶지는 않습니다. – Mysticial

답변

9

이 경우 배열의 구조는 캐시 친화적이지 않습니다.

uv을 모두 사용하지만 두 개의 서로 다른 배열의 경우 하나의 캐시 라인에 동시에로드되지 않으며 캐시 미스가 큰 성능 저하를 초래합니다.

_mm_prefetch을 사용하면 AoS 표현을 더욱 빠르게 만들 수 있습니다.

+0

'_mm_prefetch'에 해당하는 GCC/clang이 있습니까? –

+0

http://gcc.gnu.org/ml/gcc-patches/2008-01/msg01425.html –

+2

그러나 캐시 누락은 부분적으로 낭비되지 않습니다. 물건을 얻으려면 * 필요한 정보가 있어야합니다 (더 많은 '유' 's'이상), 왜 성능이 저하 될까요? – harold

1

프리 페치는 데이터가 표시되기를 기다리는 대부분의 실행 시간을 소비하는 코드에 중요합니다. 현대 프론트 사이드 버스는 프로그램이 현재로드 세트보다 너무 앞서지 않는다면 프리 페치가 안전해야하는 충분한 대역폭을 가지고 있습니다.

다양한 이유로 인해 구조 및 클래스가 C++에서 다양한 성능 문제를 일으킬 수 있으며 수용 가능한 수준의 성능을 얻으려면 더 많은 조정이 필요할 수 있습니다. 코드가 클 때는 객체 지향 프로그래밍을 사용하십시오. 데이터가 크고 성능이 중요하면 그렇게하지 마십시오.

float v[N]; 
float u[N]; 
    //And similarly many other variables of float type, up to 10-12 of them. 
//Either using an inlined function or just adding this text in main() 
     v[j] += u[j]; 
     v[j] = v[j] * i[j] * t[j]; 
+1

OOP가 AoS 사용과 충돌해서는 안됩니다. 스칼라 필드는 수학에서 객체로 간주되는 것처럼 OOP에서 객체로 간주 될 수 있지만 여러 스칼라 필드를 사용하여 공간 영역을 나타내는 경우 OOP와 일관된 방식으로 SoA를 사용합니다. 그것은 당신이 어떤 물건을 OOP에있는 것으로 인식 하느냐에 달려 있습니다. – 16807

0

확실하게 벡터화를 달성하지 않으면 SoA 변환을 할 동기가별로 없습니다.

사실상 __RESTRICT에 대한 사실 인정 외에도 gcc 4.9는 #pragma GCC ivdep을 사용하여 가정 앨리어싱 종속성을 손상 시켰습니다.

명시 적 프리 페치를 사용하는 것이 유용 할 경우 물론 SoA에 더 많은 프리 페치가 필요할 수 있습니다. 주요한 점은 페이지를 미리 가져 와서 DTLB 미스 해결을 가속화하는 것이므로 알고리즘이 더 많은 캐시를 필요로 할 수 있습니다.

당신의 OS에 대한 세부 사항을 포함하여 더 자세한 내용없이 "컴파일 시간"할당이라고 부르는 것에 대해서는 지적인 의견을 말할 수 없다고 생각합니다. 높은 수준에서 할당하고 할당을 다시 사용하는 전통이 중요하다는 데는 의심의 여지가 없습니다.