自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

我們要先實現(xiàn)業(yè)務(wù)功能,還是先優(yōu)化代碼?

開發(fā) 前端
在日常的代碼開發(fā)里,由于偷懶不想寫一個組合的邏輯表達式,從而增加分支邏輯的現(xiàn)象,實際上是較為常見的。

在做軟件設(shè)計咨詢工作時,我常常發(fā)現(xiàn):許多高性能軟件產(chǎn)品的研發(fā)團隊,在軟件開發(fā)階段,僅僅關(guān)注并實現(xiàn)業(yè)務(wù)的特性功能。待功能交付后,才花費大量時間對軟件代碼進行調(diào)整優(yōu)化。

而且,在與這些程序員接觸的過程中,我還觀察到一個有趣的現(xiàn)象:大家普遍認為,在軟件編碼實現(xiàn)階段,過早考慮代碼優(yōu)化意義不大,應(yīng)等到功能開發(fā)完成,再基于打點 Profiling(數(shù)據(jù)分析)去優(yōu)化代碼實現(xiàn)。

其實,這個想法是否可取,也曾困擾過我。然而,在經(jīng)歷眾多由低級編碼導(dǎo)致的性能問題后,我發(fā)現(xiàn)高性能編碼實現(xiàn)極具價值,且能讓我更好地處理編碼實現(xiàn)優(yōu)化與 Profiling 優(yōu)化之間的關(guān)系。

建立正確的高性能編碼價值觀

首先,提及高性能編碼,想必您肯定聽說過現(xiàn)代計算機科學(xué)的鼻祖高德納(Donald Knuth)的那句名言:“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.” 意思是:我們應(yīng)該忘掉那些效率低下的事情,告誡自己在 97% 的情況下:過早優(yōu)化是萬惡之源。但是,我們也不應(yīng)該在關(guān)鍵的 3% 上錯過優(yōu)化機會?!禖omputer Programming as an Art (1974)》P671。

不過我認為,或許很多程序員僅僅記住了這句話的前半部分,即 “97% 的情況下,過早優(yōu)化是萬惡之源”,卻沒有留意到這句話還有后半句:我們不應(yīng)該放棄掉那關(guān)鍵的 3% 的優(yōu)化機會。

所以,由此造成的后果是:過度推崇不要對代碼進行提前優(yōu)化,并將此當(dāng)作編寫低性能軟件代碼的借口。也就是說,當(dāng)下我們在軟件編碼過程中所遇到的大多數(shù)問題,并非由過早優(yōu)化所致,而是因為在編寫代碼時對執(zhí)行效率缺乏關(guān)注所引發(fā)的。

其實,在編寫代碼階段樹立追求高性能實現(xiàn)的意識極為重要,主要有兩方面原因。

第一個原因在于,或許原本只是一個細微的編碼問題,卻有可能引發(fā)軟件較大的性能問題。例如,我曾經(jīng)參與的一個 C++ 高性能軟件開發(fā)項目,由于一位研發(fā)人員在編碼時不慎遺漏了函數(shù)行參的引用符號,致使函數(shù)調(diào)用開銷增大,軟件版本性能顯著下降。而且這個問題較為隱蔽,我們后續(xù)耗費了大量精力,在代碼中增添了眾多定位手段,才得以發(fā)現(xiàn)問題。

第二個原因是,一旦錯失高性能編碼的時機,將性能問題遺留到軟件生命周期的后期,很可能因為錯過了當(dāng)時編寫代碼的具體情境,后續(xù)就難以再察覺這個問題。在此,我再為您舉個例子加以說明。

可以看到,在如下所示的代碼片段中,這個類的實例在接收數(shù)據(jù)之后,會更新各個 Channel 中的數(shù)據(jù)量大小,而后對外提供了一個方法,用于判斷所有通道中是否存在數(shù)據(jù)

public class ChannelGroup {
class Channel{
public String channelname;
public int dataSize;
    }


