Lik32 코드를 사용하여 내 컴퓨터에서 테스트를 다시 실행하기로 결정했습니다. 높은 해상도가 1ms라고 생각하는 내 창이나 컴파일러로 인해 변경해야했습니다.
mingw32-g ++. exe -O3-벽 -std = c ++ 11 -fexceptions -g
vector<int> rand_vec(10000000);
GCC는 두 원본 코드에서 동일한 변환을 수행했습니다.
세 번째 조건이 항상 맞아야하기 때문에 두 가지 첫 번째 조건 만 테스트합니다. GCC는 일종의 셜록입니다.
역전
.L233:
mov DWORD PTR [rsp+104], 0
mov DWORD PTR [rsp+100], 0
mov DWORD PTR [rsp+96], 0
call std::chrono::_V2::system_clock::now()
mov rbp, rax
mov rax, QWORD PTR [rsp+8]
jmp .L219
.L293:
mov edx, DWORD PTR [rsp+104]
add edx, 1
mov DWORD PTR [rsp+104], edx
.L217:
add rax, 4
cmp r14, rax
je .L292
.L219:
mov edx, DWORD PTR [rax]
cmp edx, 94
jg .L293 // >= 95
cmp edx, 19
jg .L218 // >= 20
mov edx, DWORD PTR [rsp+96]
add rax, 4
add edx, 1 // < 20 Sherlock
mov DWORD PTR [rsp+96], edx
cmp r14, rax
jne .L219
.L292:
call std::chrono::_V2::system_clock::now()
.L218: // further down
mov edx, DWORD PTR [rsp+100]
add edx, 1
mov DWORD PTR [rsp+100], edx
jmp .L217
And sorted
mov DWORD PTR [rsp+104], 0
mov DWORD PTR [rsp+100], 0
mov DWORD PTR [rsp+96], 0
call std::chrono::_V2::system_clock::now()
mov rbp, rax
mov rax, QWORD PTR [rsp+8]
jmp .L226
.L296:
mov edx, DWORD PTR [rsp+100]
add edx, 1
mov DWORD PTR [rsp+100], edx
.L224:
add rax, 4
cmp r14, rax
je .L295
.L226:
mov edx, DWORD PTR [rax]
lea ecx, [rdx-20]
cmp ecx, 74
jbe .L296
cmp edx, 19
jle .L297
mov edx, DWORD PTR [rsp+104]
add rax, 4
add edx, 1
mov DWORD PTR [rsp+104], edx
cmp r14, rax
jne .L226
.L295:
call std::chrono::_V2::system_clock::now()
.L297: // further down
mov edx, DWORD PTR [rsp+96]
add edx, 1
mov DWORD PTR [rsp+96], edx
jmp .L224
따라서 마지막 경우에는 분기 예측이 필요하지 않다는 점을 제외하고는 많은 것을 알려주지 않습니다.
이제 if의 6 가지 조합을 모두 시도했지만 상단 2는 원래 역순으로 정렬되었습니다. high는> = 95, low는 <20, mid는 각각 10000000 반복으로 20-94입니다.
high, low, mid: 43000000ns
mid, low, high: 46000000ns
high, mid, low: 45000000ns
low, mid, high: 44000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 44000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 45000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 42000000ns
mid, low, high: 46000000ns
high, mid, low: 46000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 43000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 44000000ns
low, mid, high: 44000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 43000000ns
mid, low, high: 48000000ns
high, mid, low: 44000000ns
low, mid, high: 44000000ns
mid, high, low: 45000000ns
low, high, mid: 45000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 43000000ns
mid, low, high: 47000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 46000000ns
low, high, mid: 44000000ns
high, low, mid: 43000000ns
mid, low, high: 46000000ns
high, mid, low: 45000000ns
low, mid, high: 45000000ns
mid, high, low: 45000000ns
low, high, mid: 44000000ns
high, low, mid: 42000000ns
mid, low, high: 46000000ns
high, mid, low: 44000000ns
low, mid, high: 45000000ns
mid, high, low: 45000000ns
low, high, mid: 44000000ns
1900020, 7498968, 601012
Process returned 0 (0x0) execution time : 2.899 s
Press any key to continue.
그렇다면 왜 순서가 높고 낮고 메드가 더 빠르며 (마지막으로)
가장 예측할 수없는 것이 마지막이므로 분기 예측자를 통해 실행되지 않습니다.
if (i >= 95) ++nHigh; // most predictable with 94% taken
else if (i < 20) ++nLow; // (94-19)/94% taken ~80% taken
else if (i >= 20 && i < 95) ++nMid; // never taken as this is the remainder of the outfalls.
따라서 가지를 예측하고 가져 가고 나머지를
6 % + (0.94 *) 20 %의 오해.
"정렬"
if (i >= 20 && i < 95) ++nMid; // 75% not taken
else if (i < 20) ++nLow; // 19/25 76% not taken
else if (i >= 95) ++nHigh; //Least likely branch
가지가 취해지지 않고 셜록으로 예측됩니다.
25 % + (0.75 *) 24 %의 오해
18-23 %의 차이 (측정 된 차이는 ~ 9 %)를 주지만 %를 잘못 예측하는 대신주기를 계산해야합니다.
Nehalem CPU에서 17 사이클이 페널티를 잘못 예측한다고 가정하고 각 검사에서 발행하는 데 1 사이클이 걸리고 (4-5 명령) 루프도 1 사이클이 걸린다고 가정 해 봅시다. 데이터 의존성은 카운터와 루프 변수이지만, 일단 잘못 예측하면 시간에 영향을 미치지 않아야합니다.
따라서 "역"의 경우 타이밍을 얻습니다 (컴퓨터 아키텍처 : 양적 접근 방식 IIRC에서 사용되는 공식이어야 함).
mispredict*penalty+count+loop
0.06*17+1+1+ (=3.02)
(propability)*(first check+mispredict*penalty+count+loop)
(0.19)*(1+0.20*17+1+1)+ (= 0.19*6.4=1.22)
(propability)*(first check+second check+count+loop)
(0.75)*(1+1+1+1) (=3)
= 7.24 cycles per iteration
"정렬"과 동일
0.25*17+1+1+ (=6.25)
(1-0.75)*(1+0.24*17+1+1)+ (=.25*7.08=1.77)
(1-0.75-0.19)*(1+1+1+1) (= 0.06*4=0.24)
= 8.26
(8.26-7.24) /8.26 = 13.8 % 대 ~ 9 % 측정 (측정 값에 근접!?!).
따라서 OP의 분명함은 분명하지 않습니다.
이러한 테스트를 통해 더 복잡한 코드 또는 더 많은 데이터 종속성을 가진 다른 테스트는 확실히 다르므로 사례를 측정하십시오.
테스트 순서를 변경하면 결과가 변경되었지만 루프 시작의 정렬이 다르기 때문에 모든 최신 Intel CPU에서 16 바이트로 정렬되어야하지만이 경우에는 그렇지 않습니다.