나는 친구와 다른 사람들에 대한 두 개의 조각. 는 빠르게 그리고 왜?
value = 5;
if (condition) {
value = 6;
}
고:
if (condition) {
value = 6;
} else {
value = 5;
}
어떤 경우value
행렬?
참고:알고 있는value=상태? 6:5;
이 존재하고 나는 그것을 기대할 빨리,하지만 그것이 아니었't 는 옵션입니다.
편집(요청한 직원은 이 질문에 그 순간에):
대한 답변을 주시기 바랍니다-을 고려하여 중86 어셈블리의해 생성되는 주류의 컴파일러(말 g++,소++,vc,mingw)모두에서 최적화하고 비 최적화되어 버전 또는MIPS 어셈블리. -언제 어셈블리가 다른 이유를 설명하는 버전이 빠르고 때(예를 들면"기 때문에 더 나은 분기 없 및 분기에는 다음과 같은 문제 blahblah")
TL;박사:에서는 최적화되지 않은 코드,if
없이다른
보 irrelevantly 더 효율적이지만 심지어 가장 기본적인 수준의 최적화를 사용하는 코드는 기본적으로 다시 작성하는value=상태+5
.
나는 그것을 시도 했]1과 생성된 어셈블리를 위해 다음과 같은 코드:
int ifonly(bool condition, int value)
{
value = 5;
if (condition) {
value = 6;
}
return value;
}
int ifelse(bool condition, int value)
{
if (condition) {
value = 6;
} else {
value = 5;
}
return value;
}
에 gcc6.3 최적화 기능을 비활성화(-O0
),관련 차이점은:
mov DWORD PTR [rbp-8], 5
cmp BYTE PTR [rbp-4], 0
je .L2
mov DWORD PTR [rbp-8], 6
.L2:
mov eax, DWORD PTR [rbp-8]
위해은
도구ifelse
가
cmp BYTE PTR [rbp-4], 0
je .L5
mov DWORD PTR [rbp-8], 6
jmp .L6
.L5:
mov DWORD PTR [rbp-8], 5
.L6:
mov eax, DWORD PTR [rbp-8]
후기가 약간 더 효율적을 가지고 있기 때문에 추가 뛰어나 둘 다에서 두 개 이상의과에서 가장 세 할당 그래서 당신이하지 않는 진짜 필요하의 성능(힌트:작업하는 경우가 아니면에스지't 며,심지어 당신은 _probably_don't)차지't 할 수 있습니다.
그러나,가장 낮은 optimization level(-O1
)두 기능을 감소하는 동일합니다:
test dil, dil
setne al
movzx eax, al
add eax, 5
는 기본적으로 동의
return 5 + condition;
가정한 조건은 하나.
높은 최적화 수준 don't 이 정말로 출력물을 제외하고,그들을 피하기 위해 관리합movzx
에 의해 효율적으로 비우는EAX
등록에서 시작합니다.
면책 조항:당신은 아마 안't 쓰5+상태
자신(도 불구하는 표준이 보장되는 변환true
를 정수 유형은1
)기 때문에 의도되지 않을 수도 한눈에 명확하게 이해하기 어려운 사람들을 읽기 코드(를 포함할 수 있는 미래의 자). 이 코드를 표시하는 컴파일러에서 생산하는 두가지 경우입니다(실제적으로)동일합니다. Ciprian Tomoiaga국은 그것에서 아주 잘한 의견:
ahuman's 은 코드를 작성에 대한 인간고compiler코드를 작성기계.
응답에서CompuChip보여주는int
그들은 모두를 최적화하여 같은 어셈블리,그래서 그't 문제입니다.
어떤 경우의 값입니다 행렬?
나는 해석합니다 이에 더 일반적 방법,즉,어떤 경우값이
의 종류의 구조 및 임무 비싸다(그리고 움직임은 저렴한).
다음
T value = init1;
if (condition)
value = init2;
하위 때문에 최적의 경우에는상태로
사실,당신은 불필요한 초기화init1
그리고 당신의 사본을 할당합니다.
T value;
if (condition)
value = init2;
else
value = init3;
이것은 더 낫다. 하지만 여전히 최적의 경우 기본 건축가 비용과하는 경우 복사본을 건설은 더 비싼 다음 초기 설정입니다.
을 조건자 솔루션에 좋은:
T value = condition ? init1 : init2;
는 경우 또는,don't 과 같은 조건부 연산자를 만들 수 있는 도우미 기능은 다음과 같다:
T create(bool condition)
{
if (condition)
return {init1};
else
return {init2};
}
T value = create(condition);
에 따라init1
과init2
은 또한 고려할 수 있습 this:
auto final_init = condition ? init1 : init2;
T value = final_init;
하지만 다시 한번 강조해야하는 것이 관련된 경우에만 건축 및 할당은 정말에 대한 비용이 지정된 형식. 그리고 그렇다 하더라도,그에 의해서만프로파일링당신은 확실히 알 수 있습니다.
에서 의사 어셈블리 언어
li #0, r0
test r1
beq L1
li #1, r0
L1:
may 수 있습 not 보다 빠
test r1
beq L1
li #1, r0
bra L2
L1:
li #0, r0
L2:
에 따라 어떻게 정교한 실제 CPU 입니다. 에서 간단한 좋은:
*으로 알려진 계정 목록을 얻을 수있 CPU 후에 제조된 대략 1990,좋은 성과에 따라 코드 피팅에 있는 지시](https://en.wikipedia.org/wiki/Cache_memory). 때에 따라서을 최소화하는 코드 사이즈. 이것에 무게 찬성의 첫 번째 예입니다.
*기본"순서대로 다섯 단계 파이프라인"CPU,아직 약에서 무엇을 얻을 많은 마이크로컨트롤러,이는파이프라인 버블모든 시간 지점—조건부 또는 무조건—은,그래서 그것은 또한 중요한 수를 최소화하의 지점이다. 이것은 또한 무게 찬성의 첫 번째 예입니다.
*좀 더 세련된 Cpu 멋을 충분히 할"out-of-order 실행"지만,충분히 멋진을 사용하여 가장 잘 알려진 구현하는 개념이 발생할 수 있는 파이프라인 거품을 할 때마다 그들은 발생작성 후 쓰기 hazards. 이것에 무게 찬성 second 를 들어,r0
은 한 번만 작성하면 무슨 상관없습니다. 이러한 Cpu 에는 일반적으로 멋진 프로세스를 무조건적 지점에서 교육 가져오기 프로그램,그래서 당신은 aren't 거래서 작성 후 쓰기에 대한 처벌 지점 penalty.
I don't 알고 있는 경우는 사람입니다 여전히 만드는 이런 종류의 CPU 더 이상입니다. 그러나 Cpudo 사용하면"가장 잘 알려진 구현"의 순서 실행 가능성이 있을 절감에 덜 자주 사용하는 지침,그래서 당신이 알고 있어야 이런 일이 발생할 수 있습니다. 실제 예는잘못된 데이터에 의존 대상록에서popcnt
과lzcnt
on Sandy Bridge Cpu.
*가장 높은 끝에서,이 OOO 엔진 것이 바람 발행이 정확히 같은 시퀀스의 내부 작업 모두에 대한 코드 조각—이것은 하드웨어 버전"don't 에 대해 걱정,그것을 컴파일러에서 생성됩니다 같은 컴퓨터 코드고 있습니다."그러나,코드 크기가 여전히 문제하고,지금 당신은 또한에 대해 걱정해야한 예측의 조건부 지점입니다. 분기 예측오류 잠재적으로는 완전한 파이프라인 flush 는 치명적인 성능에 대한 참조 https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array 얼마나 이해하기 위해서는 차이를 만들 수 있습니다.
는 경우 지점 is 고도로 예측할 수 없고,귀하의 CPU 가 조건부 설정하거나 조건부 이 지침에,이 시간 그들을 사용:
리#0,r0 테스트 r1 r0setne
나
리#0,r0 리#1,r2 테스트 r1 movne r2r0
조건부 설정 버전도보다 더 작고 어떤 다른 대안은 경우는 명령을 사용할 수 있 그것은 실질적으로 보장하는 것은 옳은 일을 위해 이 시나리오는 경우에도 분기 예측했다. 조건 이동해야한다는 추가로 스크래치,등록하고 항상 폐기물 중 하나li
instruction's 의 가치가 파견 및 리소스를 실행;경우에는 지점에서 사실은 예측,가지가 많은 버전을 수 있습니다 잘 빠르게 할 수 있습니다.
무엇을 생각하게들도 하나 라이너는 더 빠르게 또는 더 느린?
unsigned int fun0 ( unsigned int condition, unsigned int value )
{
value = 5;
if (condition) {
value = 6;
}
return(value);
}
unsigned int fun1 ( unsigned int condition, unsigned int value )
{
if (condition) {
value = 6;
} else {
value = 5;
}
return(value);
}
unsigned int fun2 ( unsigned int condition, unsigned int value )
{
value = condition ? 6 : 5;
return(value);
}
더 라인의 코드의 높은 수준 언어로 제공하는 컴파일러 더 그래서 작동하려면 일반적인 원칙에 대해 그것을 컴파일러 코드를 더합니다. 는 경우 알고리즘은 같은 다음과 같은 경우 다음 중 하나이 기대하는 것은 최소한의 컴파일러 최적화 그습니다.
00000000 <fun0>:
0: e3500000 cmp r0, #0
4: 03a00005 moveq r0, #5
8: 13a00006 movne r0, #6
c: e12fff1e bx lr
00000010 <fun1>:
10: e3500000 cmp r0, #0
14: 13a00006 movne r0, #6
18: 03a00005 moveq r0, #5
1c: e12fff1e bx lr
00000020 <fun2>:
20: e3500000 cmp r0, #0
24: 13a00006 movne r0, #6
28: 03a00005 moveq r0, #5
2c: e12fff1e bx lr
큰 놀라게 했던 첫 번째 함수에서 다른 순서,동일한 실행 시간이지만.
0000000000000000 <fun0>:
0: 7100001f cmp w0, #0x0
4: 1a9f07e0 cset w0, ne
8: 11001400 add w0, w0, #0x5
c: d65f03c0 ret
0000000000000010 <fun1>:
10: 7100001f cmp w0, #0x0
14: 1a9f07e0 cset w0, ne
18: 11001400 add w0, w0, #0x5
1c: d65f03c0 ret
0000000000000020 <fun2>:
20: 7100001f cmp w0, #0x0
24: 1a9f07e0 cset w0, ne
28: 11001400 add w0, w0, #0x5
2c: d65f03c0 ret
잘하면 당신은 아이디어를 얻을 수 있고 이 질문이 있는 경우 분명 다른 구현하지 않았 실제로 다릅니다.
마찬가지로 지금까지 매트릭스가 확실하지 않는 방법 중요한 것,
if(condition)
{
big blob of code a
}
else
{
big blob of code b
}
그냥 넣어 같은 if-then-else 래퍼로 큰 도시의 모든 코드들 value=5 개 또는 더 복잡합니다. 마찬가지로 비교 경우에도 큰 덩어리의 코드 그것은 여전히 계산되며,동일 또는 동일하지 않은 뭔가 종종 컴파일한 부정적인 경우(상태)뭔가 종종로 컴파일하지 않을 경우 상태 goto.
00000000 <fun0>:
0: 0f 93 tst r15
2: 03 24 jz $+8 ;abs 0xa
4: 3f 40 06 00 mov #6, r15 ;#0x0006
8: 30 41 ret
a: 3f 40 05 00 mov #5, r15 ;#0x0005
e: 30 41 ret
00000010 <fun1>:
10: 0f 93 tst r15
12: 03 20 jnz $+8 ;abs 0x1a
14: 3f 40 05 00 mov #5, r15 ;#0x0005
18: 30 41 ret
1a: 3f 40 06 00 mov #6, r15 ;#0x0006
1e: 30 41 ret
00000020 <fun2>:
20: 0f 93 tst r15
22: 03 20 jnz $+8 ;abs 0x2a
24: 3f 40 05 00 mov #5, r15 ;#0x0005
28: 30 41 ret
2a: 3f 40 06 00 mov #6, r15 ;#0x0006
2e: 30 41
우리는 이 운동을 통해와 다른 사람에 대한 최근의 유래. 이 mips 컴파일러를 흥미롭게도 이 경우 뿐만 아니라 실현 기능이 동일한지만,하나의 기능을 다른 저장에 코드를 공간입니다. 하지 않았는 여기지
00000000 <fun0>:
0: 0004102b sltu $2,$0,$4
4: 03e00008 jr $31
8: 24420005 addiu $2,$2,5
0000000c <fun1>:
c: 0004102b sltu $2,$0,$4
10: 03e00008 jr $31
14: 24420005 addiu $2,$2,5
00000018 <fun2>:
18: 0004102b sltu $2,$0,$4
1c: 03e00008 jr $31
20: 24420005 addiu $2,$2,5
좀 더 많은 대상입니다.
00000000 <_fun0>:
0: 1166 mov r5, -(sp)
2: 1185 mov sp, r5
4: 0bf5 0004 tst 4(r5)
8: 0304 beq 12 <_fun0+0x12>
a: 15c0 0006 mov $6, r0
e: 1585 mov (sp)+, r5
10: 0087 rts pc
12: 15c0 0005 mov $5, r0
16: 1585 mov (sp)+, r5
18: 0087 rts pc
0000001a <_fun1>:
1a: 1166 mov r5, -(sp)
1c: 1185 mov sp, r5
1e: 0bf5 0004 tst 4(r5)
22: 0204 bne 2c <_fun1+0x12>
24: 15c0 0005 mov $5, r0
28: 1585 mov (sp)+, r5
2a: 0087 rts pc
2c: 15c0 0006 mov $6, r0
30: 1585 mov (sp)+, r5
32: 0087 rts pc
00000034 <_fun2>:
34: 1166 mov r5, -(sp)
36: 1185 mov sp, r5
38: 0bf5 0004 tst 4(r5)
3c: 0204 bne 46 <_fun2+0x12>
3e: 15c0 0005 mov $5, r0
42: 1585 mov (sp)+, r5
44: 0087 rts pc
46: 15c0 0006 mov $6, r0
4a: 1585 mov (sp)+, r5
4c: 0087 rts pc
00000000 <fun0>:
0: 00a03533 snez x10,x10
4: 0515 addi x10,x10,5
6: 8082 ret
00000008 <fun1>:
8: 00a03533 snez x10,x10
c: 0515 addi x10,x10,5
e: 8082 ret
00000010 <fun2>:
10: 00a03533 snez x10,x10
14: 0515 addi x10,x10,5
16: 8082 ret
와 컴파일러
이와 함께 나는 코드를 기대하는 것이 하나의 다른 목표를 일치뿐만 아니라
define i32 @fun0(i32 %condition, i32 %value) #0 {
%1 = icmp ne i32 %condition, 0
%. = select i1 %1, i32 6, i32 5
ret i32 %.
}
; Function Attrs: norecurse nounwind readnone
define i32 @fun1(i32 %condition, i32 %value) #0 {
%1 = icmp eq i32 %condition, 0
%. = select i1 %1, i32 5, i32 6
ret i32 %.
}
; Function Attrs: norecurse nounwind readnone
define i32 @fun2(i32 %condition, i32 %value) #0 {
%1 = icmp ne i32 %condition, 0
%2 = select i1 %1, i32 6, i32 5
ret i32 %2
}
00000000 <fun0>:
0: e3a01005 mov r1, #5
4: e3500000 cmp r0, #0
8: 13a01006 movne r1, #6
c: e1a00001 mov r0, r1
10: e12fff1e bx lr
00000014 <fun1>:
14: e3a01006 mov r1, #6
18: e3500000 cmp r0, #0
1c: 03a01005 moveq r1, #5
20: e1a00001 mov r0, r1
24: e12fff1e bx lr
00000028 <fun2>:
28: e3a01005 mov r1, #5
2c: e3500000 cmp r0, #0
30: 13a01006 movne r1, #6
34: e1a00001 mov r0, r1
38: e12fff1e bx lr
fun0:
push.w r4
mov.w r1, r4
mov.w r15, r12
mov.w #6, r15
cmp.w #0, r12
jne .LBB0_2
mov.w #5, r15
.LBB0_2:
pop.w r4
ret
fun1:
push.w r4
mov.w r1, r4
mov.w r15, r12
mov.w #5, r15
cmp.w #0, r12
jeq .LBB1_2
mov.w #6, r15
.LBB1_2:
pop.w r4
ret
fun2:
push.w r4
mov.w r1, r4
mov.w r15, r12
mov.w #6, r15
cmp.w #0, r12
jne .LBB2_2
mov.w #5, r15
.LBB2_2:
pop.w r4
ret
지금은 기술적으로 있는 성과 차이의 일부에서 이러한 솔루션이,때로는 결과가 5 경우에는 뛰어 넘 결과는 6 코드고,반대의 경우도 마찬가지입니다 지점보다 빠르게 실행하는가? 주장 할 수 없었지만 실행해야 하는 다릅니다. 하지만 그의 이상한 경우 조건 vs 하지 않을 경우 상태 코드에서 결과에서는 컴파일러하는 경우 이 뛰어 넘는 다른 실행을 통해. 하지만 이것은 반드시으로 인해 코딩 스타일에 하지만 비교하는 경우와 다른 경우에서 어떤 구문입니다.
인 때문에,어셈블리는 하나의 태그,내가 가지고 있다고 가정 코드입니다 의사 코드(그리고 반드시 c)그 번역하여 그것은 인간으로 6502 어셈블리입니다.
1 옵션(지 않고 다른 사람)
ldy #$00
lda #$05
dey
bmi false
lda #$06
false brk
2 옵션을(으로 다른 사람)
ldy #$00
dey
bmi else
lda #$06
sec
bcs end
else lda #$05
end brk
가정:조건에 Y 등록 설정 0 1 에 첫 번째 라인의 두 옵션 중 하나,그 결과는 어큐뮬레이터에.
그래서,계산 후에 사이클에 대한 모두의 가능성이 각각의 경우에,우리는 제 1 구성은 일반적으로 더 빠르;9 주기 때 조건이 0 고 10 주기 때 조건은 1,반면 두 번째 옵션은 또한 9 개 주기 때 조건이 0 지만,13 주기 때 조건은 1 입니다. (주기 계산에 포함되지 않BRK
끝에).
결론:는 경우에만
이보다 더 빨리는 경우-다른 사람
입니다.
과 완전성을 위해,여기에 최적화된value=상태+5
솔루션:
ldy #$00
lda #$00
tya
adc #$05
brk
이것은 우리의 시간 아래로 8cycles(다시 포함되지 않은BRK
끝에).