    Channel[] channels;


public Channels() {
        channels = new Channel[10]; 
    }


public receiveData(...)  {....}  // 收到數(shù)據(jù)更新Channel中信息,省略
public boolean hasData() {   // 判斷所有通道是否有數(shù)據(jù)。
for (Channel Channel : Channels) {
if (Channel.dataSize > 0){
return true;
            }
        }
return false;
    }
}

那么在看完這段代碼之后,您認為這段代碼實現(xiàn)中的方法 hasData ,能算作高性能實現(xiàn)嗎?倘若僅依據(jù)這段代碼實現(xiàn)來加以分析,會覺得它似乎不存在性能問題。畢竟,對于一個僅有 10 個元素的數(shù)組而言,運用二分法查找來提高查找速度的必要性并非很大。

好,我們暫且如此認為,接著再做一個假設(shè):在真實的編寫代碼過程中,存在這樣一個潛在的上下文信息,即在絕大多數(shù)業(yè)務(wù)場景下,是第三個通道收到數(shù)據(jù)。那么針對這種情況,如果再采用從前向后的順序遍歷,必然不是性能最佳的實現(xiàn)方式,而應(yīng)當(dāng)先判斷第三個通道中的數(shù)據(jù)。

所以通過這兩個例子,您應(yīng)該明白了,如果在編碼實現(xiàn)階段并非從高性能實現(xiàn)的角度出發(fā),而是打算在后續(xù)通過打點數(shù)據(jù)分析來優(yōu)化解決問題,幾乎是不太可行的。

實際上,依我的思考和實踐經(jīng)驗而言,在開發(fā)一個高性能軟件系統(tǒng)時,在編碼階段考慮高性能的實現(xiàn)方法,與完成業(yè)務(wù)功能后再進行代碼調(diào)優(yōu),兩者并不沖突,應(yīng)當(dāng)同等重視。因為前期的高性能編碼實現(xiàn)過程,大多由人主觀把控,所以可能會因判斷失誤或者實現(xiàn)過程中的疏忽,引入一些低效率的代碼實現(xiàn)。如此一來,后期通過熱點代碼分析以及代碼調(diào)優(yōu)的過程,是不可省略的。

而且說實話,在我心中,優(yōu)秀的軟件代碼應(yīng)當(dāng)兼具代碼簡潔與性能,倘若對編碼性能嗤之以鼻,我認為這樣的程序員通常也寫不出高質(zhì)量的代碼。

好了,在理解了應(yīng)當(dāng)如何看待高性能編碼之后,接下來的問題便是,如何才能掌握實現(xiàn)高性能編碼的方法,下面我們就具體來瞧瞧。

高性能編碼實現(xiàn)方法

其實在軟件開發(fā)的進程中,高性能編碼實現(xiàn)的方法與技術(shù)繁多,不同的編程語言之間還會存在一定差異,很難在一節(jié)課里介紹周全。

所以,今天我主要從編寫的代碼映射到執(zhí)行過程的視角,為您介紹四種高性能編碼實現(xiàn)方法,以及相應(yīng)的實現(xiàn)原則和手段,分別是循環(huán)實現(xiàn)、函數(shù)方法實現(xiàn)、表達式實現(xiàn)以及控制流程實現(xiàn)。

在實際的軟件編碼過程中,您也能夠依照這樣的角度和思路,嘗試去理解與分析軟件代碼的運行態(tài)過程,逐步積累并完善高性能編碼的實現(xiàn)技巧。

好,接下來我們就從循環(huán)實現(xiàn)入手,來瞧瞧這種高性能實現(xiàn)的原則和方法。

高性能循環(huán)實現(xiàn)

我們都清楚,在編寫代碼時,循環(huán)體內(nèi)的代碼會被多次執(zhí)行,因而其代碼開銷會被放大,常常會出現(xiàn)在熱點代碼當(dāng)中。也就是說,怎樣實現(xiàn)高效循環(huán)是達成高性能編碼最為關(guān)鍵的一步。

