内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

為什么程序員喜歡用大量的ifelse而偏不用switch

為什么程序員喜歡用大量的if else而偏不用switch,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到吳興網(wǎng)站設(shè)計(jì)與吳興網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋吳興地區(qū)。

前言

說(shuō)來(lái)也是巧最近在看 Dubbo 源碼,然后發(fā)現(xiàn)了一處很奇怪的代碼,剛好和這個(gè) switch 和 if else 有關(guān)!

讓我們來(lái)看一下這段代碼,它屬于 ChannelEventRunnable,這個(gè) runnable 是 Dubbo IO 線程創(chuàng)建,將此任務(wù)扔到業(yè)務(wù)線程池中處理.

為什么程序員喜歡用大量的if else而偏不用switch

看到?jīng)],把 state == ChannelState.RECEIVED 拎出來(lái)獨(dú)立一個(gè) if,而其他的 state 還是放在 switch 里面判斷。

為什么程序員喜歡用大量的if else而偏不用switch

我當(dāng)時(shí)腦子里就來(lái)回掃描,想想這個(gè)到底有什么花頭,奈何知識(shí)淺薄一臉懵逼。

于是就開(kāi)始了一波探險(xiǎn)之旅!

原來(lái)是 CPU 分支預(yù)測(cè)

遇到問(wèn)題當(dāng)然是問(wèn)搜索引擎了,一般而言我會(huì)同時(shí)搜索各大引擎,咱這也不說(shuō)誰(shuí)比誰(shuí)好,反正有些時(shí)候度娘還是不錯(cuò)的,比如這次搜索度娘給的結(jié)果比較靠前,google 比較靠后。

一般搜索東西我都喜歡先在官網(wǎng)上搜,找不到了再放開(kāi)搜,所以先這么搜 site:xxx.com key。

為什么程序員喜歡用大量的if else而偏不用switch

你看這就有了,完美啊!

我們先來(lái)看看官網(wǎng)的這篇博客怎么說(shuō)的,然后再詳細(xì)地分析一波。

Dubbo 官網(wǎng)的博客

現(xiàn)代 CPU 都支持分支預(yù)測(cè) (branch prediction) 和指令流水線 (instruction pipeline),這兩個(gè)結(jié)合可以極大提高 CPU 效率。對(duì)于簡(jiǎn)單的 if 跳轉(zhuǎn),CPU 是可以比較好地做分支預(yù)測(cè)的。但是對(duì)于 switch 跳轉(zhuǎn),CPU 則沒(méi)有太多的辦法。 switch 本質(zhì)上是根據(jù)索引,從地址數(shù)組里取地址再跳轉(zhuǎn)。

也就是說(shuō) if 是跳轉(zhuǎn)指令,如果是簡(jiǎn)單的跳轉(zhuǎn)指令的話 CPU 可以利用分支預(yù)測(cè)來(lái)預(yù)執(zhí)行指令,而 switch 是要先根據(jù)值去一個(gè)類似數(shù)組結(jié)構(gòu)找到對(duì)應(yīng)的地址,然后再進(jìn)行跳轉(zhuǎn),這樣的話 CPU 預(yù)測(cè)就幫不上忙了。

然后又因?yàn)橐粋€(gè) channel 建立了之后,超過(guò)99.9%情況它的 state 都是 ChannelState.RECEIVED,因此就把這個(gè)狀態(tài)給挑出來(lái),這樣就能利用 CPU 分支預(yù)測(cè)機(jī)制來(lái)提高代碼的執(zhí)行效率。

并且還給出了 Benchmark 的代碼,就是通過(guò)隨機(jī)生成 100W 個(gè) state,并且 99.99% 是 ChannelState.RECEIVED,然后按照以下兩種方式來(lái)比一比(這 benchSwitch 官網(wǎng)的例子名字打錯(cuò)了,我一開(kāi)始沒(méi)發(fā)現(xiàn)后來(lái)校對(duì)文章才發(fā)現(xiàn))。

為什么程序員喜歡用大量的if else而偏不用switch

雖然博客也給出了它的對(duì)比結(jié)果,但是我還是本地來(lái)跑一下看看結(jié)果如何,其實(shí) JMH 不推薦在 ide 里面跑,但是我懶,直接 idea 里面跑了。

為什么程序員喜歡用大量的if else而偏不用switch

從結(jié)果來(lái)看確實(shí)通過(guò) if 獨(dú)立出來(lái)代碼的執(zhí)行效率更高(注意這里測(cè)的是吞吐),博客還提出了這種技巧可以放在性能要求嚴(yán)格的地方,也就是一般情況下沒(méi)必要這樣特殊做。

