대답은 하드웨어에 따라 다르다는 것을 알고 있습니다. 그러나 실종 된 일반적인 직관이 있다면 궁금합니다.C++에서 더 빨랐습니까? (2 * i + 1) 또는 (i << 1 | 1)?
나는 "(내가 < < 1 | 1)"내가 사용하는 것이 일반적 나의 접근 방식을 변경해야하는 경우 지금 궁금하네요, 대답 주어진 this 질문 & 묻는 대신 "(2 * I + 1)"의? ?
대답은 하드웨어에 따라 다르다는 것을 알고 있습니다. 그러나 실종 된 일반적인 직관이 있다면 궁금합니다.C++에서 더 빨랐습니까? (2 * i + 1) 또는 (i << 1 | 1)?
나는 "(내가 < < 1 | 1)"내가 사용하는 것이 일반적 나의 접근 방식을 변경해야하는 경우 지금 궁금하네요, 대답 주어진 this 질문 & 묻는 대신 "(2 * I + 1)"의? ?
"...에 대한 답변에 대한 실험
다음 코드 : (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
}
의지하여 다음 코드로 컴파일 :
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
LEA
를 사용합니다
00000000004004c0 <main>: 4004c0: 8d 44 3f 01 lea 0x1(%rdi,%rdi,1),%eax 4004c4: c3 retq
-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
-DUSE_SHIFTOR
: 대부분의 경우는 LEA
를 사용하는 것이 00000000004004c0 <main>: 4004c0: 8d 04 3f lea (%rdi,%rdi,1),%eax 4004c3: 83 c8 01 or $0x1,%eax 4004c6: c3 retq
사실, 그것은 사실입니다. 그러나 코드는 이 아니며이 두 경우에 동일합니다.
<<
또는 |
같은 수 없습니다(x + 1) == (x | 1)
!(x & 1)
다른 추가는 다음 비트까지 수행하는 경우에만 사실이다 : 그 두 가지 이유가있다. 일반적으로 하나를 추가하면 절반의 경우에 가장 낮은 비트가 설정됩니다.우리 (및 컴파일러, 아마도)는 두 번째가 반드시 적용 가능하다는 것을 알고 있지만 첫 번째 가능성은 여전히 있습니다. 은 "나 버전은"난 그냥 GCC-4.7.1이 FrankH의 소스를 사용하여이 테스트 1.
어떤 컴파일러를 사용 했습니까? –
gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 –
누군가가 실제로 추측과 야생의 가정을 테스트에 적용했는지 확인하는 것이 좋습니다. 하지만 gcc가 shift 버전을 최적화하지 않는 이유에 대해 설명합니다. 포인트 1이 유효하지 않습니다. x << 1은 모든 x에 대해 x + x와 정확히 같은 방식으로 줄 바꿈을합니다. 또한 최근의 컴파일러는 교대 버전을 매우 동일한 lea 명령어로 최적화합니다. – hirschhornsalz
가장 많이 죽은 컴파일러를 제외한 모든 컴파일러는 동일한 표현식을 사용하여 같은 실행 코드로 컴파일합니다.
일반적으로 컴파일러가 최적화 할 때 가장 좋은 방법이기 때문에 일반적으로 이러한 간단한 산술 표현식을 최적화하는 것에 대해 걱정할 필요가 없습니다. ("똑똑한 컴파일러"가 옳은 일을 할 수있는 많은 다른 경우와 달리, 실제 컴파일러는 평평하게 떨어집니다.)
이렇게하면 PPC, Sparc 및 MIPS의 동일한 명령어 쌍이 작동합니다. 길 : 추가와 함께 한 쉬프트. ARM에서는 단일 퓨즈 드 시프트 - 더하기 명령어를 사용하고 x86에서는 아마도 LEA
op가 될 것입니다.
x86의 단일 LEA로 컴파일 할 수 없습니까? –
@Axel Gneiting : 오, 네 말이 맞아! 나는 대답을 고쳐 줄 것이다. – Crashworks
예, 아마 x86 환경에서 'LEA EAX, EAX + EAX + 1'이 금식입니다. –
ISO 표준은 실제로 성능 요구 사항을 요구하지 않기 때문에 구현, 컴파일러 플래그, 대상 CPU 및 달의 위상에 따라 달라집니다.
알고리즘 선택과 같은 매크로 수준의 최적화에 비해 이러한 종류의 최적화 (몇주기 절약)는 거의 투자 수익 측면에서 중요하지 않습니다.
코드의 가독성을 최우선으로 목표로 삼으십시오. 비트를 이동하려는 의도가 있고 OR
인 경우 비트 시프트 버전을 사용하십시오. 귀하의 의도가 번식하는 경우 *
버전을 사용하십시오. 일단 문제가 발생하면 실적에 대해 걱정할뿐입니다.
어떤 점잖은 컴파일러는 훨씬 더 나은 어쨌든 -S 옵션과 함께 GCC의
컴파일러가 달의 위상에 의존하지 않았 으면 좋겠어.하지만 지금 생각해 보면, 나는 조수의 특성에 의존하는 것처럼 보인다. 만조에서 홍수가 난 것처럼 –
? 나는 더 높은 고도로 서버를 옮길 것을 권할지도 모른다.;) – jalf
나는 곱셈을 최적화하기 위해 비트 쉬프트/가산을 사용하지 않는 컴파일러에 의해 매우 실망했다. –
출력 :-) (어떤 컴파일러 플래그가 주어지지) 할 수있는 것보다 최적화 :
.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
잘 모르겠어요 어느 쪽이 맞는지는 중요하지만, 나는 그것이 중요하다고 생각하지 않는다.
컴파일러에서 최적화가 전혀 수행되지 않으면 두 번째는 더 빠른 어셈블리 명령어로 변환됩니다. 각 명령어의 소요 시간은 아키텍처에 따라 다릅니다. 대부분의 컴파일러는 동일한 어셈블리 레벨 지침으로 최적화합니다.
아무도 신경 쓰지 않습니다. 그들도 마찬가지입니다.
걱정하지 마시고 코드가 정확하고 간단하며 완료되도록하십시오.
우리는 "컴파일러가 두 양식을 동일하게 취급 할 것"이라고 말하면서 부정적이지 않거나 최소한 성명서를 뒷받침 할 수 있습니까? –
오케이, 오케이, 미안. "이 세부 사항에서 속도를 중요시한다면 손으로 조립 한 어셈블러를 작성해야합니까?" 아니? 일반적으로 cpp를 쓸 때 정확성, 단순성 및 완성을 위해 노력합니다. 최적화가 단순함에서 따르지 않는다면 다음 불쌍한 애벌레에게 사냥을 의뢰하여이 코드를 찾아 내려고합니다. –
i + i + 1
은 더하기가 곱셈보다 빠르며 이동보다 빠르기 때문에 보다 빠를 수 있습니다.
이 답변은 근거가없는 추측이므로 힌트조차 없기 때문에 도움이되지 않습니다. 그것을 뒷받침 할 수있는 프로파일 링 또는 해체. 다른 사람들이 말한 바와 같이 사람들이 "마이크로 최적화"를하도록 장려합니다. –
shr 명령은 최악의 경우에 완료하려면 4 클럭 사이클이 걸리는 반면, 첫 번째 형식은 시프트 권한이있는 것이 더 빠릅니다. 그러나 최상의 형식은 다른 (어셈블리) 지시 사항을 완벽하게 파악할 수 있으므로 컴파일러에서 결정해야합니다.
에 비트 제로를 강제로 요구하기 때문에 컴파일러 따라서, 서로 다른 코드를 생성하고, 생성 된 코드는
lea 0x1(%rdi,%rdi,1),%eax
retq
입니다
은 시프트 또는 곱셈 버전이 사용 되더라도 상관 없습니다.
확실하지는 않지만 동일한 기계 명령어로 작동합니다 ... 그래서 나는 어느 것이 더 읽기 쉽다고 말하고 싶습니다. –
@Jon Seigel : "읽을 수있는"은 코드의 의도를보다 명확하게 표현하는 것을 의미합니다. 당신은 (OP) 2로 곱하고 하나를 더하고 있습니까, 아니면 왼쪽으로 이동하고 LSB를 설정합니까? – jason
컴파일러에서 수행 할 작업을 수행하려고합니다. 그럼 좋지 않아. ^^ – pinichi