那么,編寫高效循環(huán)代碼的重要參考原則有哪些呢?我覺得主要有兩個,下面我們具體來了解一下。

第一點,盡量避免對循環(huán)起始條件和終止條件的重復(fù)計算。為了讓您更輕松地理解這個原則,我先帶您來看一個高效循環(huán)的反面例子。在下面這個代碼示例中,所實現(xiàn)的功能是循環(huán)遍歷并更新字符串中的值,您會發(fā)現(xiàn),在循環(huán)執(zhí)行的過程中,strlen 被調(diào)用了多次,所以性能較低。

void updateStr(char* str)
{
for(int i = 0; i<strlen(str); i++)
    {
        str[i]= '*';
    }
}

那么,針對這種情況,我們就應(yīng)該在循環(huán)開始時,將字符串長度值保存在一個變量中,從而避免重復(fù)計算。修改好的代碼如下:

void updateStr(char* str)
{
int length = strlen(str);
for(int i = 0; i< length; i++)
    {
        str[i]= '*';
    }
}

第二點,盡量避免循環(huán)體中存在重復(fù)的計算邏輯。

我們同樣也來看一個反模式的代碼示例。在下面這段代碼的實現(xiàn)過程中,x*y的值并沒有發(fā)生變化,但是在循環(huán)體中被執(zhí)行了很多遍。

void initData(int[] data, int length, int x, int y){
for(int i = 0; i < length; i++)
    {
        data[i] = x * y + 100;
    }
}

因此,從高性能編碼實現(xiàn)的角度出發(fā),我們能夠?qū)?nbsp;x*y 值的計算過程遷移至循環(huán)體之外,以此降低這部分的冗余計算開銷。實際上到這里,您可以記住這么一句話:編寫高效循環(huán)代碼的本質(zhì),就是盡可能讓循環(huán)體中執(zhí)行的代碼越少越好,剔除掉所有能夠冗余的重復(fù)計算。

那么在具體的代碼實現(xiàn)里,需要檢查的循環(huán)優(yōu)化點其實還有眾多。例如,您還需要檢查是否存在重復(fù)的函數(shù)調(diào)用、多余的對象申請和構(gòu)造、多余的局部變量定義等等。所以,在編寫循環(huán)代碼時,您需要留意識別并剝離出此類代碼實現(xiàn)。

高性能函數(shù)方法實現(xiàn)

實現(xiàn)高性能的函數(shù)方法,存在兩個重要的出發(fā)點:盡可能通過內(nèi)聯(lián)來降低運行期函數(shù)調(diào)用,盡可能減少不必要的運行期多態(tài)。接下來,我為您講解一下為何要從這兩個點出發(fā),以及應(yīng)當(dāng)如何去做。

第一點,盡可能通過內(nèi)聯(lián)來降低運行期函數(shù)調(diào)用。所謂 “通過內(nèi)聯(lián)”,指的是將代碼直接插入到代碼調(diào)用中進行執(zhí)行,從而減少運行期函數(shù)調(diào)用。那為何要減少生成真實的運行期函數(shù)呢?這是由于函數(shù)調(diào)用自身會產(chǎn)生一些額外的性能開銷。在函數(shù)調(diào)用的過程中,需要先把當(dāng)前局部變量壓棧,在調(diào)用結(jié)束后還需要出棧操作,同時還需要更新相關(guān)寄存器。所以當(dāng)函數(shù)體內(nèi)部的邏輯較小時,所產(chǎn)生的額外開銷所占比例會比較高。因此,對于較小的函數(shù)方法,我們應(yīng)盡量采用內(nèi)聯(lián)實現(xiàn),以此降低不必要的調(diào)用開銷。

