99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

if-else的效率有多低你知道嗎?

Q4MP_gh_c472c21 ? 來源:程序喵大人 ? 作者:程序喵大人 ? 2021-02-02 10:30 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

首先看一段經(jīng)典的代碼,并統(tǒng)計它的執(zhí)行時間:

// test_predict.cc#include 《algorithm》#include 《ctime》#include 《iostream》

int main() { const unsigned ARRAY_SIZE = 50000; int data[ARRAY_SIZE]; const unsigned DATA_STRIDE = 256;

for (unsigned c = 0; c 《 ARRAY_SIZE; ++c) data[c] = std::rand() % DATA_STRIDE;

std::sort(data, data + ARRAY_SIZE);

{ // 測試部分 clock_t start = clock(); long long sum = 0;

for (unsigned i = 0; i 《 100000; ++i) { for (unsigned c = 0; c 《 ARRAY_SIZE; ++c) { if (data[c] 》= 128) sum += data[c]; } }

double elapsedTime = static_cast《double》(clock() - start) / CLOCKS_PER_SEC;

std::cout 《《 elapsedTime 《《 “

”; std::cout 《《 “sum = ” 《《 sum 《《 “

”; } return 0;}~/test$ g++ test_predict.cc ;。/a.out7.95312sum = 480124300000

此程序的執(zhí)行時間是7.9秒,如果把排序那一行代碼注釋掉,即

// std::sort(data, data + ARRAY_SIZE);

結(jié)果為:

~/test$ g++ test_predict.cc ;。/a.out24.2188sum = 480124300000

改動后的程序執(zhí)行時間變?yōu)榱?4秒。

其實只改動了一行代碼,程序執(zhí)行時間卻有3倍的差距,而且看上去數(shù)組是否排序與程序執(zhí)行速度貌似沒什么關(guān)系,這里面其實涉及到CPU分支預(yù)測的知識點。

提到分支預(yù)測,首先要介紹一個概念:流水線。

拿理發(fā)舉例,小理發(fā)店一般都是一個人工作,一個人洗剪吹一肩挑,而大理發(fā)店分工明確,洗剪吹都有特定的員工,第一個人在剪發(fā)的時候,第二個人就可以洗頭了,第一個人剪發(fā)結(jié)束吹頭發(fā)的時候,第二個人可以去剪發(fā),第三個人就可以去洗頭了,極大的提高了效率。

這里的洗剪吹就相當(dāng)于是三級流水線,在CPU架構(gòu)中也有流水線的概念,如圖:

6d2c97d0-624f-11eb-8b86-12bb97331649.png

在執(zhí)行指令的時候一般有以下幾個過程:

取指:Fetch

譯指:Decode

執(zhí)行:execute

回寫:Write-back

流水線架構(gòu)可以更好的壓榨流水線上的四個員工,讓他們不停的工作,使指令執(zhí)行的效率更高。

再談分支預(yù)測,舉個經(jīng)典的例子:

火車高速行駛的過程中遇到前方有個岔路口,假設(shè)火車內(nèi)沒有任何通訊手段,那火車就需要在岔路口前停下,下車詢問別人應(yīng)該選擇哪條路走,弄清楚路線后后再重新啟動火車?yán)^續(xù)行駛。高速行駛的火車慢速停下,再重新啟動后加速,可以想象這個過程浪費了多少時間。

有個辦法,火車在遇到岔路口前可以猜一條路線,到路口時直接選擇這條路行駛,如果經(jīng)過多個岔路口,每次做出選擇時都能選擇正確的路口行駛,這樣火車一路上都不需要減速,速度自然非常快。但如果火車開過頭才發(fā)現(xiàn)走錯路了,就需要倒車回到岔路口,選擇正確的路口繼續(xù)行駛,速度自然下降很多。所以預(yù)測的成功率非常重要,因為預(yù)測失敗的代價較高,預(yù)測成功則一帆風(fēng)順。

計算機的分支預(yù)測就如同火車行駛中遇到了岔路口,預(yù)測成功則程序的執(zhí)行效率大幅提高,預(yù)測失敗程序的執(zhí)行效率則大幅下降。

CPU都是多級流水線架構(gòu)運行,如果分支預(yù)測成功,很多指令都提前進入流水線流程中,則流水線中指令運行的非常順暢,而如果分支預(yù)測失敗,則需要清空流水線中的那些預(yù)測出來的指令,重新加載正確的指令到流水線中執(zhí)行,然而現(xiàn)代CPU的流水線級數(shù)非常長,分支預(yù)測失敗會損失10-20個左右的時鐘周期,因此對于復(fù)雜的流水線,好的分支預(yù)測方法非常重要。

