2010-12-07 7 views
8

대답은 하드웨어에 따라 다르다는 것을 알고 있습니다. 그러나 실종 된 일반적인 직관이 있다면 궁금합니다.C++에서 더 빨랐습니까? (2 * i + 1) 또는 (i << 1 | 1)?

나는 "(내가 < < 1 | 1)"내가 사용하는 것이 일반적 나의 접근 방식을 변경해야하는 경우 지금 궁금하네요, 대답 주어진 this 질문 & 묻는 대신 "(2 * I + 1)"의? ?

+4

확실하지는 않지만 동일한 기계 명령어로 작동합니다 ... 그래서 나는 어느 것이 더 읽기 쉽다고 말하고 싶습니다. –

+2

@Jon Seigel : "읽을 수있는"은 코드의 의도를보다 명확하게 표현하는 것을 의미합니다. 당신은 (OP) 2로 곱하고 하나를 더하고 있습니까, 아니면 왼쪽으로 이동하고 LSB를 설정합니까? – jason

+2

컴파일러에서 수행 할 작업을 수행하려고합니다. 그럼 좋지 않아. ^^ – pinichi

답변

8

"...에 대한 답변에 대한 실험
다음 코드 : (32 비트 또는 64 비트 용) gcc -fomit-frame-pointer -O8 -m{32|64}

int main(int argc, char **argv) 
{ 
#ifdef USE_SHIFTOR 
return (argc << 1 | 1); 
#else 
return (2 * argc + 1); 
#endif 
} 

의지하여 다음 코드로 컴파일 :

  1. 86, 32 :
    080483a0 <main>: 
    80483a0: 8b 44 24 04    mov 0x4(%esp),%eax 
    80483a4: 8d 44 00 01    lea 0x1(%eax,%eax,1),%eax 
    80483a8: c3      ret
  2. 는 " LEA를 사용합니다
  3. x86, 64 비트 :
    00000000004004c0 <main>: 
    4004c0: 8d 44 3f 01    lea 0x1(%rdi,%rdi,1),%eax 
    4004c4: c3      retq
  4. x86, 64 비트, -DUSE_SHIFTOR :
    080483a0 <main>: 
    80483a0: 8b 44 24 04    mov 0x4(%esp),%eax 
    80483a4: 01 c0     add %eax,%eax 
    80483a6: 83 c8 01    or  $0x1,%eax 
    80483a9: c3      ret
  5. 86, 32 비트, -DUSE_SHIFTOR : 대부분의 경우는 LEA를 사용하는 것이
    00000000004004c0 <main>: 
    4004c0: 8d 04 3f    lea (%rdi,%rdi,1),%eax 
    4004c3: 83 c8 01    or  $0x1,%eax 
    4004c6: c3      retq

사실, 그것은 사실입니다. 그러나 코드는 이 아니며이 두 경우에 동일합니다.

  1. 또한 오버 플로우와 랩 어라운드 수 있지만 비트 연산은 << 또는 | 같은 수 없습니다
  2. (x + 1) == (x | 1)!(x & 1) 다른 추가는 다음 비트까지 수행하는 경우에만 사실이다 : 그 두 가지 이유가있다. 일반적으로 하나를 추가하면 절반의 경우에 가장 낮은 비트가 설정됩니다.

우리 (및 컴파일러, 아마도)는 두 번째가 반드시 적용 가능하다는 것을 알고 있지만 첫 번째 가능성은 여전히 ​​있습니다. 은 "나 버전은"난 그냥 GCC-4.7.1이 FrankH의 소스를 사용하여이 테스트 1.

+0

어떤 컴파일러를 사용 했습니까? –

+0

gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 –

+1

누군가가 실제로 추측과 야생의 가정을 테스트에 적용했는지 확인하는 것이 좋습니다. 하지만 gcc가 shift 버전을 최적화하지 않는 이유에 대해 설명합니다. 포인트 1이 유효하지 않습니다. x << 1은 모든 x에 대해 x + x와 정확히 같은 방식으로 줄 바꿈을합니다. 또한 최근의 컴파일러는 교대 버전을 매우 동일한 lea 명령어로 최적화합니다. – hirschhornsalz

5

가장 많이 죽은 컴파일러를 제외한 모든 컴파일러는 동일한 표현식을 사용하여 같은 실행 코드로 컴파일합니다.

일반적으로 컴파일러가 최적화 할 때 가장 좋은 방법이기 때문에 일반적으로 이러한 간단한 산술 표현식을 최적화하는 것에 대해 걱정할 필요가 없습니다. ("똑똑한 컴파일러"가 옳은 일을 할 수있는 많은 다른 경우와 달리, 실제 컴파일러는 평평하게 떨어집니다.)

이렇게하면 PPC, Sparc 및 MIPS의 동일한 명령어 쌍이 작동합니다. 길 : 추가와 함께 한 쉬프트. ARM에서는 단일 퓨즈 드 시프트 - 더하기 명령어를 사용하고 x86에서는 아마도 LEA op가 될 것입니다.

+0

x86의 단일 LEA로 컴파일 할 수 없습니까? –

