부호없는 정수와 부호있는 정수의 성능 차이는 무엇입니까? [닫은]


42

부호있는 정수와 부동 소수점을 혼합 할 때의 성능 저하를 알고 있습니다.

부호없는 정수와 부동 소수점을 혼합하는 것이 더 나쁜가요?

플로트없이 부호있는 / 부호없는 믹싱 할 때 히트가 있습니까?

다른 크기 (u32, u16, u8, i32, i16, i8)가 성능에 영향을 미칩니 까? 어떤 플랫폼에서?


2
PS3 특정 텍스트 / 태그를 제거했습니다.이 아키텍처에 대한 좋은 질문이기 때문에 정수와 부동 소수점 레지스터를 분리하는 모든 아키텍처에 대해 정답을 얻습니다. 실제로 모든 것입니다.

답변:


36

int (모든 종류의)와 float를 혼합하면 큰 페널티는 서로 다른 레지스터 세트에 있기 때문입니다. 한 레지스터 세트에서 다른 레지스터 세트로 이동하려면 값을 메모리에 쓰고 다시 읽어야하므로 로드 적중 저장소 스톨이 발생합니다.

다른 크기 또는 int의 부호 사이를 이동하면 모든 레지스터가 동일한 레지스터 세트에 유지되므로 큰 벌칙을 피할 수 있습니다. 부호 확장 등으로 인해 더 적은 벌금이 부과 될 수 있지만, 이는로드 적중 상점보다 훨씬 작습니다.


링크 한 기사에 따르면 PS3 셀 프로세서는 예외로되어 있습니다. 모든 것이 동일한 레지스터 세트에 저장되어 있기 때문입니다 (기사 중간에 찾거나 "셀"로 검색 할 수 있음).
bummzack

4
@bummzack : PPE가 아닌 SPE에만 적용됩니다. SPE는 매우 특수한 부동 소수점 환경을 가지고 있으며 캐스트는 여전히 상대적으로 비쌉니다. 또한 부호있는 정수와 부호없는 정수의 비용은 여전히 ​​동일합니다.

그것은 좋은 기사이며 LHS에 대해 아는 것이 중요합니다 (그리고 그것을 위해 투표하고 있습니다). 그러나 나의 질문은 그 사인 관련 처벌에 관한 것입니다. 나는 이것들이 작고 무시할 만하다는 것을 알고 있지만, 여전히 그들에 대한 실제 숫자 나 참조를보고 싶습니다.
Luis

1
@Luis-이것에 대한 공개 문서를 찾으려고했지만 현재 찾을 수 없습니다. Xbox360 설명서에 액세스 할 수있는 경우 Bruce Dawson이 제공하는 유용한 백서가 있습니다.
celion

@Luis : 아래에 분석을 게시했지만 만족스러운 경우 celion에게 답변을주십시오-그가 말한 모든 것이 정확하면 내가 한 일은 GCC를 몇 번 실행하는 것입니다.

12

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 코드를 최적화하는 데 많은 시간을 보낸 사람이라면 아마도로드 히트 상점에 대한 공포 이야기가 적어도 하나있을 것입니다. 그러나이 경우에는 문제가되지 않습니다. 정수 유형의 스토리지 크기는 정렬되고 레지스터에 맞는 한 성능에 영향을 미치지 않습니다.


2
(CW는 실제로 celion의 답변에 대한 의견 일 뿐이며 사람들이 코드를 더 설명하기 위해 어떤 코드 변경을해야하는지 궁금하기 때문입니다.)

PS3 CPU에 대한 정보는 쉽고 합법적으로 이용할 수 있으므로 PS3과 관련된 CPU에 대한 논의는 문제가되지 않습니다. Sony가 OtherOS 지원을 제거 할 때까지 누구나 PS3에서 Linux를 사용하고 프로그래밍 할 수있었습니다. GPU가 한계를 벗어 났지만 CPU (SPE 포함)는 정상입니다. OtherOS 지원이 없어도 적절한 GCC를 쉽게 잡고 코드 생성기가 어떤지 확인할 수 있습니다.
JasonD