至此我們已經(jīng)知道了這個(gè)結(jié)論是對(duì)的,不過(guò)我們還需要深入分析一波,首先得看看 if 和 switch 的執(zhí)行方式到底差別在哪里,然后再看看 CPU 分支預(yù)測(cè)和指令流水線的到底是干啥的,為什么會(huì)有這兩個(gè)東西?

if vs switch

執(zhí)行效率

我們先簡(jiǎn)單來(lái)個(gè)小 demo 看看 if 和 switch 的執(zhí)行效率,其實(shí)就是添加一個(gè)全部是 if else 控制的代碼, switch 和 if + switch 的不動(dòng),看看它們之間對(duì)比效率如何(此時(shí)還是 RECEIVED 超過(guò)99.9%)。

為什么程序員喜歡用大量的if else而偏不用switch

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

來(lái)看一下執(zhí)行的結(jié)果如何:

為什么程序員喜歡用大量的if else而偏不用switch

好家伙,我跑了好幾次,這全 if 的比 if + switch 強(qiáng)不少啊,所以是不是源碼應(yīng)該全改成 if else 的方式,你看這吞吐量又高,還不會(huì)像現(xiàn)在一下 if 一下又 switch 有點(diǎn)不倫不類的樣子。

我又把 state 生成的值改成隨機(jī)的,再來(lái)跑一下看看結(jié)果如何:

為什么程序員喜歡用大量的if else而偏不用switch

我跑了多次還是 if 的吞吐量都是最高的,怎么整這個(gè)全 if 的都是最棒滴。

反編譯 if 和 switch

在我的印象里這個(gè) switch 應(yīng)該是優(yōu)于 if 的,不考慮 CPU 分支預(yù)測(cè)的話,單從字節(jié)碼角度來(lái)說(shuō)是這樣的,我們來(lái)看看各自生成的字節(jié)碼。

switch 的反編譯

先看一下 switch 的反編譯,就截取了關(guān)鍵部分。

為什么程序員喜歡用大量的if else而偏不用switch

也就是說(shuō) switch 生成了一個(gè) tableswitch,上面的 getstatic 拿到值之后可以根據(jù)索引直接查這個(gè) table,然后跳轉(zhuǎn)到對(duì)應(yīng)的行執(zhí)行即可,也就是時(shí)間復(fù)雜度是 O(1)。

比如值是 1 那么直接跳到執(zhí)行 64 行,如果是 4 就直接跳到 100 行。

關(guān)于 switch 還有一些小細(xì)節(jié),當(dāng) swtich 內(nèi)的值不連續(xù)且差距很大的時(shí)候,生成的是 lookupswitch,按網(wǎng)上的說(shuō)法是二分法進(jìn)行查詢(我沒(méi)去驗(yàn)證過(guò)),時(shí)間復(fù)雜度是 O(logn),不是根據(jù)索引直接能找到了,我看生成的 lookup 的樣子應(yīng)該就是二分了,因?yàn)榘粗荡笮∨判蛄恕?/p>

為什么程序員喜歡用大量的if else而偏不用switch

還有當(dāng) switch 里面的值不連續(xù)但是差距比較小的時(shí)候,還是會(huì)生成 tableswtich 不過(guò)填充了一些值,比如這個(gè)例子我 switch 里面的值就 1、3、5、7、9,它自動(dòng)填充了2、4、6、8 都指到 default 所跳的行。

為什么程序員喜歡用大量的if else而偏不用switch

if 的反編譯

讓我們?cè)賮?lái)看看 if 的反編譯結(jié)果

為什么程序員喜歡用大量的if else而偏不用switch

可以看到 if 是每次都會(huì)取出變量和條件進(jìn)行比較,而 switch 則是取一次變量之后查表直接跳到正確的行,從這方面來(lái)看 switch 的效率應(yīng)該是優(yōu)于 if 的。當(dāng)然如果 if 在第一次判斷就過(guò)了的話也就直接 goto 了,不會(huì)再執(zhí)行下面的哪些判斷了。

所以從生成的字節(jié)碼角度來(lái)看 switch 效率應(yīng)該是大于 if 的,但是從測(cè)試結(jié)果的角度來(lái)看 if 的效率又是高于 switch 的,不論是隨機(jī)生成 state,還是 99.99% 都是同一個(gè) state 的情況下。