實際上,不同的編程語言,支撐函數(shù)方法內(nèi)聯(lián)的語法和機制存在一定差異。在 Java 語言的開發(fā)過程中,我建議您盡量使用 final 來定義方法,因為在這種場景下,Java 的 JIT 有較大概率將這個代碼方法內(nèi)聯(lián)掉。而在 C++ 中,針對一些熱點小函數(shù),您可以使用 Inline 關(guān)鍵字來定義方法,如此便能明確告知編譯器盡量將代碼內(nèi)聯(lián)掉。補充:在早期 C 語言的開發(fā)過程中,由于沒有內(nèi)聯(lián)語法,程序員常常使用編譯宏來定義方法,以此減少真實方法的調(diào)用開銷。然而,最終編譯器或解釋器能否將代碼內(nèi)聯(lián)掉,還存在許多隱性約束條件,所以您在編碼實現(xiàn)時需要多加留意。

第二點,盡量減少不必要的運行期多態(tài)。

多態(tài)的本質(zhì)即為函數(shù)指針,它需要在運行過程中獲取內(nèi)存中變量的值,以此判斷代碼執(zhí)行需要跳轉(zhuǎn)至哪個位置。而這種在運行期動態(tài)決定跳轉(zhuǎn)地址的情況,極易導(dǎo)致指令集流水線的中斷,使得指令 Cache Miss 的概率增加,進而引發(fā)性能下降。

不過在 Java 語言中,由于類方法模式均是抽象的,所以我們能夠?qū)㈥P(guān)鍵方法定義為靜態(tài)方法,從而避免多態(tài)調(diào)用;對于 C++ 而言,在定義類方法時,我們可以依據(jù)需求決定是否使用抽象方法,以減少不必要的多態(tài);而在 C 語言中,我們能夠通過盡量避免使用不必要的函數(shù)指針,來降低運行期多態(tài)。

另外,在實現(xiàn)高性能函數(shù)方法時,還有一些要點您也需要留意,例如盡量避免遞歸調(diào)用、盡量減少不必要的參數(shù)傳遞等等。不過這些均屬于高性能編程的常識性問題,所以在此我就不再展開闡述了。

高性能表達式實現(xiàn)

其實,現(xiàn)在的編譯器針對表達式級別的優(yōu)化支持能力已經(jīng)很強大了,比如說,如果你在編寫代碼的過程中,使用下面的乘法操作:

int y = x * 128;

那么,對于高性能的編譯器(如新版的 GCC 9.x 等)而言,能夠?qū)⑦@個乘法操作優(yōu)化為移位操作,進而提升執(zhí)行性能。然而,我們在編碼的過程中,不能完全依賴這種編譯器的能力,因為一方面編譯器的優(yōu)化能力存在邊界,另一方面在編寫代碼的過程中,編譯器對表達式的優(yōu)化也只是順便為之。所以在此,我為您總結(jié)了高性能表達式實現(xiàn)中幾個較為重要的點,它們均屬于簡單的實現(xiàn)規(guī)則,您也可以在編寫代碼的過程中作為參考并加以注意。

第一點,盡量將常量計算放到一起。

比如你可以看看下面的代碼,這是一個包含了 3 個乘法運算的表達式:

int z = 32 * x * 432 * y;

那么,如果將常量乘法計算放到一起,就很容易在編譯期優(yōu)化掉,從而就可以避免執(zhí)行時再計算。

第二點,盡量將表達式簡化,從而減少冗余運算開銷。

我們同樣來看一個例子。在下面的這段代碼示例中,兩個表達式的實現(xiàn)邏輯相同,都是先乘法再加法,不過您會發(fā)現(xiàn),第二個表達式少了一次乘法運算,所以它的執(zhí)行性能會更為出色:

int z = x * x + y *x  ;  //兩個乘法操作,一個加法操作
 int z = x * (x+y); //一個乘法操作,一個加法操作

第三點,盡量減少除法運算。

目前 CPU 中對除法計算的開銷仍然較大,因此倘若能夠優(yōu)化為移位操作或者乘法操作,那么都能夠提升執(zhí)行性能。

