日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

我们应该搞清楚分支预测

發(fā)布時(shí)間:2023/12/20 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我们应该搞清楚分支预测 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

分支預(yù)測(cè)的英文名字是「Branch Prediction

大家可以在Google上搜索這個(gè)關(guān)鍵字,可以看到關(guān)于分支預(yù)測(cè)的很多內(nèi)容,不過(guò)要搞清楚分支預(yù)測(cè)如何工作的,才是問(wèn)題的關(guān)鍵。

分支預(yù)測(cè)對(duì)程序的影響

我們來(lái)看看下面的兩段代碼

代碼1

#include?<algorithm> #include?<ctime> #include?<iostream> int?main() {// Generate dataconst?unsigned?arraySize = 32768;int?data[arraySize];for?(unsigned?c = 0; c < arraySize; ++c)data[c] = std::rand() % 256;// !!! With this, the next loop runs faster.//std::sort(data, data + arraySize);// Testclock_t?start = clock();long?long?sum = 0;for?(unsigned?i = 0; i < 100000; ++i) {for?(unsigned?c = 0; c < arraySize; ++c) { // Primary loopif?(data[c] >= 128) sum += data[c];}}double?elapsedTime = static_cast<double>(clock()-start) / CLOCKS_PER_SEC;std::cout?<< elapsedTime << '\n';std::cout?<< "sum = "?<< sum << '\n'; }

執(zhí)行結(jié)果

@ubuntu:/data/study$ g++ fenzhi.cpp && ./a.out 21.6046 sum = 314931600000

代碼2

#include?<algorithm> #include?<ctime> #include?<iostream> int?main() {// Generate dataconst?unsigned?arraySize = 32768;int?data[arraySize];for?(unsigned?c = 0; c < arraySize; ++c)data[c] = std::rand() % 256;// !!! With this, the next loop runs faster.std::sort(data, data + arraySize);// Testclock_t?start = clock();long?long?sum = 0;for?(unsigned?i = 0; i < 100000; ++i) {for?(unsigned?c = 0; c < arraySize; ++c) { // Primary loopif?(data[c] >= 128) sum += data[c];}}double?elapsedTime = static_cast<double>(clock()-start) / CLOCKS_PER_SEC;std::cout?<< elapsedTime << '\n';std::cout?<< "sum = "?<< sum << '\n'; }

執(zhí)行結(jié)果:

@ubuntu:/data/study$ g++ fenzhi.cpp && ./a.out 8.52157 sum = 314931600000

第一段代碼生成隨機(jī)數(shù)組后,沒(méi)有進(jìn)行排序,第二段代碼對(duì)隨機(jī)的數(shù)組進(jìn)行排序,執(zhí)行的時(shí)間上發(fā)生了非常大的差異。

所以,他們發(fā)生了什么事情呢?

導(dǎo)致他們結(jié)果不同的原因,就是分支預(yù)測(cè),分支預(yù)測(cè)是CPU處理器對(duì)程序的一種預(yù)測(cè),和CPU架構(gòu)有關(guān)系,現(xiàn)在的很多處理器都有分支預(yù)測(cè)的功能。

CPU在執(zhí)行這段代碼的時(shí)候

if?(data[c] >= 128) sum += data[c];

CPU會(huì)有一個(gè)提前預(yù)測(cè)機(jī)制,比如前面的執(zhí)行結(jié)果都是true,那么下一次在判斷if的時(shí)候,就會(huì)默認(rèn)認(rèn)為是true來(lái)處理,讓下面的幾條指令提前進(jìn)入預(yù)裝。

當(dāng)然,這個(gè)判斷不會(huì)影響實(shí)際的結(jié)果輸出,這個(gè)判斷只是為了讓CPU并行執(zhí)行代碼。

CPU執(zhí)行一條指令分為幾個(gè)階段

既然是分階段執(zhí)行,也就是我們正常說(shuō)的pipeline(流水線(xiàn)執(zhí)行)。

流水線(xiàn)的工人只要完成自己負(fù)責(zé)的內(nèi)容就好了,沒(méi)有必要去關(guān)心其他的人處理。

