부호있는 정수와 부동 소수점을 혼합 할 때의 성능 저하를 알고 있습니다.
부호없는 정수와 부동 소수점을 혼합하는 것이 더 나쁜가요?
플로트없이 부호있는 / 부호없는 믹싱 할 때 히트가 있습니까?
다른 크기 (u32, u16, u8, i32, i16, i8)가 성능에 영향을 미칩니 까? 어떤 플랫폼에서?
답변
int (모든 종류의)와 float를 혼합하면 큰 페널티는 서로 다른 레지스터 세트에 있기 때문입니다. 한 레지스터 세트에서 다른 레지스터 세트로 이동하려면 값을 메모리에 쓰고 다시 읽어야하므로 로드 적중 저장소 스톨이 발생합니다.
다른 크기 또는 int의 부호 사이를 이동하면 모든 레지스터가 동일한 레지스터 세트에 유지되므로 큰 벌칙을 피할 수 있습니다. 부호 확장 등으로 인해 더 적은 벌금이 부과 될 수 있지만, 이는로드 적중 상점보다 훨씬 작습니다.
답변
Xbox 360 및 PS3에 대한 정보는 특히 하위 수준의 세부 정보와 같이 라이선스가 허여 된 개발자 전용 벽 뒤에있을 것으로 생각됩니다. 그러나 동등한 x86 프로그램을 구성하고 분해하여 일반적인 아이디어를 얻을 수 있습니다.
먼저 서명되지 않은 확장 비용이 무엇인지 살펴 보겠습니다.
unsigned char x = 1;
unsigned int y = 1;
unsigned int z;
z = x;
z = y;
관련 부분은 다음과 같이 분해됩니다 (GCC 4.4.5 사용).
z = x;
27: 0f b6 45 ff movzbl -0x1(%ebp),%eax
2b: 89 45 f4 mov %eax,-0xc(%ebp)
z = y;
2e: 8b 45 f8 mov -0x8(%ebp),%eax
31: 89 45 f4 mov %eax,-0xc(%ebp)
따라서 기본적으로 동일합니다. 한 경우에는 바이트를 이동하고 다른 경우에는 단어를 이동합니다. 다음:
signed char x = 1;
signed int y = 1;
signed int z;
z = x;
z = y;
로 바뀝니다 :
z = x;
11: 0f be 45 ff movsbl -0x1(%ebp),%eax
15: 89 45 f4 mov %eax,-0xc(%ebp)
z = y;
18: 8b 45 f8 mov -0x8(%ebp),%eax
1b: 89 45 f4 mov %eax,-0xc(%ebp)
따라서 부호 확장 비용은 하위 명령 수준이 movsbl
아닌 비용입니다 movzbl
. 현대식 프로세서의 작동 방식으로 인해 현대식 프로세서에서 정량화하는 것은 기본적으로 불가능합니다. 메모리 속도에서 캐싱, 파이프 라인에 있던 것에 이르기까지 모든 것이 런타임을 지배하게 될 것입니다.
~ 10 분 만에 이러한 테스트를 작성하는 데 걸리는 시간에 실제 성능 버그를 쉽게 발견 할 수 있었으며 컴파일러 최적화 수준을 설정하자마자 이러한 간단한 작업에서 코드를 인식 할 수 없게되었습니다.
이것은 스택 오버플로가 아니므로 여기서는 아무도 마이크로 최적화가 중요하지 않다고 주장하지 않기를 바랍니다. 게임은 종종 매우 크고 숫자가 큰 데이터에서 작동하므로 분기, 캐스트, 일정, 구조 정렬 등에주의를 기울이면 매우 중요한 개선이 이루어질 수 있습니다. PPC 코드를 최적화하는 데 많은 시간을 보낸 사람이라면 아마도로드 히트 상점에 대한 공포 이야기가 적어도 하나있을 것입니다. 그러나이 경우에는 문제가되지 않습니다. 정수 유형의 스토리지 크기는 정렬되고 레지스터에 맞는 한 성능에 영향을 미치지 않습니다.
답변
부호있는 정수 연산은 거의 모든 아키텍처에서 더 비쌀 수 있습니다. 예를 들어, 부호없는 경우 상수로 나누기가 더 빠릅니다. 예 :
unsigned foo(unsigned a) { return a / 1024U; }
다음에 최적화 될 것입니다.
unsigned foo(unsigned a) { return a >> 10; }
그러나…
int foo(int a) { return a / 1024; }
다음에 최적화합니다.
int foo(int a) {
return (a + 1023 * (a < 0)) >> 10;
}
또는 분기가 저렴한 시스템에서
int foo(int a) {
if (a >= 0) return a >> 10;
else return (a + 1023) >> 10;
}
모듈로도 마찬가지입니다. 2의 제곱이 아닌 경우에도 마찬가지입니다 (그러나 예제는 더 복잡합니다). 아키텍처에 하드웨어 분할 (예 : 대부분의 ARM)이없는 경우 서명되지 않은 비 정렬 분할도 더 빠릅니다.
일반적으로 컴파일러에 음수를 지정할 수 없으면 표현식, 특히 루프 종료 및 기타 조건에 사용되는 표현식을 최적화하는 데 도움이됩니다.
다른 크기의 정수에 관해서는 그렇습니다. 약간의 영향이 있지만 메모리를 덜 옮기는 것과 무게를 비교해야합니다. 요즘에는 확장 할 때보 다 적은 메모리에 액세스하여 더 많은 것을 얻을 수 있습니다. 당신은 그 시점에서 마이크로 최적화에 먼 거리에 있습니다.
답변
부호있는 또는 부호없는 int를 사용하는 작업은 현재 프로세서 (x86_64, x86, powerpc, arm)에서 동일한 비용을가집니다. 32 비트 프로세서에서 u32, u16, u8 s32, s16, s8은 같아야합니다. 잘못된 정렬로 페널티를받을 수 있습니다.
그러나 int를 float로 변환하거나 float를 int로 변환하는 것은 비용이 많이 드는 작업입니다. 최적화 된 구현 (SSE2, Neon …)을 쉽게 찾을 수 있습니다.
가장 중요한 점은 아마도 메모리 액세스 일 것입니다. 데이터가 L1 / L2 캐시에 맞지 않으면 변환보다 더 많은주기가 느슨해집니다.
답변
존 퍼디 (Jon Purdy)는 위에서 언급 할 수 없다고 말했다. 서명되지 않은 산술은 단어의 비트 수에 대한 간단한 모수 산술 모듈로 2입니다. 서명 된 작업은 원칙적으로 오버플로가 발생할 수 있지만 일반적으로 해제되어 있습니다.
때로는 두 개 이상의 데이터 항목을 int로 묶는 것과 같이 영리한 (그러나 읽기 쉬운 것은 아니지만) 명령 당 여러 작업 (포켓 산술)을 얻을 수 있습니다. 그러나 당신은 무엇을하고 있는지 이해해야합니다. 물론 MMX를 사용하면 자연스럽게이 작업을 수행 할 수 있습니다. 그러나 때로는 가장 큰 HW 지원 단어 크기를 사용하고 수동으로 데이터를 패킹하면 가장 빠른 구현이 가능합니다.
데이터 정렬에주의하십시오. 대부분의 HW 구현에서 정렬되지 않은로드 및 저장은 느립니다. 자연스러운 정렬은 4 바이트 워드의 경우 주소가 4의 배수이고 8 바이트 워드 주소가 8 바이트의 배수 여야 함을 의미합니다. 이것은 SSE로 넘어갑니다 (128 비트는 16 바이트 정렬을 선호합니다). AVX는 곧 이러한 “벡터”레지스터 크기를 256 비트에서 512 비트로 확장합니다. 정렬 된로드 / 스토어는 정렬되지 않은로드 / 스토어보다 빠릅니다. HW 괴짜에게는 정렬되지 않은 메모리 작업이 캐시 라인 및 페이지 경계와 같은 범위에 걸쳐있을 수 있으며 HW 가주의해야합니다.
답변
C에서는 부호있는 오버플로가 정의되지 않기 때문에 루프 인덱스에 부호있는 정수를 사용하는 것이 약간 좋습니다. 따라서 컴파일러는 그러한 루프의 코너 수가 더 적다고 가정합니다. 이것은 gcc의 “-fstrict-overflow”(기본적으로 활성화되어 있음)에 의해 제어되며 어셈블리 출력을 읽지 않으면 그 효과를 알아 채기가 어려울 수 있습니다.
그 외에도 x86은 메모리 피연산자를 사용할 수 있기 때문에 유형을 혼합하지 않으면 더 잘 작동합니다. 유형을 변환해야하는 경우 (확장자 또는 0 확장) 명시적인로드 및 레지스터 사용을 의미합니다.
지역 변수에 대해 int를 사용하면 대부분 기본적으로 발생합니다.
답변
celion이 지적했듯이 int와 float 간 변환 오버 헤드는 레지스터 간의 값 복사 및 변환과 관련이 있습니다. 서명되지 않은 int 자체의 유일한 오버 헤드는 보장 된 랩 어라운드 동작에서 비롯되므로 컴파일 된 코드에서 일정량의 오버플로 검사가 필요합니다.
부호있는 정수와 부호없는 정수를 변환 할 때 기본적으로 오버 헤드는 없습니다. 플랫폼에 따라 다른 크기의 정수 가 (무한하게) 액세스하는 속도가 빠르거나 느려질 수 있습니다. 일반적으로, 플랫폼의 워드 크기에 가장 가까운의 정수의 크기에 가장 빠른 것입니다 액세스, 하지만 전반적인 성능 차이는 특히 캐시 크기, 다른 많은 요인에 따라 달라집니다 당신이 사용하는 경우 uint64_t
당신이 필요로하는 모든 일 때 uint32_t
, 그것은 할 수있다 적은 양의 데이터가 한 번에 캐시에 들어가기 때문에로드 오버 헤드가 발생할 수 있습니다.
그래도 이것에 대해 생각하는 것은 약간 과도합니다. 데이터에 적합한 유형을 사용하면 완벽하게 작동해야하며 아키텍처를 기반으로 유형을 선택하여 얻을 수있는 힘의 양은 무시할 수 있습니다.