@ 제이슨 : 내 게시물을 CW로 표시 했으므로 누군가이 작업을 수행하면 정보를 제공 할 수 있습니다. 그러나 소니의 공식 GameOS 컴파일러 (실제로 유일하게 중요한 것)에 액세스 할 수있는 사람은 그렇게 할 수 없습니다.

실제로 부호있는 정수는 PPC IIRC에서 더 비쌉니다. 성능에 약간의 영향이 있지만 거기에 있습니다 ... 또한 많은 PS3 PPU / SPU 세부 정보가 있습니다 : jheriko-rtw.blogspot.co.uk/2011/07/ps3-ppuspu-docs.html 및 여기 : jheriko-rtw.blogspot.co.uk/2011/03/ppc-instruction-set.html . 이 GameOS 컴파일러가 무엇인지 궁금하십니까? 이것이 GCC compier입니까 아니면 SNC입니까? 이미 언급 된 것 이외의 iirc는 가장 안쪽의 루프를 최적화 할 때 오버 헤드가 있습니다. 나는 이것을 설명하는 문서에 접근 할 수 없다-그리고 비록 내가하더라도 ...
jheriko

4

부호있는 정수 연산은 거의 모든 아키텍처에서 더 비쌀 수 있습니다. 예를 들어, 부호없는 경우 상수로 나누기가 더 빠릅니다. 예 :

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)이없는 경우 서명되지 않은 비 정렬 분할도 더 빠릅니다.

일반적으로 컴파일러에 음수를 지정할 수 없으면 표현식, 특히 루프 종료 및 기타 조건에 사용되는 표현식을 최적화하는 데 도움이됩니다.

다른 크기의 정수에 관해서는 그렇습니다. 약간의 영향이 있지만 메모리를 덜 옮기는 것과 무게를 비교해야합니다. 요즘에는 확장 할 때보 다 적은 메모리에 액세스하여 더 많은 것을 얻을 수 있습니다. 당신은 그 시점에서 마이크로 최적화에 먼 거리에 있습니다.


나는 -O0에서도 GCC가 실제로 생성하는 것을 더 반영하도록 최적화 된 코드를 편집했습니다. test + lea가 분기없이 수행 할 수있을 때 분기가있는 것은 잘못된 것입니다.

2
x86에서는 아마도 ARMv7에서는 조건부로만 실행됩니다.
John Ripley

3

부호있는 또는 부호없는 int를 사용하는 작업은 현재 프로세서 (x86_64, x86, powerpc, arm)에서 동일한 비용을가집니다. 32 비트 프로세서에서 u32, u16, u8 s32, s16, s8은 같아야합니다. 잘못된 정렬로 페널티를받을 수 있습니다.

그러나 int를 float로 변환하거나 float를 int로 변환하는 것은 비용이 많이 드는 작업입니다. 최적화 된 구현 (SSE2, Neon ...)을 쉽게 찾을 수 있습니다.

가장 중요한 점은 아마도 메모리 액세스 일 것입니다. 데이터가 L1 / L2 캐시에 맞지 않으면 변환보다 더 많은주기가 느슨해집니다.


2

존 퍼디 (Jon Purdy)는 위에서 언급 할 수 없다고 말했다. 서명되지 않은 산술은 단어의 비트 수에 대한 간단한 모수 산술 모듈로 2입니다. 서명 된 작업은 원칙적으로 오버플로가 발생할 수 있지만 일반적으로 해제되어 있습니다.

때로는 두 개 이상의 데이터 항목을 int로 묶는 것과 같이 영리한 (그러나 읽기 쉬운 것은 아니지만) 명령 당 여러 작업 (포켓 산술)을 얻을 수 있습니다. 그러나 당신은 무엇을하고 있는지 이해해야합니다. 물론 MMX를 사용하면 자연스럽게이 작업을 수행 할 수 있습니다. 그러나 때로는 가장 큰 HW 지원 단어 크기를 사용하고 수동으로 데이터를 패킹하면 가장 빠른 구현이 가능합니다.

