一段簡單文件IO的C程序,為什麼我的優化反而更慢?

項目的的起因:眾編程語言間的 swapview 之戰

我給swapview的C語言版提交了優化PR。

原先的代碼:https://github.com/lilydjwg/swapview/blob/master/C/swapview.c

我優化的代碼:https://github.com/begeekmyfriend/swapview/blob/swapview-for-c/C/swapview.c

編譯腳本是一樣的(使用C11,O4優化):https://github.com/begeekmyfriend/swapview/blob/swapview-for-c/C/Makefile

差距主要是在IO調用下,分別列一下兩份代碼IO調用主要差異(性能至上,兼容可讀性):

原先的代碼:fopen、fread、getline(內含malloc)

我優化的代碼:open、read、dup2(重定向到stdin)、getchar

得到的反饋是:https://github.com/lilydjwg/swapview/pull/91

Your version uses less memory, makes fewer mallocs, but runs slower....

怎麼也想不通,難道是替換了getline引起的?用C語言究竟怎麼寫才好?

最後麻煩有請項目發起人@依雲 貼一下benchmark對比好嗎?謝謝!

-------------------------------------------------------------------------------------------------

使用 http://lilyshare.b0.upaiyun.com/temp/run_benchmark 在Linux x86_64測試結果(發起人@依雲 的環境下測試):

原始代碼:top: 68.83, min: 68.55, avg: 70.13, max: 73.15, mdev: 1.58, cnt: 20

我變慢的:top: 103.43, min: 102.44, avg: 104.59, max: 107.51, mdev: 1.43, cnt: 20

--------------------------------------------------------------------------------------------------

對於優化理由我在PR鏈接裡面給出了(上面發過,我再發一遍):https://github.com/lilydjwg/swapview/pull/91

主要是原先代碼getline()的地方有邏輯錯誤,導致每次讀一行要消耗一次malloc/free,繼上次失敗後,我提交了新的PR:Remove unnecessary mallocs for "getline" calling · begeekmyfriend/swapview@25d77ee · GitHub

這次實測(使用發起人提供的benchmark,在我的機器比較):

原先代碼: top: 94.53, min: 94.13, avg: 96.08, max: 101.18, mdev: 1.96, cnt: 20

我變快的:top: 82.97, min: 82.73, avg: 84.08, max: 89.38, mdev: 1.81, cnt: 20

說明這次改動生效了。


好吧,我用了一下神器perf。

結果是這樣的:

原版本:

+ 11.15% smaps_account

+ 10.35% format_decode

+ 8.21% number.isra.11

+ 8.04% vsnprintf

+ 6.63% memcpy_erms

+ 6.16% _IO_getdelim

+ 5.91% smaps_pte_range

+ 5.27% getSwapFor

+ 3.58% memchr

+ 3.15% _int_free

+ 2.78% malloc

+ 2.53% __memcpy_sse2

+ 2.35% strchr

+ 2.13% show_smap

+ 2.09% vm_normal_page

+ 1.91% skip_atoi

+ 1.57% find_vma

+ 1.06% _int_malloc

+ 0.87% mangle_path

+ 0.85% show_map_vma

+ 0.64% free

+ 0.63% prepend_name

+ 0.63% seq_printf

+ 0.62% __walk_page_range

+ 0.62% seq_putc

+ 0.62% _raw_spin_lock

題主版本:

+ 41.20% _IO_getc

+ 9.13% smaps_account

+ 8.54% format_decode

+ 8.12% getSwapFor

+ 5.47% vsnprintf

+ 3.47% number.isra.11

+ 3.35% memcpy_erms

+ 2.95% _IO_getc@plt

+ 2.49% show_smap

+ 1.59% smaps_pte_range

+ 1.45% skip_atoi

+ 0.87% find_vma

+ 0.87% copy_user_enhanced_fast_string

+ 0.87% strchr

+ 0.86% vm_normal_page

+ 0.86% mangle_path

+ 0.57% seq_printf

+ 0.56% d_path

很明顯吧?用getchar和getline比太慢了!


你實施那些「優化」的理由是什麼?為什麼你認為你的做法應該比原來更快?


優化之前先profile,這是一切優化的前題。只是分析,很難知道優化點在哪?

事實上幾個簡單的測試就可以找到優化點。

time ./swapview

發現user的時間佔sys時間的一半,說明IO是主要問題。

strace ./swapview

發現主要read佔90%,說明中間有大量的短數據讀取。查看了一下代碼和數據情況,/proc/pid/smap文件中有很多行,按行讀取時絕大讀取都無效。

為了確認我的說法,我把這部分處理刪除後,重新測試下,read數等於open數。

然後改用了一個簡單粗暴的作法:定長緩衝。一次性讀取文件的全部內容,再到內存中查找關鍵字。

結果,不分析不會知道的,read數下降為修改前的1/3,但實際上時間貢獻很小,user的時間減少到0了,但sys時間沒變。程序絕大部分時間花在讀取proc文件系統中,也就是說單線程基本不太可能有更多的提升。


不知道你的代碼中是否有自建緩衝區,否則效率要低是很正常的。(之前做過read和fread的對比,很明顯,但是當加了一個buf[200]的緩衝區後也可以做到秒輸出了)


推薦閱讀:

如何優化QTableView的性能?
你見過哪些令你瞠目結舌的C/C++代碼技巧?
gcc為何有時會在call前push eax,默認的調用約定不是cdecl?
現代C/C++編譯器有多智能?能做出什麼厲害的優化?
參加2017年在新浪舉辦的北京場「前端體驗大會」是個什麼樣的體驗?

TAG:C編程語言 | 優化 | 性能優化 | IO |