一段簡單文件IO的C程序,為什麼我的優化反而更慢?
01-08
項目的的起因:眾編程語言間的 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/91Your 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年在新浪舉辦的北京場「前端體驗大會」是個什麼樣的體驗?