首先 CPU 分支預(yù)測(cè)的優(yōu)化是肯定的,那關(guān)于隨機(jī)情況下 if 還是優(yōu)于 switch 的話這我就有點(diǎn)不太確定為什么了,可能是 JIT 做了什么優(yōu)化操作,或者是隨機(jī)情況下分支預(yù)測(cè)成功帶來(lái)的效益大于預(yù)測(cè)失敗的情形?

難道是我枚舉值太少了體現(xiàn)不出 switch 的效果?不過(guò)在隨機(jī)情況下 switch 也不應(yīng)該弱于 if 啊,我又加了 7 個(gè)枚舉值,一共 12 個(gè)值又測(cè)試了一遍,結(jié)果如下:

為什么程序員喜歡用大量的if else而偏不用switch

好像距離被拉近了,我看有戲,于是我背了波 26 個(gè)字母,實(shí)不相瞞還是唱著打的字母。

為什么程序員喜歡用大量的if else而偏不用switch

擴(kuò)充了分支的數(shù)量后又進(jìn)行了一波測(cè)試,這次 swtich 爭(zhēng)氣了,終于比 if 強(qiáng)了。

為什么程序員喜歡用大量的if else而偏不用switch

題外話: 我看網(wǎng)上也有對(duì)比 if 和 switch 的,它們對(duì)比出來(lái)的結(jié)果是 switch 優(yōu)于 if,首先 jmh 就沒(méi)寫對(duì),定義一個(gè)常量來(lái)測(cè)試 if 和 switch,并且測(cè)試方法的 result 寫了沒(méi)有消費(fèi),這代碼也不知道會(huì)被 JIT 優(yōu)化成啥樣了,寫了幾十行,可能直接優(yōu)化成 return 某個(gè)值了。

小結(jié)一下測(cè)試結(jié)果

對(duì)比了這么多我們來(lái)小結(jié)一下。

對(duì)于熱點(diǎn)分支

首先對(duì)于熱點(diǎn)分支將其從 switch 提取出來(lái)用 if 獨(dú)立判斷,充分利用 CPU 分支預(yù)測(cè)帶來(lái)的便利確實(shí)優(yōu)于純 swtich,從我們的代碼測(cè)試結(jié)果來(lái)看,大致吞吐量高了兩倍。

在熱點(diǎn)分支的情形下

在熱點(diǎn)分支的情形下改成純 if 判斷而不是 if + swtich的情形下,吞吐量提高的更多。是純 switch 的 3.3 倍,是 if + switch 的 1.6 倍。

在隨機(jī)分支的情形下

在隨機(jī)分支的情形下,三者差別不是很大,但是還是純 if 的情況最優(yōu)秀。

但是從字節(jié)碼角度來(lái)看其實(shí) switch 的機(jī)制效率應(yīng)該更高的,不論是 O(1) 還是 O(logn),但是從測(cè)試結(jié)果的角度來(lái)說(shuō)不是的。

在選擇條件少的情況下 if 是優(yōu)于 switch 的,這個(gè)我不太清楚為什么,可能是在值較少的情況下查表的消耗相比帶來(lái)的收益更大一些?有知道的小伙伴可以在文末留言。

在選擇條件很多的情況下 switch 是優(yōu)于 if 的,再多的選擇值我就沒(méi)測(cè)了,大伙有興趣可以自己測(cè)測(cè),不過(guò)趨勢(shì)就是這樣的。

CPU 分支預(yù)測(cè)

接下來(lái)咱們?cè)賮?lái)看看這個(gè)分支預(yù)測(cè)到底是怎么弄的,為什么會(huì)有分支預(yù)測(cè)這玩意,不過(guò)在談到分支預(yù)測(cè)之前需要先介紹下指令流水線(Instruction pipelining),也就是現(xiàn)代微處理器的 pipeline。

CPU 本質(zhì)就是取指執(zhí)行,而取指執(zhí)行我們來(lái)看下五大步驟,分別是獲取指令(IF)、指令解碼(ID)、執(zhí)行指令(EX)、內(nèi)存訪問(wèn)(MEM)、寫回結(jié)果(WB),再來(lái)看下維基百科上的一個(gè)圖。

為什么程序員喜歡用大量的if else而偏不用switch

當(dāng)然步驟實(shí)際可能更多,反正就是這個(gè)意思需要經(jīng)歷這么多步,所以說(shuō)一次執(zhí)行可以分成很多步驟,那么這么多步驟就可以并行,來(lái)提升處理的效率。

指令流水線

所以說(shuō)指令流水線就是試圖用一些指令使處理器的每一部分保持忙碌,方法是將傳入的指令分成一系列連續(xù)的步驟,由不同的處理器單元執(zhí)行,不同的指令部分并行處理。

