C程式最佳化之路(三) (轉)
C之路(三):namespace prefix = o ns = "urn:schemas--com::office" />
――liyuming1978@163.com
本文講述在編寫C程式程式碼的常用最佳化辦法,分為I/O篇,篇,演算法篇。MMX本來我也想歸在這裡的,但是由於內容和標題不太符和,決定換一個名字,叫MMX技術詳解,和H263影片技術中的MMX應用兩篇文章。
三.演算法篇
在上一篇中我們講述了對記憶體操作的最佳化,這一篇則主要講述一些常用的最佳化演算法。這個東東太多,內容可能會有點凌亂,見諒。
I.從小處說起:
先說說一些小地方先:
① 比如n/2寫為n>>1這個是常用的方法,不過要注意的是這兩個不是完全等價的!因為:如果n=3的話,n/2=1;n>>1=1;但是,如果n=-3的話,n/2=-1;n>>1=-2所以說在正數的時候,他們都是向下取整,但是負數的時候就不一樣了。(在JPG2000中的整數YUV到RGB變換一定要使用>>來代替除法就是這個道理)
② 還有就是a=a+1要寫為a++; a=a+b要寫為a+=b(估計一般用VB的才會寫a=a+1 :P)
③ 將多種運算融合:比如a[i++];就是先訪問a[i],再令i加1;從的角度上說,這個確實是最佳化的,如果寫為a[i],和i++的話,有可能就會有兩次的對i變數的讀,一次寫(具體要看的最佳化能力了),但是如果a[i++]的話,就一定只讀寫i變數一次。不過這裡有一個問題要注意:在條件判斷內的融合一定要小心,比如:(idct變換中的0塊判斷,陳王演算法)
if (!((x1 = (blk[8*4]<<8)) | (x2 = blk[8*6]) | (x3 = blk[8*2]) | (x4 = blk[8*1]) | (x5 = blk[8*7]) | (x6 = blk[8*5]) | (x7 = blk[8*3])))
在條件判斷中融合了賦值語句,但是實際上如果條件為真的話,是不需要這些賦值語句的,也就是說當條件真的時候,多了一些垃圾語句,這些是在h263原始碼上的問題,雖然這些垃圾語句使得計算0塊的時候,時間增加了30%,但是由於idct僅僅佔1%的時間,0塊又僅僅30%~70%的時間,所以這些損失是沒有什麼關係的。(這是後來我用匯編改寫原始碼的時候得到的結論)。這裡也說明了,程式最佳化一定重點在最耗時的地方。對於不耗時的程式碼最佳化是沒有太大的實用意義的。
II.以記憶體換速度:
天下總是難有雙得的事情,也是一樣,大多數情況,速度同記憶體(或者是效能,比如說壓縮效能什麼的)是不可兼得的。目前程式加速的常用演算法一個大方面就是利用查表來避免計算(比如在jpg有huffman碼錶,在YUV到RGB變換也有變換表)這樣原來的複雜計算現在僅僅查表就可以了,雖然浪費了記憶體,不過速度顯著提升,還是很划算的。在查詢裡面也有這樣的思想,將熱點起來以加速查詢。 現在介紹一個簡單的例子,(臨時想的,呵呵):比如,在程式中要經常(一定要是經常!)計算1000到2000的階乘,那麼我們可以使用一個陣列a[1000]先把這些值算好,保留下來,以後要計算1200!的時候,查表a[1200-1000]就可以了。
III.化零為整
由於零散的記憶體分配,以及大量小建立耗時很大,所以對它們的最佳化有時會很有效果,比如上一篇我說的連結串列存在的問題,就是因為大量的零散記憶體分配。現在就從一個vb的程式說起,以前我用vb給別人編小程式的時候,(呵呵,主要是用vb程式設計比vc快,半天就可以寫一個)在使用MlexGrid的時候(就是一個表格控制元件),發現如果一行一行的增加新行,重新整理速度十分的慢,所以我就每次增加100行,等到資料多到再加新行的時候,再加100行,這樣就“化零為整”了,使用這樣的方法,重新整理的速度比原來快了n倍!其實這樣的思想應用很多,如:程式執行的時候,其實就佔用了一定的空間,後來的小塊記憶體分配是先在這個空間上的,這就保證了記憶體碎片儘可能的少,同時加快執行速度。
IV.條件語句或者case語句將最有可能的放在前面
最佳化效果不明顯。想得到就用吧,想不到就算了。
V.為了程式的可讀性,不去做那些編譯器可以做的或者最佳化不明顯的處理:
這個是很重要的,一個普通程式的好壞,主要是它的可讀性,可移植性,可重用性,然後才是它的效能。所以,如果編譯器本身可以幫助我們最佳化的話,我們就沒有必要寫那些大家都不怎麼看得懂的東西。比如a=52(結束)-16(起始);這樣寫可能是因為在別人讀程式的時候,一下就明白了a的含義。我們不用寫為a=36,因為編譯器是會幫我們算出來的。
IV.具體情況具體分析:
具體情況具體分析,這是放之四海而皆準的真理。沒有具體的分析,就不能針對問題靈活應用解決的辦法。下面我就說說分析的方法。即如何找到程式的耗時點:(從最簡單的辦法說起,先說明一個GetTickCount(),這個函式在頭尾各一次,返回值相減就是程式的耗時,精確到1ms)
① 對於認為是比較耗時的函式,執行兩次,或者將函式內部的語句註釋掉(要保證程式可以執行),看看多(或者少了)多少時間。這個辦法簡單不精確。
② 每個地方都用GetTickCount()函式測試時間,注意GetTickCount()只能精確到ms。一般的小於10ms就不太精確了。
③ 使用另外一個函式QueryPerformanceCounter(&Counter)和QueryPerformanceFrequency(&Frequency),前面計算時鐘週期,後面是cpu頻率相除就是時間。不過如果你要精確到這一步的話,建議將程式設定為最高階別,防止它被阻塞。
最後講講我處理的一個程式:程式要求我忘了,反正裡面有一個函式,函式里面有一個大的迴圈,迴圈內部的處理比較耗時。結果最初程式表現出來的狀況是開始還很快,越到後面越慢;我在跟蹤程式中變數的時候,發現最初的迴圈在迴圈幾次後就跳出了,而後面的迴圈次數越來越多。找到了為什麼慢的原因,就可以對症下藥了,我的處理是每次迴圈不是從頭開始,而是從上一次迴圈跳出的地方開始左右迴圈(因為可能下一次迴圈跳出的地方別上一次的小,所以也要遍歷前面的),這樣程式的速度在後面也很快了。我講這個的道理就是在實際運用中,要具體的分析程式慢的真正原因,才能達到最佳的最佳化效果。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-977223/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C程式最佳化之路 (轉)C程式
- C程式最佳化之路(二) (轉)C程式
- C++程式碼最佳化方法總結(三) (轉)C++
- C++ 層次程式碼最佳化 (轉)C++
- Delphi程式碼最佳化(三) 浮點篇 (轉)
- C++程式碼最佳化方法總結(一) (轉)C++
- C++程式碼最佳化方法總結(四) (轉)C++
- C++程式碼最佳化方法總結(二) (轉)C++
- CSS程式碼重構與最佳化之路CSS
- 我的C學習之路 (轉)
- 程式設計之路 (轉)程式設計
- 最佳化--C程式設計師之終極標靶 (轉)C程式程式設計師
- 關於C++程式碼最佳化的方法總結(轉)C++
- 重走JAVA程式設計之路(三)反射Java程式設計反射
- ORACLE SQL效能最佳化系列 (三) (轉)OracleSQL
- C++程式設計思想筆記之三 (轉)C++程式設計筆記
- Prefer C++(三) (轉)C++
- 程式設計之路-介面美化 (轉)程式設計
- C#速成(之三) (轉)C#
- C/C++自學之路C++
- DircetDraw c/c++ 使用指導(三) (轉)C++
- Delphi 程式碼最佳化——字串篇 (轉)字串
- 如何“玩轉”MongoDB?羅輯思維資料庫效能最佳化之路!MongoDB資料庫
- Oracle SQL效能最佳化系列講座之三(轉)OracleSQL
- 程式碼最佳化試驗——短迴圈最佳化(下) (轉)
- 程式碼最佳化試驗——短迴圈最佳化(上) (轉)
- C#聊天程式 (轉)C#
- Delphi程式碼最佳化 完結篇 (轉)
- 最佳化J2ME程式大小 (轉)
- 一個 Shell 程式的效能最佳化(轉)
- React 進階之路(三)React
- c#之路漫漫長C#
- 程式設計技術點滴三(C/C++)程式設計C++
- 轉貼:胡陽:汗水鋪就程式碼之路,三分天註定,七分靠打拼
- 最佳化J2ME應用程式 (轉)
- Delphi程式碼最佳化(二) 整數篇 (轉)
- C#演算法----(三)希爾排序 (轉)C#演算法排序
- 0.11之路(三):system模組