2008-09-10 13 views
6

더 빠른 코드 인 "ans = n * 3"또는 "ans = n + (n * 2)"로 컴파일됩니까?더 빠른 코드 "n * 3"또는 "n + (n * 2)"로 컴파일됩니까?

n은 int 또는 long 중 하나이며 최신 Win32 Intel 상자에서 실행되고 있다고 가정합니다.

일부 역 참조가 포함 된 경우, 즉 더 빠를 수 있습니까?

 

long a; 
long *pn; 
long  ans; 

... 
*pn = some_number; 
ans = *pn * 3; 

또는

 
ans = *pn+(*pn*2); 

또는, 최적화 컴파일러는 어떤 경우에도이 차지하는 가능성으로 걱정하지 않아도 뭔가 하나?

답변

55

일부 이국적인 컴파일러로 작업하지 않으면 IMO와 같은 미세 최적화가 필요하지 않습니다. 나는 처음에는 가독성을 두겠다.

1

실제로 사용하는 컴파일러에 따라 다르지만 실제로는 동일한 코드로 변환됩니다.

작은 테스트 프로그램을 만들고 디스 어셈블리를 확인하여 직접 확인할 수 있습니다.

1

대부분의 컴파일러는 정수 곱셈을 일련의 비트 시프트와 분해로 분해 할만큼 똑똑합니다. Windows 컴파일러에 대해서는 모르겠지만 적어도 gcc를 사용하면 어셈블러를 침을 뱉어 낼 수 있습니다. 그러면이 어셈블러를 보면 두 어셈블러 모두에서 동일한 어셈블러를 볼 수 있습니다.

0

컴파일러는 귀하와 같은 코드를 최적화합니다. 현대 컴파일러는 두 경우 모두 동일한 코드를 생성하고 왼쪽 시프트로 * 2을 추가로 대체합니다.

+0

확실하지 않음 :) 임베디드 소프트웨어 개발을 위해 정말 이상한 컴파일러를 보았습니다. – aku

+1

임베디드 시스템에서 거의 모든 기존의 지혜가 끝납니다. ;-) –

4

이것은 컴파일러, 구성 및 주변 코드에 따라 달라집니다.

측정을하지 않고 상황이 '빠르다'는 것을 시도하거나 추측해서는 안됩니다. 거의 항상 완벽한 부적절, 그리고 당신이 진정으로이 중요 도메인에서 작업하는 경우, 당신은 이미 프로파일 러를 사용하여보고 할 것 -

일반적으로에서는 현재 나노 최적화 이런 종류의 물건에 대해 걱정하지한다 컴파일러의 어셈블리 언어 출력에서.

10

직접 측정하기가 쉽지 않으므로 어떻게해야합니까?

/* test1.c */ 
int main() 
{ 
    int result = 0; 
    int times = 1000000000; 
    while (--times) 
     result = result * 3; 
    return result; 
} 

machine:~$ gcc -O2 test1.c -o test1 
machine:~$ time ./test1.exe 

real 0m0.673s 
user 0m0.608s 
sys  0m0.000s 

몇 번에 대한 테스트를 수행하고, 다른 경우에 반복 (Cygwin에서에서 gcctime 사용).

당신이 어셈블리 코드를 들여다하려면

gcc -S -O2 test1.c

+0

불행히도 이것은 나쁜 예입니다 - i686-apple-darwin8-gcc-4.0.1에서는 항상 0이므로 루프에서 "result = result * 3"을 완전히 제거합니다. 초기 조건을 "결과 = 1"로 변경하면 더 나은 결과를 얻을 수 있습니다. –

+0

3 이상인 경우 난수 배열을 생성하여 처리하므로 컴파일러에서 가정을 할 수 없습니다. – DarenW

15

그것은 중요하지 않습니다. 최신 프로세서는 일련의 시프트를 수행해야하는 이전 프로세서와 달리 MUL을 수행하기 위해 내부적으로 추가함으로써 다중 사이클을 사용하여 정수 클록 사이클 이하로 정수형 MUL 명령어를 실행할 수 있습니다.나는

MUL EAX,3 

보다 빠른

MOV EBX,EAX 
SHL EAX,1 
ADD EAX,EBX 

최적화 이런 종류의 유용되었을 수 있습니다 마지막 프로세서는 아마이었다 실행 내기 것 486 (예, 이것은 인텔 프로세서 편견이지만,이다 다른 아키텍쳐의 대표 일 수도 있음).

어쨌든 합리적인 컴파일러는 가장 작은/가장 빠른 코드를 생성 할 수 있어야합니다. 항상 가독성을 우선 고려해야합니다.

+3

당신이 사용할 수있는 레지스터에 대한 대기 시간과 비 융통성이 고려되면 MUL이 더 빠르게 실행되는 것은 의심 스럽습니다. 또한 x86에서 LEA는 3 명령 시퀀스가 ​​아닌 3 * n 및 n + 2 * n에 적합한 컴파일러에서 사용됩니다. –

+1

참이지만 LEA는 작은 상수 세트 (정확하게 기억한다면 2, 3, 4, 5, 8 & 9)를 곱할 때만 유용합니다. 어쨌든 제 요점은 컴파일러가 가장 빠른 코드를 찾아 내도록하는 것이 었습니다. – Ferruccio

4

컴파일러가 코드로 무엇을하는지 알아내는 것은 어렵지 않습니다. 여기서는 DevStudio 2005를 사용하고 있습니다. 다음 코드로 간단한 프로그램을 작성하십시오.

중간 행에 중단 점을 배치하고 디버거를 사용하여 코드를 실행하십시오. 중단 점이 트리거되면 소스 파일을 마우스 오른쪽 단추로 클릭하고 "디스 어셈블리로 이동"을 선택하십시오. 이제 CPU가 실행중인 코드 창이 나타납니다. 이 경우 마지막 두 줄은 "lea eax, [ebx + ebx * 2]"(이 특별한 경우에는 비트 이동 및 추가가 아님)와 똑같은 명령어를 생성합니다. 최신 IA32 CPU에서 CPU의 파이프 라인 특성으로 인해 비트 이동이 아닌 직선 MUL을 수행하는 것이 더 효율적입니다. 수정 된 값을 너무 빨리 사용하면 패널티가 발생합니다.

이것은 aku가 말하고있는 것을 보여줍니다. 즉, 컴파일러는 코드에 대한 최상의 지침을 선택하기에 충분히 똑똑합니다.

+0

파이프 라인이 문제가 아닙니다. 산술 유닛은 내부적으로 한 단계에서 ebx + ebx * 2를 사용할 수 있습니다. – Artelius

0

그런 식으로 코드를 최적화하려면 컴파일러를 신뢰하십시오. 가독성은 코드 수준에서 훨씬 더 중요합니다. 진정한 최적화는 더 높은 수준에서 이루어져야합니다.

1

상관하지 않습니다. 나는 더 중요한 것들을 최적화 할 필요가 있다고 생각한다. 혼자서 코딩하고 테스트하는 대신에 생각을 투자하고 그 질문을 쓰는 데 얼마나 많은 시간을 투자 했습니까?

:-)

만큼 당신이 괜찮은 최적화 컴파일러를 사용하고 같은
1

, 컴파일러는을 이해하기 쉽게 그냥 코드를 작성합니다. 이렇게하면 컴파일러가 현명한 최적화를 쉽게 수행 할 수 있습니다.

이 질문은 최적화 컴파일러가 최적화에 대해 더 잘 알고 있음을 나타냅니다. 그래서 컴파일러를 신뢰하십시오. n * 3을 사용하십시오.

this answer도 확인하십시오.