Saya memutuskan untuk menjalankan kembali tes pada mesin saya sendiri menggunakan kode Lik32. Saya harus mengubahnya karena windows atau kompiler saya berpikir resolusi tinggi adalah 1 ms, menggunakan
mingw32-g ++. exe -O3 -Wall -std = c ++ 11 -feksepsi -g
vector<int> rand_vec(10000000);
GCC telah melakukan transformasi yang sama pada kedua kode asli.
Perhatikan bahwa hanya dua kondisi pertama yang diuji karena yang ketiga harus selalu benar, GCC adalah sejenis Sherlock di sini.
Balik
.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
Jadi ini tidak memberi tahu kita banyak kecuali bahwa kasus terakhir tidak memerlukan prediksi cabang.
Sekarang saya mencoba semua 6 kombinasi if, 2 teratas adalah yang asli terbalik dan diurutkan. tinggi> = 95, rendah <20, sedang 20-94 dengan 10.000.000 iterasi masing-masing.
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.
Jadi mengapa urutannya tinggi, rendah, med maka lebih cepat (sedikit)
Karena yang paling tidak dapat diprediksi adalah yang terakhir dan karena itu tidak pernah dijalankan melalui prediktor cabang.
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.
Jadi cabang akan diprediksi diambil, diambil, dan sisanya dengan
6% + (0,94 *) 20% mispredicts.
"Diurutkan"
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
Cabang-cabang akan diprediksi dengan tidak diambil, tidak diambil dan Sherlock.
25% + (0,75 *) 24% salah duga
Memberikan perbedaan 18-23% (perbedaan terukur ~ 9%) tetapi kita perlu menghitung siklus alih-alih salah mengartikan%.
Mari kita asumsikan 17 siklus kesalahan hukuman pada CPU Nehalem saya dan bahwa setiap cek membutuhkan 1 siklus untuk mengeluarkan (4-5 instruksi) dan loop mengambil satu siklus juga. Ketergantungan data adalah variabel penghitung dan loop, tetapi begitu salah duga tidak keluar dari situ seharusnya tidak mempengaruhi waktu.
Jadi untuk "membalikkan", kita mendapatkan timing (ini harus menjadi rumus yang digunakan dalam Arsitektur Komputer: Pendekatan Kuantitatif 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
dan sama untuk "diurutkan"
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% vs. ~ 9% diukur (dekat dengan yang diukur!?!).
Jadi yang jelas dari OP tidak jelas.
Dengan tes ini, tes lain dengan kode yang lebih rumit atau lebih banyak ketergantungan data tentu akan berbeda, jadi ukur kasus Anda.
Mengubah urutan pengujian mengubah hasil, tetapi itu bisa jadi karena keberpihakan yang berbeda pada awal loop yang idealnya harus 16 byte yang diluruskan pada semua CPU Intel yang lebih baru tetapi tidak dalam kasus ini.