데이터 정렬에주의하십시오. 대부분의 HW 구현에서 정렬되지 않은로드 및 저장은 느립니다. 자연스러운 정렬은 4 바이트 워드의 경우 주소가 4의 배수이고 8 바이트 워드 주소가 8 바이트의 배수 여야 함을 의미합니다. 이것은 SSE로 넘어갑니다 (128 비트는 16 바이트 정렬을 선호합니다). AVX는 곧 이러한 "벡터"레지스터 크기를 256 비트에서 512 비트로 확장합니다. 정렬 된로드 / 스토어는 정렬되지 않은로드 / 스토어보다 빠릅니다. HW 괴짜에게는 정렬되지 않은 메모리 작업이 캐시 라인 및 페이지 경계와 같은 범위에 걸쳐있을 수 있으며 HW 가주의해야합니다.


1

C에서는 부호있는 오버플로가 정의되지 않기 때문에 루프 인덱스에 부호있는 정수를 사용하는 것이 약간 좋습니다. 따라서 컴파일러는 그러한 루프의 코너 수가 더 적다고 가정합니다. 이것은 gcc의 "-fstrict-overflow"(기본적으로 활성화되어 있음)에 의해 제어되며 어셈블리 출력을 읽지 않으면 그 효과를 알아 채기가 어려울 수 있습니다.

그 외에도 x86은 메모리 피연산자를 사용할 수 있기 때문에 유형을 혼합하지 않으면 더 잘 작동합니다. 유형을 변환해야하는 경우 (확장자 또는 0 확장) 명시적인로드 및 레지스터 사용을 의미합니다.

지역 변수에 대해 int를 사용하면 대부분 기본적으로 발생합니다.


0

celion이 지적했듯이 int와 float 간 변환 오버 헤드는 레지스터 간의 값 복사 및 변환과 관련이 있습니다. 서명되지 않은 int 자체의 유일한 오버 헤드는 보장 된 랩 어라운드 동작에서 비롯되므로 컴파일 된 코드에서 일정량의 오버플로 검사가 필요합니다.

부호있는 정수와 부호없는 정수를 변환 할 때 기본적으로 오버 헤드는 없습니다. 플랫폼에 따라 다른 크기의 정수 (무한하게) 액세스하는 속도가 빠르거나 느려질 수 있습니다. 일반적으로, 플랫폼의 워드 크기에 가장 가까운의 정수의 크기에 가장 빠른 것입니다 액세스, 하지만 전반적인 성능 차이는 특히 캐시 크기, 다른 많은 요인에 따라 달라집니다 당신이 사용하는 경우 uint64_t당신이 필요로하는 모든 일 때 uint32_t, 그것은 할 수있다 적은 양의 데이터가 한 번에 캐시에 들어가기 때문에로드 오버 헤드가 발생할 수 있습니다.

그래도 이것에 대해 생각하는 것은 약간 과도합니다. 데이터에 적합한 유형을 사용하면 완벽하게 작동해야하며 아키텍처를 기반으로 유형을 선택하여 얻을 수있는 힘의 양은 무시할 수 있습니다.


어떤 오버 플로우 검사를 언급하고 있습니까? 어셈블러보다 낮은 수준을 의미하지 않는 한 두 개의 정수를 추가하는 코드는 대부분의 시스템에서 동일하며 부호 크기를 사용하는 소수의 시스템에서는 더 이상 길지 않습니다. 달라요

@JoeWreschnig : 젠장. 나는 그것을 찾을 수없는 것 같지만 적어도 특정 플랫폼에서 정의 된 랩 어라운드 동작을 설명하는 다른 어셈블러 출력의 예를 보았습니다. 내가 찾을 수있는 유일한 관련 게시물 : stackoverflow.com/questions/4712315/...
존 퍼디

다른 랩 어라운드 동작에 대한 다른 어셈블러 출력은 컴파일러가 부호있는 경우에 최적화 할 수 있기 때문입니다. 예를 들어 b> 0이면 a + b> a 인 경우 부호있는 오버 플로우가 정의되지 않아서 신뢰할 수 없습니다. 실제로는 완전히 다른 상황입니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.