Tôi quyết định chạy lại bài kiểm tra trên máy của mình bằng mã Lik32. Tôi đã phải thay đổi nó do các cửa sổ hoặc trình biên dịch của tôi nghĩ rằng độ phân giải cao là 1ms, sử dụng
mingw32-g ++. exe -O3 -Wall -std = c ++ 11 -fexceptions -g
vector<int> rand_vec(10000000);
GCC đã thực hiện chuyển đổi tương tự trên cả hai mã gốc.
Lưu ý rằng chỉ có hai điều kiện đầu tiên được kiểm tra là điều kiện thứ ba phải luôn luôn đúng, GCC là một loại Sherlock ở đây.
Đảo ngược
.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
Vì vậy, điều này không cho chúng ta biết nhiều ngoại trừ trường hợp cuối cùng không cần dự đoán chi nhánh.
Bây giờ tôi đã thử tất cả 6 kết hợp của if, top 2 là đảo ngược ban đầu và được sắp xếp. cao là> = 95, thấp là <20, giữa là 20-94 với 10000000 lần lặp mỗi lần.
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.
Vậy tại sao thứ tự cao, thấp, med lại nhanh hơn (biên)
Bởi vì điều khó lường nhất là cuối cùng và do đó không bao giờ được chạy qua một công cụ dự đoán nhánh.
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.
Vì vậy, các nhánh sẽ được dự đoán lấy, lấy và phần còn lại với
6% + (0,94 *) 20% sai.
"Sắp xếp"
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
Các nhánh sẽ được dự đoán với không lấy, không lấy và Sherlock.
25% + (0,75 *) 24% dự đoán sai
Cho chênh lệch 18-23% (đo chênh lệch ~ 9%) nhưng chúng ta cần tính chu kỳ thay vì dự đoán sai%.
Giả sử 17 chu kỳ bị phạt dự đoán sai đối với CPU Nehalem của tôi và mỗi lần kiểm tra cần 1 chu kỳ để ban hành (4-5 hướng dẫn) và vòng lặp cũng mất một chu kỳ. Các phụ thuộc dữ liệu là các bộ đếm và các biến vòng lặp, nhưng một khi các thông tin sai lệch sẽ không ảnh hưởng đến thời gian.
Vì vậy, để "đảo ngược", chúng ta có được thời gian (đây phải là công thức được sử dụng trong Kiến trúc máy tính: Phương pháp định lượng 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
và tương tự cho "sắp xếp"
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% so với ~ 9% được đo (gần với số đo!?!).
Vì vậy, sự rõ ràng của OP là không rõ ràng.
Với các thử nghiệm này, các thử nghiệm khác với mã phức tạp hơn hoặc phụ thuộc dữ liệu nhiều hơn chắc chắn sẽ khác nhau vì vậy hãy đo trường hợp của bạn.
Việc thay đổi thứ tự kiểm tra đã thay đổi kết quả nhưng điều đó có thể là do sự sắp xếp khác nhau của bắt đầu vòng lặp, lý tưởng nhất là 16 byte được căn chỉnh trên tất cả các CPU Intel mới hơn nhưng không phải trong trường hợp này.