就像我們工廠的流水線一樣,我這個(gè)奧特曼的腳拼上去了馬上拼下一個(gè)奧特曼的腳,我可不會(huì)等上一個(gè)奧特曼的都組裝完了再組裝下一個(gè)奧特曼。

為什么程序員喜歡用大量的if else而偏不用switch

當(dāng)然也沒(méi)有這么死板,不一定就是順序執(zhí)行,有些指令在等待而后面的指令其實(shí)不依賴前面的結(jié)果,所以可以提前執(zhí)行,這種叫亂序執(zhí)行。

我們?cè)僬f(shuō)回我們的分支預(yù)測(cè)。

這代碼就像我們的人生一樣總會(huì)面臨著選擇,只有做了選擇之后才知道后面的路怎么走呀,但是事實(shí)上發(fā)現(xiàn)這代碼經(jīng)常走的是同一個(gè)選擇,于是就想出了一個(gè)分支預(yù)測(cè)器,讓它來(lái)預(yù)測(cè)走勢(shì),提前執(zhí)行一路的指令。

為什么程序員喜歡用大量的if else而偏不用switch

那預(yù)測(cè)錯(cuò)了怎么辦?這和咱們?nèi)松灰粯樱梢?strong>把之前執(zhí)行的結(jié)果全拋了然后再來(lái)一遍,但是也有影響,也就是流水線越深,錯(cuò)的越多浪費(fèi)的也就越多,錯(cuò)誤的預(yù)測(cè)延遲是10至20個(gè)時(shí)鐘周期之間,所以還是有副作用的。

簡(jiǎn)單的說(shuō)就是通過(guò)分支預(yù)測(cè)器來(lái)預(yù)測(cè)將來(lái)要跳轉(zhuǎn)執(zhí)行的那些指令,然后預(yù)執(zhí)行,這樣到真正需要它的時(shí)候可以直接拿到結(jié)果了,提升了效率。

分支預(yù)測(cè)

分支預(yù)測(cè)又分了很多種預(yù)測(cè)方式,有靜態(tài)預(yù)測(cè)、動(dòng)態(tài)預(yù)測(cè)、隨機(jī)預(yù)測(cè)等等,從維基百科上看有16種。

為什么程序員喜歡用大量的if else而偏不用switch

靜態(tài)預(yù)測(cè)

我簡(jiǎn)單說(shuō)下我提到的三種,靜態(tài)預(yù)測(cè)就是愣頭青,就和蒙英語(yǔ)選擇題一樣,我管你什么題我都選A,也就是說(shuō)它會(huì)預(yù)測(cè)一個(gè)走勢(shì),一往無(wú)前,簡(jiǎn)單粗暴。

動(dòng)態(tài)預(yù)測(cè)

動(dòng)態(tài)預(yù)測(cè)則會(huì)根據(jù)歷史記錄來(lái)決定預(yù)測(cè)的方向,比如前面幾次選擇都是 true ,那我就走 true 要執(zhí)行的這些指令,如果變了最近幾次都是 false ,那我就變成 false 要執(zhí)行的這些指令,其實(shí)也是利用了局部性原理。

隨機(jī)預(yù)測(cè)

隨機(jī)預(yù)測(cè)看名字就知道了,這是蒙英語(yǔ)選擇題的另一種方式,瞎猜,隨機(jī)選一個(gè)方向直接執(zhí)行。

還有很多就不一一列舉了,各位有興趣自行去研究,順便提一下在 2018 年谷歌的零項(xiàng)目和其他研究人員公布了一個(gè)名為 Spectre 的災(zāi)難性安全漏洞,其可利用 CPU 的分支預(yù)測(cè)執(zhí)行泄漏敏感信息,這里就不展開(kāi)了,文末會(huì)附上鏈接。

之后又有個(gè)名為 BranchScope 的攻擊,也是利用預(yù)測(cè)執(zhí)行,所以說(shuō)每當(dāng)一個(gè)新的玩意出來(lái)總是會(huì)帶來(lái)利弊。

至此我們已經(jīng)知曉了什么叫指令流水線和分支預(yù)測(cè)了,也理解了 Dubbo 為什么要這么優(yōu)化了,但是文章還沒(méi)有結(jié)束,我還想提一提這個(gè) stackoverflow 非常有名的問(wèn)題,看看這數(shù)量。

為什么程序員喜歡用大量的if else而偏不用switch

為什么處理有序數(shù)組要比非有序數(shù)組快?