預(yù)測方法主要分為靜態(tài)分支預(yù)測和動態(tài)分支預(yù)測:

靜態(tài)分支預(yù)測:聽名字就知道,該策略不依賴執(zhí)行環(huán)境,編譯器在編譯時就已經(jīng)對各個分支做好了預(yù)測。

動態(tài)分支預(yù)測:即運行時預(yù)測,CPU會根據(jù)分支被選擇的歷史紀(jì)錄進行預(yù)測,如果最近多次都走了這個路口,那CPU做出預(yù)測時會優(yōu)先考慮這個路口。

tips:這里只是簡單的介紹了分支預(yù)測的方法,更多的分支預(yù)測方法資料大家可關(guān)注公眾號回復(fù)分支預(yù)測關(guān)鍵字領(lǐng)取。

了解了分支預(yù)測的概念,我們回到最開始的問題,為什么同一個程序,排序和不排序的執(zhí)行速度相差那么多。

因為程序中有個if條件判斷,對于不排序的程序,數(shù)據(jù)散亂分布,CPU進行分支預(yù)測比較困難,預(yù)測失敗的頻率較高,每次失敗都會浪費10-20個時鐘周期,影響程序運行的效率。而對于排序后的數(shù)據(jù),CPU根據(jù)歷史記錄比較好判斷即將走哪個分支,大概前一半的數(shù)據(jù)都不會進入if分支,后一半的數(shù)據(jù)都會進入if分支,預(yù)測的成功率非常高,所以程序運行速度很快。

如何解決此問題?總體思路肯定是在程序中盡量減少分支的判斷,方法肯定是具體問題具體分析了,對于該示例程序,這里提供兩個思路削減if分支。

方法一:使用位操作:

int t = (data[c] - 128) 》》 31;sum += ~t & data[c];

方法二:使用表結(jié)構(gòu):

#include 《algorithm》#include 《ctime》#include 《iostream》