高性能控制流程實現(xiàn)首先您需要知曉的是,控制流程代碼在執(zhí)行的過程中,CPU 執(zhí)行會通過指令分支預(yù)測,提前將接下來的執(zhí)行指令搬移到 Cache 中,如果預(yù)測失敗,就有可能導(dǎo)致指令流水線中斷,從而對執(zhí)行性能產(chǎn)生影響。

所以,您在編寫控制流程代碼時,就需要思考一下怎樣才能更好地實現(xiàn),以此來優(yōu)化代碼的執(zhí)行性能。

那么具體該怎么做呢?在此,我也為您分享一下我在實踐過程中總結(jié)的經(jīng)驗,即盡量減少不必要的分支判斷。這個原則是最為重要、也是最容易被忽略的。為何這么說呢?我們來看一個具體的例子。在下面這段代碼里,您可以發(fā)現(xiàn) x==2 和 x==3 對應(yīng)的分支場景是相同的,但是它們還是被放置在了兩個代碼分支當(dāng)中,所以這樣執(zhí)行起來不但低效,而且還存在重復(fù)代碼:

if ( 2 == x ) {   // 場景1
     printf("case 1");
 }
 if ( 3 == x ) {    //場景1
     printf("case 1");
 }
 if ( 4 == x ) {    //場景2
     printf("case 2");
 }

在日常的代碼開發(fā)里,由于偷懶不想寫一個組合的邏輯表達式,從而增加分支邏輯的現(xiàn)象,實際上是較為常見的。所以說,我們在實際編寫控制流程代碼的時候,務(wù)必要注意盡量減少不必要的代碼分支,如此才能有效地提升執(zhí)行性能。

然而這里您可能還存在一個問題,那就是如果深入挖掘優(yōu)化代碼中一些重復(fù)的分支邏輯,其中包含的門道還比較多。比如說,通過多態(tài)來避免代碼中重復(fù)的 switch 分支邏輯,利用表驅(qū)動來減少 switch 邏輯和小的 for 循環(huán)平鋪執(zhí)行等等。所以在此,我給您一個小建議,在一些特殊場景下(比如 if 條件嵌套非常多的場景),您可以考慮使用 switch 來替換 if ,這樣也有可能改進代碼的執(zhí)行性能。

責(zé)任編輯:武曉燕 來源: 二進制跳動
相關(guān)推薦

2011-03-07 17:11:21

云遷移云轉(zhuǎn)型

2025-01-26 00:01:00

2021-03-19 07:40:22

緩存數(shù)據(jù)庫日志

2023-09-16 18:48:28

代碼邏輯

2023-12-27 13:44:00

數(shù)據(jù)庫系統(tǒng)分布式

2021-01-13 05:23:27

緩存數(shù)據(jù)庫高并發(fā)

2009-10-10 11:18:54

谷歌Android

2019-06-04 08:27:03

2018-07-13 15:56:39

緩存數(shù)據(jù)庫數(shù)據(jù)

2020-04-03 14:30:01

數(shù)據(jù)科學(xué)遠程工作數(shù)據(jù)科學(xué)家

2010-08-27 10:37:41

馬云

2018-06-19 07:16:27

工業(yè)物聯(lián)網(wǎng)IIoT物聯(lián)網(wǎng)

2021-05-05 10:54:47

數(shù)據(jù)泄漏漏洞網(wǎng)絡(luò)攻擊

2024-12-16 08:01:57

2021-01-29 10:51:48

高并發(fā)數(shù)據(jù)庫緩存

2019-12-24 09:12:10

運維架構(gòu)技術(shù)

2021-02-16 23:57:32

5G手機運營商

2023-08-27 21:57:07

技術(shù)債語言工具

2019-08-14 16:11:41

硬件電子技術(shù)系統(tǒng)

2017-08-15 08:27:48

云備份問題恢復(fù)
點贊
收藏

51CTO技術(shù)棧公眾號