मैंने लाइक 32 कोड का उपयोग करके अपनी मशीन पर परीक्षण को फिर से चलाने का फैसला किया। मुझे अपनी विंडोज़ या कंपाइलर सोच के कारण इसे बदलना पड़ा है
mingw32-g ++। exe -O3 -Wall -std = c ++ 11 -fexception -g
vector<int> rand_vec(10000000);
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
तो यह हमें बहुत कुछ नहीं बताता सिवाय इसके कि आखिरी मामले के लिए शाखा की भविष्यवाणी की जरूरत नहीं है।
अब मैंने इफ के सभी 6 संयोजनों की कोशिश की, शीर्ष 2 मूल रिवर्स और सॉर्ट किए गए हैं। उच्च है = = 95, निम्न <20 है, मध्य प्रत्येक 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% का अंतर मापा गया) लेकिन हमें गलत% के बजाय चक्रों की गणना करने की आवश्यकता है।
मान लें कि मेरे नेहेल्म सीपीयू पर 17 चक्र गलत अर्थदंड लगाया गया है और प्रत्येक चेक को जारी करने के लिए 1 चक्र लगता है (4-5 निर्देश) और लूप एक चक्र भी लेता है। डेटा निर्भरता काउंटर और लूप वैरिएबल हैं, लेकिन एक बार गलतफहमी इस तरह से बाहर हो जाती है कि यह समय को प्रभावित नहीं करना चाहिए।
तो "रिवर्स" के लिए, हमें समय मिलता है (यह कंप्यूटर आर्किटेक्चर में उपयोग किया जाने वाला सूत्र होना चाहिए: एक मात्रात्मक दृष्टिकोण 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% मापा (मापा के पास!)!)।
तो ओपी का स्पष्ट होना स्पष्ट नहीं है।
इन परीक्षणों के साथ, अधिक जटिल कोड या अधिक डेटा निर्भरता वाले अन्य परीक्षण निश्चित रूप से भिन्न होंगे ताकि आपके मामले को मापें।
परीक्षण के क्रम को बदलने से परिणाम बदल गए लेकिन यह लूप शुरू के विभिन्न संरेखण के कारण हो सकता है जो आदर्श रूप से 16 नए बाइट्स को सभी नए इंटेल सीपीयू पर गठबंधन किया जाना चाहिए, लेकिन इस मामले में नहीं है।