int main() { const unsigned ARRAY_SIZE = 50000; int data[ARRAY_SIZE]; const unsigned DATA_STRIDE = 256;

for (unsigned c = 0; c 《 ARRAY_SIZE; ++c) data[c] = std::rand() % DATA_STRIDE;

int lookup[DATA_STRIDE]; for (unsigned c = 0; c 《 DATA_STRIDE; ++c) { lookup[c] = (c 》= 128) ? c : 0; }

std::sort(data, data + ARRAY_SIZE);

{ // 測試部分 clock_t start = clock(); long long sum = 0;

for (unsigned i = 0; i 《 100000; ++i) { for (unsigned c = 0; c 《 ARRAY_SIZE; ++c) { // if (data[c] 》= 128) sum += data[c]; sum += lookup[data[c]]; } }

double elapsedTime = static_cast《double》(clock() - start) / CLOCKS_PER_SEC; std::cout 《《 elapsedTime 《《 “

”; std::cout 《《 “sum = ” 《《 sum 《《 “

”; } return 0;}

其實Linux中有一些工具可以檢測出分支預(yù)測成功的次數(shù),有valgrind和perf,使用方式如圖:

6da42ab6-624f-11eb-8b86-12bb97331649.png

圖片截自下方參考資料中

條件分支的使用會影響程序執(zhí)行的效率,我們平時開發(fā)過程中應(yīng)該盡可能減少在程序中隨意使用過多的分支,能避免則避免。

更多的分支預(yù)測方法資料大家可關(guān)注公眾號回復(fù)分支預(yù)測關(guān)鍵字領(lǐng)取。

拓展閱讀:

《虛函數(shù)真的就那么慢嗎?它的開銷究竟在哪里?來看這4段代碼!》

想必很多人都聽說過虛函數(shù)開銷大,貌似很多答案都說是因為虛函數(shù)表導(dǎo)致的那一次間接調(diào)用,真的如此嗎?

直接看下面這兩段代碼:

#include 《cmath》#include “timer.h”struct Base { public: virtual int f(double i1, int i2) { return static_cast《int》(i1 * log(i1)) * i2; }};

int main() { TimerLog t(“timer”); Base *a = new Base(); int ai = 0; for (int i = 0; i 《 1000000000; i++) { ai += a-》f(i, 10); } cout 《《 ai 《《 endl;}

執(zhí)行時間:12.895s

#include 《cmath》#include “timer.h”struct Base { public: int f(double i1, int i2) { return static_cast《int》(i1 * log(i1)) * i2; }};

int main() { TimerLog t(“timer”); Base *a = new Base(); int ai = 0; for (int i = 0; i 《 1000000000; i++) { ai += a-》f(i, 10); } cout 《《 ai 《《 endl;}

執(zhí)行時間:12.706s

這兩段代碼的執(zhí)行時間幾乎沒有區(qū)別,可見虛函數(shù)表導(dǎo)致的那一次函數(shù)間接調(diào)用并不浪費時間,所以虛函數(shù)的開銷并不在重定向上,這一次重定向基本上不影響程序性能。

那它的開銷究竟在哪里呢?看下面兩段代碼,這兩段代碼和上面相比只改動了一行:

#include 《cmath》#include “timer.h”struct Base { public: virtual int f(double i1, int i2) { return static_cast《int》(i1 * log(i1)) * i2; }};

int main() { TimerLog t(“timer”); Base *a = new Base(); int ai = 0; for (int i = 0; i 《 1000000000; i++) { ai += a-》f(10, i); // 這里有改動 } cout 《《 ai 《《 endl;}

執(zhí)行時間:436ms

#include 《cmath》#include “timer.h”struct Base { public: int f(double i1, int i2) { return static_cast《int》(i1 * log(i1)) * i2; }};

int main() { TimerLog t(“timer”); Base *a = new Base(); int ai = 0; for (int i = 0; i 《 1000000000; i++) { ai += a-》f(10, i); // 這里有改動 } cout 《《 ai 《《 endl;}

執(zhí)行時間154ms

這里看到,僅僅改變了一行代碼,虛函數(shù)調(diào)用就比普通函數(shù)慢了幾倍,為什么?

虛函數(shù)其實最主要的性能開銷在于它阻礙了編譯器內(nèi)聯(lián)函數(shù)和各種函數(shù)級別的優(yōu)化,導(dǎo)致性能開銷較大,在普通函數(shù)中l(wèi)og(10)會被優(yōu)化掉,它就只會被計算一次,而如果使用虛函數(shù),log(10)不會被編譯器優(yōu)化,它就會被計算多次。如果代碼中使用了更多的虛函數(shù),編譯器能優(yōu)化的代碼就越少,性能就越低。

虛函數(shù)通常通過虛函數(shù)表來實現(xiàn),在虛表中存儲函數(shù)指針,實際調(diào)用時需要間接訪問,這需要多一點時間。

然而這并不是虛函數(shù)速度慢的主要原因,真正原因是編譯器在編譯時通常并不知道它將要調(diào)用哪個函數(shù),所以它不能被內(nèi)聯(lián)優(yōu)化和其它很多優(yōu)化,因此就會增加很多無意義的指令(準(zhǔn)備寄存器、調(diào)用函數(shù)、保存狀態(tài)等),而且如果虛函數(shù)有很多實現(xiàn)方法,那分支預(yù)測的成功率也會降低很多,分支預(yù)測錯誤也會導(dǎo)致程序性能下降。

如果你想要寫出高性能代碼并頻繁的調(diào)用虛函數(shù),注意如果用其它的方式(例如if-else、switch、函數(shù)指針等)來替換虛函數(shù)調(diào)用并不能根本解決問題,它還有可能會更慢,真正的問題不是虛函數(shù),而是那些不必要的間接調(diào)用。

正常的函數(shù)調(diào)用:

復(fù)制棧上的一些寄存器,以允許被調(diào)用的函數(shù)使用這些寄存器;

將參數(shù)復(fù)制到預(yù)定義的位置,這樣被調(diào)用的函數(shù)可以找到對應(yīng)參數(shù);

入棧返回地址;

跳轉(zhuǎn)到函數(shù)的代碼,這是一個編譯時地址,因為編譯器/鏈接器硬編碼為二進制;

從預(yù)定義的位置獲取返回值,并恢復(fù)想要使用的寄存器。

而虛函數(shù)調(diào)用與此完全相同,唯一的區(qū)別就是編譯時不知道函數(shù)的地址,而是:

從對象中獲取虛表指針,該指針指向一個函數(shù)指針數(shù)組,每個指針對應(yīng)一個虛函數(shù);

從虛表中獲取正確的函數(shù)地址,放到寄存器中;

跳轉(zhuǎn)到該寄存器中的地址,而不是跳轉(zhuǎn)到一個硬編碼的地址。

通常,使用虛函數(shù)沒問題,它的性能開銷也不大,而且虛函數(shù)在面向?qū)ο蟠a中有強大的作用。

但是不能無腦使用虛函數(shù),特別是在性能至關(guān)重要的或者底層代碼中,而且大項目中使用多態(tài)也會導(dǎo)致繼承層次很混亂。

那么有什么好方法替代虛函數(shù)呢?這里提供幾個思路,讀者請持續(xù)關(guān)注,后續(xù)會具體講解:

使用訪問者模式來使類層次結(jié)構(gòu)可擴展;

使用普通模板替代繼承和虛函數(shù);

C++20中的concepts用來替代面向?qū)ο蟠a;

使用variants替代虛函數(shù)或模板方法。

這幾種方法是Michael Spertus大佬介紹的,各有各的優(yōu)缺點,作者都會用,但什么情況下使用哪個,取決于你自己的判斷,這里只是教你了一個工具,什么時候用都取決于你自己。

原文標(biāo)題:少寫點if-else吧,它的效率有多低你知道嗎?

文章出處:【微信公眾號:嵌入式ARM】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11080

    瀏覽量

    217108
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3689

    瀏覽量

    95260
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4900

    瀏覽量

    70751

原文標(biāo)題:少寫點if-else吧,它的效率有多低你知道嗎?

文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    知道船用變壓器哪些嗎?

    在船舶和海洋平臺上,電力系統(tǒng)的穩(wěn)定運行至關(guān)重要,而船用變壓器作為其中的關(guān)鍵設(shè)備,其種類繁多,各具特點。知道船用變壓器哪些嗎?讓我們一起來揭開它們的神秘面紗。CSD船用變壓器是船舶供電系統(tǒng)中
    的頭像 發(fā)表于 06-01 00:00 ?175次閱讀
    <b class='flag-5'>你</b><b class='flag-5'>知道</b>船用變壓器<b class='flag-5'>有</b>哪些嗎?

    超低功耗藍牙模組的功耗到底

    對于BLE(低功耗藍牙)產(chǎn)品方案來說,功耗,決定著的產(chǎn)品待機、使用壽命是多久,或者是多久需要充一次電,推薦一個非常的好用的評估nordicBLE功耗的工具: 利用我們的這個工具
    發(fā)表于 05-27 22:37

    實測 PTR54LS05低功耗到底

    實測 PTR54LS05低功耗到底?
    發(fā)表于 04-27 10:57

    、中、高頻段 (LMH) 模/頻段功率放大器模塊 skyworksinc

    電子發(fā)燒友網(wǎng)為提供()、中、高頻段 (LMH) 模/頻段功率放大器模塊相關(guān)產(chǎn)品參數(shù)、數(shù)據(jù)手冊,更有、中、高頻段 (LMH)
    發(fā)表于 04-11 18:32
    <b class='flag-5'>低</b>、中、高頻段 (LMH) <b class='flag-5'>多</b>模/<b class='flag-5'>多</b>頻段功率放大器模塊 skyworksinc

    LED戶外顯示屏的五大優(yōu)勢,知道嗎?

    LED戶外顯示屏的五大優(yōu)勢,知道嗎? LED戶外顯示屏在城市的夜晚中扮演著越來越重要的角色,其鮮艷的色彩、生動的畫面為城市增添了一抹亮色。那么,LED戶外顯示屏的顯示效果到底如何呢?讓我們一起
    的頭像 發(fā)表于 01-06 18:20 ?811次閱讀

    SHS1000 X系列手持示波表基礎(chǔ)介紹

    SHS1000X手持示波表的使用方法知道嗎?
    的頭像 發(fā)表于 11-22 09:20 ?492次閱讀
    SHS1000 X系列手持示波表基礎(chǔ)介紹

    這些電源常用仿真軟件,知道嗎

    ,便于工程師驗證分析。 電源仿真為電源系統(tǒng)開發(fā)帶來很多便利 : ▍電源的設(shè)計與分析: 在電源方案制定過程中,可利用電源仿真手段輔助電源電路設(shè)計與分析,提升電源方案的制定效率與設(shè)計可靠性; ▍電源性能
    發(fā)表于 10-25 14:20

    晶振怎么用,真的知道嗎

    導(dǎo)讀晶振應(yīng)該是陪伴我們最多而我們卻并非那么熟悉的元器件之一,其頻率對于電路的運作很重要,今天我們詳細介紹晶振的諧振頻率調(diào)整與常見應(yīng)用。諧振頻率的調(diào)整通常石英晶體產(chǎn)品給出的標(biāo)稱頻率是外接一個小電容Cs(在石英晶體產(chǎn)品的技術(shù)手冊中常稱為負載電容)時校正的振蕩頻率,Cs與石英晶體串接如圖1所示。利用Cs可使石英晶體的諧振頻率在一個小范圍內(nèi)調(diào)整。Cs的值應(yīng)選擇得比C
    的頭像 發(fā)表于 10-22 08:03 ?903次閱讀
    晶振怎么用,<b class='flag-5'>你</b>真的<b class='flag-5'>知道嗎</b>?

    電源的這些常識知道嗎?

    因為電源部件是消耗品,導(dǎo)致電源一定的使用壽命。因此,使用一定時間后,我們或多或少會出現(xiàn)一些異常現(xiàn)象。一些簡單的學(xué)生可以簡單地自己解決。然而,許多情況下,涉及技術(shù)問題的人無法開始。這時,我們需要知道
    的頭像 發(fā)表于 09-26 13:58 ?561次閱讀

    知道嗎:怎么選擇RC通濾波阻容值?

    RC低通濾波器是再常見不過的濾波電路了,只要一個電阻和一個電容就可以實現(xiàn)通濾波,在實際工程中,往往是只一個公式來約束電阻和電容,即截止頻率的計算公式: fc=1/(2πRC)
    的頭像 發(fā)表于 09-24 14:24 ?2342次閱讀
    <b class='flag-5'>你</b><b class='flag-5'>知道嗎</b>:怎么選擇RC<b class='flag-5'>低</b>通濾波阻容值?

    在光端機應(yīng)用中,晶振的核心作用你知道嗎

    在光端機應(yīng)用中,晶振的核心作用你知道嗎?隨著信息技術(shù)的發(fā)展,光纖通信在現(xiàn)代通信領(lǐng)域中扮演著重要的角色。而光端機在光纖通信系統(tǒng)中起著關(guān)鍵的作用,實現(xiàn)了光纖通信的高速、遠距離傳輸和協(xié)議兼容性,為現(xiàn)代
    發(fā)表于 09-23 11:26 ?0次下載

    物聯(lián)網(wǎng)設(shè)備也需要雙卡嗎?

    知道嗎?實際應(yīng)用中,物聯(lián)網(wǎng)設(shè)是必需的!
    的頭像 發(fā)表于 09-21 08:12 ?541次閱讀
    物聯(lián)網(wǎng)設(shè)備也需要雙卡嗎?

    隨意更換熱敏電阻什么后果知道嗎?

    國內(nèi)外飲水習(xí)慣不同,因歷史原因國內(nèi)養(yǎng)成喝開水習(xí)慣。電熱水壺成為居家必備,其關(guān)鍵元件熱敏電阻用于調(diào)節(jié)溫度。熱敏電阻出問題更換熱敏電阻要謹(jǐn)慎注意,否則可能導(dǎo)致電路性能下降、設(shè)備故障等問題。
    的頭像 發(fā)表于 08-23 14:21 ?649次閱讀
    隨意更換熱敏電阻<b class='flag-5'>有</b>什么后果<b class='flag-5'>你</b><b class='flag-5'>知道嗎</b>?

    隨意更換熱敏電阻什么后果知道嗎?

    國內(nèi)外飲水習(xí)慣不同,因歷史原因國內(nèi)養(yǎng)成喝開水習(xí)慣。電熱水壺成為居家必備,其關(guān)鍵元件熱敏電阻用于調(diào)節(jié)溫度。熱敏電阻出問題更換熱敏電阻要謹(jǐn)慎注意,否則可能導(dǎo)致電路性能下降、設(shè)備故障等問題。
    的頭像 發(fā)表于 08-23 11:42 ?733次閱讀
    隨意更換熱敏電阻<b class='flag-5'>有</b>什么后果<b class='flag-5'>你</b><b class='flag-5'>知道嗎</b>?

    知道影響貼片電感發(fā)熱的因素有哪些嗎?

    知道影響貼片電感發(fā)熱的因素有哪些嗎?
    的頭像 發(fā)表于 08-17 14:24 ?884次閱讀
    <b class='flag-5'>你</b><b class='flag-5'>知道</b>影響貼片電感發(fā)熱的因素有哪些嗎?