這個(gè)問(wèn)題在那篇博客開(kāi)頭就被提出來(lái)了,很明顯這也是和分支預(yù)測(cè)有關(guān)系,既然看到了索性就再分析一波,大伙可以在腦海里先回答一下這個(gè)問(wèn)題,畢竟咱們都知道答案了,看看思路清晰不。

就是下面這段代碼,數(shù)組排序了之后循環(huán)的更快。

為什么程序員喜歡用大量的if else而偏不用switch

然后各路大神就蹦出來(lái)了,我們來(lái)看一下首贊的大佬怎么說(shuō)的。

一開(kāi)口就是,直擊要害。

You are a victim of branch prediction fail.

緊接著就上圖了,一看就是老司機(jī)。

為什么程序員喜歡用大量的if else而偏不用switch

他說(shuō)讓我們回到 19世紀(jì),一個(gè)無(wú)法遠(yuǎn)距離交流且無(wú)線電還未普及的時(shí)候,如果是你這個(gè)鐵路交叉口的扳道工,當(dāng)火車快來(lái)的時(shí)候,你如何得知該扳哪一邊?

火車停車再重啟的消耗是很大的,每次到分叉口都停車,然后你問(wèn)他,哥們?nèi)ツ陌?,然后扳了道,再重啟就很耗時(shí),怎么辦?猜!

為什么程序員喜歡用大量的if else而偏不用switch

猜對(duì)了火車就不用停,繼續(xù)開(kāi)。猜錯(cuò)了就停車然后倒車然后換道再開(kāi)。

所以就看猜的準(zhǔn)不準(zhǔn)了!搏一搏單車變摩托。

然后大佬又指出了關(guān)鍵代碼對(duì)應(yīng)的匯編代碼,也就是跳轉(zhuǎn)指令了,這對(duì)應(yīng)的就是火車的岔口,該選條路了。

為什么程序員喜歡用大量的if else而偏不用switch

后面我就不分析了,大伙兒應(yīng)該都知道了,排完序的數(shù)組執(zhí)行到值大于 128 的之后肯定全部大于128了,所以每次分支預(yù)測(cè)的結(jié)果都是對(duì)了!所以執(zhí)行的效率很高。

而沒(méi)排序的數(shù)組是亂序的,所以很多時(shí)候都會(huì)預(yù)測(cè)錯(cuò)誤,而預(yù)測(cè)錯(cuò)誤就得指令流水線排空啊,然后再來(lái)一遍,這速度當(dāng)然就慢了。

所以大佬說(shuō)這個(gè)題主你是分支預(yù)測(cè)錯(cuò)誤的受害者。

最終大佬給出的修改方案是咱不用 if 了,惹不起咱還躲不起嘛?直接利用位運(yùn)算來(lái)實(shí)現(xiàn)這個(gè)功能,具體我就不分析了,給大家看下大佬的建議修改方案。

為什么程序員喜歡用大量的if else而偏不用switch

而 swtich 從字節(jié)碼上看是優(yōu)于 if 的,但是從測(cè)試結(jié)果來(lái)看在分支很多的情況下能顯示出優(yōu)勢(shì),一般情況下還是打不過(guò) if 。

然后也知曉了什么叫指令流水線,這其實(shí)就是結(jié)合實(shí)際了,流水線才夠快呀,然后分支預(yù)測(cè)預(yù)執(zhí)行也是一個(gè)提高效率的方法,當(dāng)然得猜的對(duì),不然分支預(yù)測(cè)錯(cuò)誤的副作用還是無(wú)法忽略的,所以對(duì)分支預(yù)測(cè)器的要求也是很高的。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。

網(wǎng)頁(yè)名稱:為什么程序員喜歡用大量的ifelse而偏不用switch
網(wǎng)頁(yè)鏈接:http://www.rwnh.cn/article4/ggojie.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開(kāi)發(fā)、響應(yīng)式網(wǎng)站、企業(yè)網(wǎng)站制作、網(wǎng)站收錄網(wǎng)站制作、關(guān)鍵詞優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)
凤冈县| 沙田区| 长垣县| 苏尼特右旗| 武隆县| 廊坊市| 潜江市| 洛浦县| 和龙市| 西乌珠穆沁旗| 陈巴尔虎旗| 灌南县| 巴里| 杂多县| 武穴市| 揭阳市| 林西县| 扬中市| 屏东市| 武邑县| 樟树市| 治县。| 杭锦后旗| 剑川县| 广灵县| 鲁甸县| 大新县| 壤塘县| 界首市| 黄石市| 驻马店市| 龙泉市| 图们市| 新营市| 黄大仙区| 航空| 瓮安县| 克山县| 遂溪县| 安新县| 潞城市|