那如果我有一段代碼,如下:

int?a = 0; a?+= 1; a?+= 2; a?+= 3;

從這個(gè)圖上我們可以看到,我們認(rèn)為是在執(zhí)行 a = 0結(jié)束后,才會(huì)執(zhí)行a+=1。

但是實(shí)際CPU是在執(zhí)行a=0的第一條執(zhí)行后,馬上就去執(zhí)行a+=1的第一條指令了。

也就因?yàn)檫@樣,執(zhí)行速度上得到了大幅度的提升。

但是對(duì)于if() 語(yǔ)言,在沒(méi)有分支預(yù)測(cè)的時(shí)候,我們需要等待if()執(zhí)行出現(xiàn)結(jié)果后才能繼續(xù)執(zhí)行下一個(gè)代碼。

如果存在分支預(yù)測(cè)的情況

通過(guò)比較我們可以發(fā)現(xiàn),如果存在分支預(yù)測(cè)的時(shí)候,就讓執(zhí)行速度變快了。

那如果預(yù)測(cè)失敗,會(huì)不會(huì)就影響了執(zhí)行的時(shí)間,答案是肯定的。

在前面的例子中,沒(méi)有對(duì)數(shù)組排序的情況下,分支預(yù)測(cè)大部分都會(huì)是失敗的,這個(gè)時(shí)候就會(huì)在執(zhí)行結(jié)束后重新取指令執(zhí)行,會(huì)嚴(yán)重影響執(zhí)行效率。

而在排序后的例子中,分支預(yù)測(cè)一直處于成功的狀態(tài),CPU的執(zhí)行速率得到大幅度的提升。

如果解決分支預(yù)測(cè)引起的性能下降

分支預(yù)測(cè)一定會(huì)存在一定的能性下降,想讓性能提升的方法就是不要使用這個(gè)該死的if語(yǔ)句。

比如,上面的代碼,我們可以修改成這樣

#include?<algorithm> #include?<ctime> #include?<iostream> int?main() {// Generate dataconst?unsigned?arraySize = 32768;int?data[arraySize];for?(unsigned?c = 0; c < arraySize; ++c)data[c] = std::rand() % 256;// !!! With this, the next loop runs faster.//std::sort(data, data + arraySize);// Testclock_t?start = clock();long?long?sum = 0;for?(unsigned?i = 0; i < 100000; ++i) {for?(unsigned?c = 0; c < arraySize; ++c) { // Primary loopint?t = (data[c] - 128) >> 31;sum += ~t & data[c];}}double?elapsedTime = static_cast<double>(clock()-start) / CLOCKS_PER_SEC;std::cout?<< elapsedTime << '\n';std::cout?<< "sum = "?<< sum << '\n'; }

比如,我們看到的絕對(duì)值代碼,里面也用了這樣的思想

/*** abs - return absolute value of an argument* @x: the value. If it is unsigned type, it is converted to signed type first.* char is treated as if it was signed (regardless of whether it really is)* but the macro's return type is preserved as char.** Return: an absolute value of x.*/ #define abs(x) __abs_choose_expr(x, long long, \__abs_choose_expr(x, long, \__abs_choose_expr(x, int, \__abs_choose_expr(x, short, \__abs_choose_expr(x, char, \__builtin_choose_expr( \__builtin_types_compatible_p(typeof(x), char), \(char)({ signed?char?__x = (x); __x<0?-__x:__x; }), \((void)0)))))))#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \__builtin_types_compatible_p(typeof(x), signed?type) || \__builtin_types_compatible_p(typeof(x), unsigned?type), \({ signed?type __x = (x); __x < 0?? -__x : __x; }), other)

當(dāng)然,你也可以這樣寫(xiě)

int?abs(int?i){if(i<0)return?~(--i);return?i; }

所以說(shuō),計(jì)算機(jī)的盡頭是數(shù)學(xué)

參考:

https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array/11227902#11227902

https://blog.csdn.net/loongshawn/article/details/118339009

https://blog.csdn.net/DBC_121/article/details/105360658

總結(jié)

以上是生活随笔為你收集整理的我们应该搞清楚分支预测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。