C++之那些年踩過的坑
上次提到了一個問題:代碼優化。並留了一個小測試:無符號數與有符號數的性能比較。不知道有沒有人去實驗。我做了一個簡單的測試:
#include <iostream>#include <chrono>int main()
在上述代碼在 VS 的 Debug 模式下運行(Release就優化掉了),穩定後運行時間在 2800ns,然後把注釋去掉,再次運行,穩定後運行時間還是 2800ns。在我在電腦上,計算有符號類型和無符號類型幾乎是沒有差別的,我相信在絕大多數的電腦上也是相同的結果。
類似的還有浮點數的計算比整型數慢
首先我要聲明:我不是反對代碼優化。對於有很多流傳廣泛的所謂的優化技巧,我覺得我們應該應該抱著學習探索的心態,而不是一味地追求一些沒有什麼意義的東西。有些優化,確實很精妙。但很多所謂的技巧,看起來的意思就是:做編譯器的那群人都是傻逼。想優化我們的程序,這是正常的、應該的想法,但我們應該用科學的方法,而不是聽了一些奇淫技巧,卻不知道裏麵發生了什麼。
其實很簡單,探究性能瓶頸靠 profiling,探究代碼背後的不為人知的故事看 assembly。我們先講後麵一個。如果你想學習C/C++可以來這個群,首先是三三零,中間是八五九,最後是七六六,裏麵有大量的學習資料可以下載。
我想大多數人還是在 Windows 下編程,所以用的肯定也是宇宙最強 IDE VS。在 VS 下看匯編很簡單,隨便設置一個斷點,然後按調試下麵的開始調試,然後在打開在調試下的窗口找到反匯編,就可以看了。比如我們研究有符號數和無符號數,先寫一個程序:
int main()
然後按照剛剛的方法(在 Debug 下)看反匯編:
/*unsigned */int a = 123456789;00B4104E mov dword ptr [a],75BCD15h
然後在把注釋去掉試試:
unsigned int a = 123456789;00C3104E mov dword ptr [a],75BCD15h
幾乎是一模一樣的,最大的差別就是有符號數使用 idiv指令(帶符號除法),無符號數使用 div指令(不帶符號除法),而這兩種指令,CPU周期都是一樣的。
當然我不是說不用無符號數,而是說我們用什麼要看場合,而不是你覺得用了性能更好,除非是被大眾認可的或者你經過嚴謹的測試的。像對於某些書籍或者什麼地方說,隻要確定範圍不為負數的,就用無符號類型,我是不認可的。如果你講範圍,那如果一個有符號類型不夠用,那麼通常(相同bit下)它對應的無符號類型也不夠用。比如你 int32_t 不夠用,就應該用 int64_t,如果還不夠,考慮寫個 BigInteger類 吧 :) 。不過對於無符號和有符號類型,它們之間的性能在當代確實是幾乎沒有什麼差別。那具體什麼場合用什麼呢?這個也不一定,比如一般來說:
-
對於位儲存、位運算、模運算等,使用無符號類型
-
對於一般運算使用有符號類型
其實我對於無符號有符號是覺得很那個什麼的。。平時我們說一個數,要麼就是整數,要麼就是小數得了,還要去分有沒有符號那真的是。不過因為是 C++ 所以也就沒什麼奇怪的了。
好像有點偏離我想說的主題了(拽回來
我並不是想專門講這個什麼有符號無符號,而是想借這個題引出(我的)一些看法:
-
過早的優化是萬惡之源
-
不要試圖幫編譯器優化
-
優化時不要去猜測,想當然得去優化自己“覺得”性能差的地方
-
探究性能的瓶頸靠 profiling
我們一點一點講。
對於一個需求,我們應該先完成功能,若性能達不到要求之後,在確定瓶頸之後再去優化。過早優化,不僅讓代碼不直接,還容易出bug,還可能對性能幾乎沒有影響。而且,我們優化時,應該關注大方向,確定大方向是正確的。比如寫一個算法,我們首先應該確保 Big-O 的時間複雜度能達標,可以用 O(n) 的就不用 O(nlogn),可以用 O(nlogn) 的就不用 O(n²),而不是先在那裏扣 i++ 還是 ++i。另外,不要想著去幫編譯器優化,因為編譯器是一堆比你強不知道多少的人寫出來的,而對於一般人,想著去幫編譯器優化,大部分是無效的,少部分是錯的。比如有人學了一點點 std::move,就老是想著 move move move 去提高性能,舉個栗子,(不同的)容易寫出這樣的代碼:
template <typename T>T create()
確實運行不會錯,但是,代碼背後做的事情不一定就跟你想的一樣,往往跟你想象的還不一樣。有些情況編譯器可以采用更好的辦法,結果因為你那麼一搞,迫不得已隻能采用次一點的辦法。可以看看 這個問題 ,不贅述了。
還有比如說用異或來交換兩個變量,有人就會想,用位運算,不僅不需要創建臨時變量,而且位運算一般不是更快嘛!
如果你已經看了上麵的鏈接,那麼你也就知道了,你(幾乎)不會知道編譯器做了什麼,編譯器可以做的優化超出你的想象(不過有的時候人能明顯看出來的優化編譯器卻做不到,但影響不大),在我的係列文章(二)中也反複強調了,不要試圖幫助編譯器去優化。若你想探究一小段代碼背後的細節,就去看反匯編吧!
最後更新:2017-04-22 19:32:35