+0

@Axel Gneiting : 오, 네 말이 맞아! 나는 대답을 고쳐 줄 것이다. – Crashworks

+2

예, 아마 x86 환경에서 'LEA EAX, EAX + EAX + 1'이 금식입니다. –

13

ISO 표준은 실제로 성능 요구 사항을 요구하지 않기 때문에 구현, 컴파일러 플래그, 대상 CPU 및 달의 위상에 따라 달라집니다.

알고리즘 선택과 같은 매크로 수준의 최적화에 비해 이러한 종류의 최적화 (몇주기 절약)는 거의 투자 수익 측면에서 중요하지 않습니다.

코드의 가독성을 최우선으로 목표로 삼으십시오. 비트를 이동하려는 의도가 있고 OR 인 경우 비트 시프트 버전을 사용하십시오. 귀하의 의도가 번식하는 경우 * 버전을 사용하십시오. 일단 문제가 발생하면 실적에 대해 걱정할뿐입니다.

어떤 점잖은 컴파일러는 훨씬 더 나은 어쨌든 -S 옵션과 함께 GCC의

+1

컴파일러가 달의 위상에 의존하지 않았 으면 좋겠어.하지만 지금 생각해 보면, 나는 조수의 특성에 의존하는 것처럼 보인다. 만조에서 홍수가 난 것처럼 –

+0

? 나는 더 높은 고도로 서버를 옮길 것을 권할지도 모른다.;) – jalf

+0

나는 곱셈을 최적화하기 위해 비트 쉬프트/가산을 사용하지 않는 컴파일러에 의해 매우 실망했다. –

4

출력 :-) (어떤 컴파일러 플래그가 주어지지) 할 수있는 것보다 최적화 :

.LCFI3: 
     movl 8(%ebp), %eax 
     addl %eax, %eax 
     orl  $1, %eax 
     popl %ebp 
     ret 

.LCFI1: 
     movl 8(%ebp), %eax 
     addl %eax, %eax 
     addl $1, %eax 
     popl %ebp 
     ret 

잘 모르겠어요 어느 쪽이 맞는지는 중요하지만, 나는 그것이 중요하다고 생각하지 않는다.

컴파일러에서 최적화가 전혀 수행되지 않으면 두 번째는 더 빠른 어셈블리 명령어로 변환됩니다. 각 명령어의 소요 시간은 아키텍처에 따라 다릅니다. 대부분의 컴파일러는 동일한 어셈블리 레벨 지침으로 최적화합니다.

+0

사실, 일반적으로, 추가가 10 배의 교대 속도 인 아키텍처를 가질 가능성이 있기 때문에 일반적으로 두 번째 아키텍처가 가장 빠르다 고 말할 수는 없습니다 (하지만 요점은 플랫폼에 따라 다름). 특정 플랫폼으로 자신을 제한하는 경우, 그 경우일지도 모르지만 그 대답을 분명히 밝혀야합니다. – paxdiablo

+1

그리고 속담을 기억하십시오. -O3을 사용하지 않는 벤치마킹은 F1 드라이버를 스케이트 보드에 얼마나 빨리 갈 수 있는지 비교하는 것과 같습니다. – Kos

0

아무도 신경 쓰지 않습니다. 그들도 마찬가지입니다.
걱정하지 마시고 코드가 정확하고 간단하며 완료되도록하십시오.

+1

우리는 "컴파일러가 두 양식을 동일하게 취급 할 것"이라고 말하면서 부정적이지 않거나 최소한 성명서를 뒷받침 할 수 있습니까? –

+0

오케이, 오케이, 미안. "이 세부 사항에서 속도를 중요시한다면 손으로 조립 한 어셈블러를 작성해야합니까?" 아니? 일반적으로 cpp를 쓸 때 정확성, 단순성 및 완성을 위해 노력합니다. 최적화가 단순함에서 따르지 않는다면 다음 불쌍한 애벌레에게 사냥을 의뢰하여이 코드를 찾아 내려고합니다. –

0

i + i + 1은 더하기가 곱셈보다 빠르며 이동보다 빠르기 때문에 보다 빠를 수 있습니다.

+0

이 답변은 근거가없는 추측이므로 힌트조차 없기 때문에 도움이되지 않습니다. 그것을 뒷받침 할 수있는 프로파일 링 또는 해체. 다른 사람들이 말한 바와 같이 사람들이 "마이크로 최적화"를하도록 장려합니다. –

-2

shr 명령은 최악의 경우에 완료하려면 4 클럭 사이클이 걸리는 반면, 첫 번째 형식은 시프트 권한이있는 것이 더 빠릅니다. 그러나 최상의 형식은 다른 (어셈블리) 지시 사항을 완벽하게 파악할 수 있으므로 컴파일러에서 결정해야합니다.

1

에 비트 제로를 강제로 요구하기 때문에 컴파일러 따라서, 서로 다른 코드를 생성하고, 생성 된 코드는

lea 0x1(%rdi,%rdi,1),%eax 
retq 
입니다

은 시프트 또는 곱셈 버전이 사용 되더라도 상관 없습니다.

관련 문제