從機器級角度思考如何優化程式的效能

2021-08-02 08:48:52 字數 3373 閱讀 5376

1.優化程式效能的必要性?

2.為什麼編譯器有優化選項,而我們還要了解如何用我們自己的**去優化?

3.優化程式效能都有哪些方法?

這邊文章我將從以上三個方面去**如何從機器級角度去優化程式的效能~

我們現在用的個人電腦的效能都是非常高的,執行乙個迴圈100次的塊在優化後與優化前可能感受不到優化的效果,那麼我們還有優化程式效能的必要嗎?設想有以下幾種情況:

1.我對優化感興趣,我想知道優化的原理。

2.嵌入式程式開發的硬體效能相對個人電腦不是那麼高,如果你是嵌入式開發工程師,並且在提交給客戶的軟體版本的效能就差那麼十幾毫秒,那麼我認為你需要了解一下如何優化。

3.我想裝b。

我相信總有一款適合你~

我們在編譯的時候可能會用到優化選項(-on),如果優化選項過高,編譯出來的**將很難除錯,如果優化選項過低,優化效果可能不明顯。一般情況下我們在編譯工程的時候優化選項都會選擇o2級別。但是有一點需要我們程式設計師明白,編譯器給你的**優化與否,取決於編譯器對你**的分析。如果編譯器分析後得出你的**沒有二義性,那麼編譯器會替你優化**。但是一般情況下,我認為編譯器很難對你的**進行優化,比如下面的例子:

int sum(int *a, int *b)

int sum(int *a, int *b)

乍一看這兩個介面表達的意思是一樣的,並且下面的效能要比上面的效能高,但是很多情況下,如果你用了上面的那個方法,編譯器是不給你優化的,為什麼?如果a和b指向同一塊記憶體,這兩個介面返回的結果是不一樣的。因為編譯器無法分析出你用這個介面的時候能不能保證兩個引數是不是指向同一塊記憶體,所以編譯器就不會給你優化。

所以,我們不能完全依賴編譯器的優化選項,還需要在我們自己的**中去盡可能的優化。

3.優化程式效能都有哪些方法?

這裡我將簡單列出我目前掌握到的**效能優化方法,並簡單的說明,如果大家有什麼問題的話,我們可以一起**共通學習。

有些背景知識需要簡單說明一下:現在的cpu都是流水線式的,也就是說cpu不是等待乙個指令完成了才去執行第二個指令的,而是可以並行的。

這是因為指令和指令之間不一定會有資料關聯,沒有資料關聯的影響,指令就可以並行處理了。另外cpu執行指令分為icu和eu兩部分。icu為指令控制單元,eu為指令執行單元。

1.消除不必要的暫存器引用,用臨時變數存放結果代替入參

現有以下兩個介面:

int dest[20] = ;

void combine_1(int size, int* dest, int* result)

}void combine_2(int size, int* dest, int* result)

*result = res;

}結論:combine_2效能優於combine_1.

為什麼2會由於1,請看下面的彙編**:

*result = dest[i] * *result;

004113e6  mov         eax,dword ptr [i] 

004113e9  mov         ecx,dword ptr [dest] 

004113ec  mov         edx,dword ptr [result] 

004113ef  mov         eax,dword ptr [ecx+eax*4] 

004113f2  imul        eax,dword ptr [edx] 

004113f5  mov         ecx,dword ptr [result] 

004113f8  mov         dword ptr [ecx],eax 

res = dest[i] * res;

0041144d  mov         eax,dword ptr [i] 

00411450  mov         ecx,dword ptr [dest] 

00411453  mov         edx,dword ptr [ecx+eax*4] 

00411456  imul        edx,dword ptr [res] 

0041145a  mov         dword ptr [res],edx 

顯然1比2多了兩條mov操作。

2.減少過程呼叫。

也就是說盡量少的進行介面呼叫。但是這項優化是要付出代價的:這破壞了**的模組性和抽象性。

3.消除迴圈低效率,迴圈條件式不應該呼叫方法。

例如for(int i = 0; i < get_max(); i++)需要改寫成int j = get_max();for(int i = 0;i < j;i++)

下面兩個方法為高階的方法:

4.迴圈展開。將原來的2n次迴圈變為n次迴圈,並且可以用多個臨時變數進行並行處理。

void combine_3(int size, int* dest, int* result)

*result = res_1 * res_2;

}我們將combine_2再次進行優化,得到了combine_3,我們可以看一下彙編:

res_1 = dest[i] * res_1;

004114b4  mov         eax,dword ptr [i] 

004114b7  mov         ecx,dword ptr [dest] 

004114ba  mov         edx,dword ptr [ecx+eax*4] 

004114bd  imul        edx,dword ptr [res_1] 

004114c1  mov         dword ptr [res_1],edx 

res_2 = dest[i + 1] * res_2;

004114c4  mov         eax,dword ptr [i] 

004114c7  mov         ecx,dword ptr [dest] 

004114ca  mov         edx,dword ptr [ecx+eax*4+4] 

004114ce  imul        edx,dword ptr [res_2] 

004114d2  mov         dword ptr [res_2],edx 

雖然說combine_3的彙編跟combine_2是一樣的,但是我們知道,迴圈跳轉由原來的2n次變為了n次,並且,我們的cpu是流水線式的可以進行並行處理的。

所以combine_3的效能是比combine_2更高的。

5.條件轉移指令替換條件指令,減少**懲罰。比如用條件表示式替換if語句。

cpu在執行指令的時候,為了提高執行效率,會對執行分支進行**。雖然**的成功率很高,但是一旦**失敗將收到44個時鐘週期的處罰(以core i7為例,**失敗會將

之前**得到的值清空,再重新執行另外的分支)。所以**懲罰還是很影響效率的。

什麼是經驗 從程式設計師角度的思考

為啥會思考這個問題?剛才在騎車回家的路上,腦子裡突然閃過紀錄片裡關於愛因斯坦研究出相對論和他的大腦的關係。我就在想,他當時在思考時間和空間時,是不是因為他腦袋裡面其實存在乙個關於時間和空間的模組,只不過是他在學過相關知識後,高速執行的大腦把這些模組一起呼叫出來 然後合併組裝在了一起,於是我們看到了今...

從邊際成本角度思考程式設計師職業

程式設計師真的是以寫 為生?yes and no.既是,也不全是。從表面來看,程式設計師產出 和軟體,領工資過日子。再往裡一層看,程式設計師職業提供的是軟體知識服務。而產出 和軟體,只是軟體知識服務的常見產出形式。為什麼軟體是服務業而不是製造業呢?因為軟體並不生產製造實體商品,而是作為一種技術手段來...

從程式設計師角度來看資料庫優化

談到資料庫優化概念,總感覺很龐大,無從下手 最近,聽了一次dba的分享,感覺有些思路 整理成文字,強化記憶 僅僅是一些個人理解和資訊聚合,可能有所偏差,還望有所指正 b 為什麼需要資料庫優化?b 資料庫效能優化主要的目的無非就是降低資料庫響應時間,提公升資料庫響應能力tps b 資料庫響應時間 b ...