2013-07-06 3 views
5

내 프로그램 코드에는 바이트 또는 2 ~ 16 자 정도의 매우 작은 다양한 객체가 있습니다. Vector2 (2 * T), Vector3 (3 * T), Vector4 (4 * T), ColourI32 (4), LightValue16 (2), Tile (2) 등 (괄호 안의 바이트 크기)C++ Small Object Performance

예상보다 느린 기능을하는 프로파일 링 (샘플 기반)을 수행하고있었습니다.

//4 bits per channel natural light and artificial RGB 
class LightValue16 
{ 
... 
    explicit LightValue16(uint16_t value); 
    LightValue16(const LightValueF &); 
    LightValue16(int r, int g, int b, int natural); 

    int natural()const; 
    void natural(int v); 
    int artificialRed()const; 
    ... 
    uint16_t data; 
}; 
... 
LightValue16 World::getLight(const Vector3I &pos) 
{ ... } 

이 기능은 세계의 인구 부분 위의 몇 가지 기본 값으로, 배열의 몇 가지를 통해 값을 조회하는 몇 가지 수학을한다. 내용물은 멋지게 줄 지어 있으며 해체를 보는 것은 대략 100 가지의 지시 사항으로 얻을 수있는만큼 좋은 것으로 보입니다.

mov eax, dword pyt [ebp + 8] 
mov cx, word ptr[ecx + edx * 2] ; or say mov ecx, Fh 
mov word ptr [eax], cx 
pop ebp 
ret 10h 

가 64 동안 나는 거의 같은 일을 보았다 그러나 한 점은 같은 구현 된 모든 반환 사이트에 띄었다. 내 GCC 빌드를 확인하지는 않았지만 같은 일을한다.

나는 약간의 실험을했으며 uint16_t 반환 유형을 사용하여 발견했습니다. 결과적으로 World :: getLight 함수가 인라인되어 (결과적으로 동일한 코어 80 인스트럭션 또는 조건부/루프가 다른 것 같이 보이지 않게 보임) 외부 함수에 대한 총 CPU 사용량이 16.87 %에서 14.04 % 내가 할 수있는 일은 케이스베이스별로 (내가 생각하는 인라인 인스트루먼트를 시도하는 것과 함께) 그런 성능 문제를 피하기위한 실용적인 방법이 있는가? 전체 코드에서 몇 % 더 빨라질 수도 있습니까?

제가 생각할 수있는 가장 좋은 점은이 경우 (< 4 또는 아마도 8 바이트 객체)의 기본 유형을 사용하고 현재 멤버를 비 멤버 함수로 옮기는 것입니다. C에서와 같이 , 네임 스페이스 만 있으면됩니다.

이것에 대해 생각해보십시오. "t foo (float x, float y, float z)"를 통해 "t foo (const Vector3F &p)"와 같은 물건을 만드는 데 드는 비용이 종종 있습니다. 그렇다면 const &을 광범위하게 사용하는 프로그램에서 상당한 차이가 날 수 있습니까?

+0

: 당신이 명시 적, 복사 생성자를 원하는 상태로 원하는 당신이 C++ (11)를 사용하는 경우, 당신은 또한이 아닌 사소한하지 않습니다 채무 불이행 기능로 내려 쓸 수 있습니다 , 명시된 경우의 차이점은 관련된 모든 오버 헤드와 16 비트 부호없는 int를 반환하는 개체를 반환한다는 것입니다. 이전 버전에서는 int가 아닌 전체 객체를 복사해야했기 때문에 RVO가 작동하더라도 CPU 시간을 약간 더 소비해야합니다. –

+0

힙이 아닌 스택에 객체를 할당하면이 상황에서 성능에 영향을 줍니까? –

+0

Timo : 전체 개체가 왜 2 바이트 이상의 메모리를 차지합니까? 컴파일러는 vtable을 거기에 넣으면 안된다고 생각합니다. –

답변

0

이 질문에 대한 의견에서 컴파일러가 분석 한 기능에 대해 class LightValue16을 단순한 uint16_t으로 처리 할 수 ​​있는지 여부는 이미 많은 논의가있었습니다.

클래스는 (가상 함수와 같은) 특별한 마법을 포함하지 않는 경우 전체 클래스는 분석 함수를 볼 수 있습니다, 다음 단지`uint16_t 유형을 사용하여 100 % 동일하게 효율적으로 컴파일러 생산 코드입니다.

문제는 "할 수 있습니다."입니다. 괜찮은 컴파일러는 일반적으로 100 % 빠른 코드를 생성하지만, 일부 최적화가 적용되지 않거나 결과 코드가 다를 수있는 산발적 인 상황이 발생할 수 있습니다. 일부의 최적화 단계에서 조금 더 많은 코드가 클래스로 남아 있기 때문에 인라인이 적용되지 않습니다. 또는 일부 최적화 단계에서 실제로이 단계에서 일반 숫자 유형이 필요합니다. 이것은 컴파일러의 실제 버그가 아닙니다. 예를 들어, "템플릿 < bool NotUsed>"를 클래스에 추가하면 의미 적으로 프로그램이 변경되지 않더라도 컴파일러 내의 최적화 단계가 변경 될 수 있습니다.

100 % 확신하려면 int 또는 double을 직접 사용하십시오.그러나 90 %의 시간은 100 % 빠르며 10 %만이 성능의 90 %가 될 것이며 O.K이어야합니다. 모든 유스 케이스의 99 % (그러나 100 %는 아님).

2

Itanium C++ ABI을 살펴보십시오. 컴퓨터에 Itanium 프로세서가 전혀없는 반면 gcc는 x86 및 x86-64 ABI를 Itanium ABI와 매우 유사하게 모델링합니다. 링크 된 부분은 반환 값 유형이 아닌 사소한 복사 생성자 또는 소멸자가있는 경우

그러나, [발생 호출자가 제공하는 메모리로 반환]

이 아닌 사소한 있는지 확인하려면한다고 복사 생성자 또는 소멸자의 의미는 What are Aggregates and PODs and how/why are they special?을 살펴보고 클래스에 대한 규칙을 "쉽게 복사 할 수 있음"을 살펴 봅니다. 귀하의 경우 문제는 정의한 복사 생성자입니다. 그것은 전혀 필요하지 않아야합니다. 컴파일러는 필요에 따라 data 멤버를 할당하는 복사 생성자를 합성합니다. 잘

LigthValue16(const LightValue16 & other) = default;