2011-10-26 5 views
2

아래 함수는 32 비트 부동 소수점 값의 절대 값을 계산합니다.C++에서 함수에 전달 된 형식 매개 변수로 공용체를 만들 수 있습니까?

__forceinline static float Abs(float x) 
{ 
    union { 
     float x; 
     int a; 
    } u; 
    //u.x = x; 
    u.a &= 0x7FFFFFFF; 
    return u.x; 
} 
함수에서 선언 된

union u는 함수에서 매개 변수로 전달 된 x와 다른 변수 x를 보유합니다. 함수 -x에 대한 인수로 노동 조합을 만들 수있는 방법이 있습니까?

위의 함수에서 주석 처리되지 않은 행이 이보다 더 오래 실행되는 이유는 무엇입니까?

__forceinline float fastAbs(float a) 
{ 
    int b= *((int *)&a) & 0x7FFFFFFF; 
    return *((float *)(&b)); 
} 

가능한 한 메모리에 대한 읽기/쓰기의 수가 적기 때문에 부동 소수점 값의 Abs를 가져 오는 가장 좋은 방법을 찾아 내려고합니다.

+2

버전에 비해'std :: abs' 시간은 얼마나 걸립니까? – avakar

+3

btw ... 왜 std :: fabs를 사용하지 않습니까? 그 이유는 위의 작업을 수행하는 것보다 훨씬 빠를 절대 값 (인텔 아키텍처에서는 FABS 임)을 계산하기 위해 적절한 CPU 명령어 세트를 사용할 것이기 때문입니다. –

+0

Ahmed Masud : 이것에 대해 확실합니까? 나는 그것을 지금 시험 할 것이다. ... 흠, 이유를 완전히 이해하지는 못했지만 간단한 fabs 기능이 가장 빠른 것으로 나왔습니다. 팹 - 0.922311, fastAbs (위에서) - 0.935108, Abs (또한 위에서) - 0.937011, 복근 - 0.936235. 이 부분이 궁금합니다. – Vadim

답변

2

릴리스 모드에서 컴파일 된 코드의 디스 어셈블리를 보면 그 차이가 분명합니다! 인라인을 제거하고 두 개의 가상 함수를 사용하여 컴파일러가 너무 많이 최적화하지 못하게하고 차이점을 보여줍니다.

이것은 첫 번째 기능입니다.

013D1002 in   al,dx 
      union { 
       float x; 
       int a; 
      } u; 
      u.x = x; 
013D1003 fld   dword ptr [x] // Loads a float on top of the FPU STACK. 
013D1006 fstp  dword ptr [x] // Pops a Float Number from the top of the FPU Stack into the destination address. 
      u.a &= 0x7FFFFFFF; 
013D1009 and   dword ptr [x],7FFFFFFFh // Execute a 32 bit binary and operation with the specified address. 
      return u.x; 
013D1010 fld   dword ptr [x] // Loads the result on top of the FPU stack. 
     } 

이것은 두 번째 기능입니다.

013D1020 push  ebp      // Standard function entry... i'm using a virtual function here to show the difference. 
013D1021 mov   ebp,esp 
      int b= *((int *)&a) & 0x7FFFFFFF; 
013D1023 mov   eax,dword ptr [a]   // Load into eax our parameter. 
013D1026 and   eax,7FFFFFFFh    // Execute 32 bit binary and between our register and our constant. 
013D102B mov   dword ptr [a],eax   // Move the register value into our destination variable 
      return *((float *)(&b)); 
013D102E fld   dword ptr [a]    // Loads the result on top of the FPU stack. 

첫 번째 경우의 부동 소수점 연산 수와 FPU 스택 사용량이 더 큽니다. 함수는 여러분이 물어 본 것과 똑같이 실행되기 때문에 놀랄 일이 아닙니다. 그래서 두 번째 기능이 더 빨라질 것으로 기대합니다.

지금 ...가상 및 인라인 것들을 제거하는 것은 조금 다르다. 왜냐하면 컴파일러가 좋은 일을하기 때문에 여기서는 디스 어셈블리 코드를 작성하기가 어렵지만, 값이 상수가 아니라면 반복한다. 컴파일러는 처음에 더 많은 부동 소수점 연산을 사용할 것이다. 기능. 물론 정수 연산은 부동 소수점 연산보다 빠릅니다.

math.h abs 함수를 직접 사용하는 것이 메서드보다 느린 것이 확실합니까? 올바르게 인라인 된 경우 abs 함수가이 작업을 수행합니다! 이 같은

00D71016 fabs 

마이크로 최적화는 긴 코드에 표시하기 어려운,하지만 기능은 부동 소수점 연산의 긴 사슬에서 호출되는 경우 값은 FPU 스택 또는 SSE 이미되기 때문에, 팹 더 나은 작동합니다 레지스터! abs는 컴파일러가 더 빠르고 더 잘 최적화 할 수 있습니다.

코드에서 루프를 실행하는 최적화 성능을 측정 할 수 없으므로 컴파일러가 실제 코드에서 모두 어떻게 혼합되는지 확인해야합니다.

+1

필자는 fabs 기능을 테스트했으며 더 빨라졌습니다. 내가 가장 중요한 비트를 수동으로 설정하는 것이 팹보다 빠르다는 사실에 대해 필자는 멍청했다.하지만 사실이 아니었다. 이제 'fastAbs'기능을 제거하고 fab으로 대체합니다. 좋은 설명들 주셔서 감사합니다! – Vadim

+0

나는 또한 내 응용 프로그램에서 그것을 테스트하고 실제로 코드 runns 조금 빨리! 다행히 나는이 질문에 물었다. 일부 비범 한 것들을 정렬하는 것이 좋지 않기 때문이다. – Vadim

5

첫 번째 질문의 경우 왜 과제를 통해 원하는대로 할 수 없는지 잘 모르겠습니다. 컴파일러는 수행 할 수있는 최적화를 수행합니다.

두 번째 샘플 코드. 엄격한 앨리어싱을 위반했습니다. 그래서 그것은 동일하지 않습니다. 이 느린 이유에 관해서는

:

CPU를 오늘 별도의 정수 및 부동 소수점 단위를 갖는 경향이 때문입니다. 이와 같이 타입 - 펀칭을하면 값을 한 유닛에서 다른 유닛으로 강제 이동하게됩니다. 이것은 오버 헤드가 있습니다. (이 종종 메모리를 통해 수행, 그래서 당신은 여분의로드 및 저장이됩니다.)

두 번째 조각에서 :합니다 (x87의 FPU 나 SSE 레지스터 중 하나) 부동 소수점 유닛에 처음이다a, 요구 범용 레지스터로 이동하여 마스크 0x7FFFFFFF을 적용합니다. 그런 다음 다시 이동해야합니다.

첫 번째 스 니펫에서 : 컴파일러는 아마도 a을 정수 단위로 직접로드 할 정도로 똑똑합니다. 그래서 첫 단계에서 FPU를 건너 뜁니다.

(어셈블리를 표시 할 때까지는 100 % 확신 할 수 없습니다. 또한 레지스터 또는 스택에서 매개 변수가 시작되는지 여부에 크게 의존합니다. 그리고 출력이 다른 부동 소수점 작동).

+0

첫 번째 버전은 UB 뿐이며, 마지막으로 작성된 유니온 멤버 만 읽을 수 있습니다 (C++ 11에서 변경된 경우 제외). – avakar

+0

C99에서 알다시피, 당신은 노조를 통해 그것을 할 수 있습니다. 하지만 C++ 11에 대해서는 잘 모르겠습니다. – Mysticial