现代软件工程讲义 2 开发技术 - 效能分析
[移山之道 第九章]
9.4? VSTS 效能分析工具
啊,效能分析,Performance!這是每一個程序員都夢想的事兒,讓自己的程序跑得又快又好,最好是比別的同學快一個數量級,別人的程序是O(N^2),而我的程序是O(n*logN),或者是O(N),這是多爽的一項成就呀!VSTS提供了方便的效能分析工具,讓我們能很快地找到程序的效能瓶頸,從而能有的放矢,改進程序。下面我們看一個具體的例子。
和同學們的作業類似,有這樣一道題:
寫一個程序,分析一個文本文件中各個詞出現的頻率,并且把頻率最高的10個詞打印出來。
?
果凍很快用C#寫好了程序,命名為WordFreq.exe,然后運行了一下,驗證了正確性,程序的基本框架如代碼清單9-3所示(全部程序可以在移山社區網站下載):
代碼清單9-3? WordFreq程序,程序框架
DoIt()
{
??? ProcessFile()? //store all words in a big buffer
??? ProcessBuffer()? //calculate and store the frequency of each word
??? OutputResult()?? //output top 10
}
?
ProcessBuffer()
{
??? GetOneWord()?? //get one word from buffer
??? FreqOneWord()
}
?
FreqOneWord(word)
{
Find the word in the array list,
If (found)
??? Update the frequency
If (not found)
??? Add the word in the array list with frequency = 1
}
?
OutputResult()
{
ArrayList.Sort()?? //sort the array
Output Top 10 entry;
}
文本文件大約是30KB~300KB大小。在運行效能分析之前,阿超讓大家預計占用時間最多的是什么函數,或者哪些語句。大家眾說紛紜,有的說是處理文件,因為I/O很花時間,有的說是排序,有的說是處理每個詞。還有人建議應該把排序和處理每一個詞同時進行,這樣就能加快速度。
我們看看到底會是什么情況。第一步,要確保編譯的程序是Release版本。然后在VS界面中選中Tools | Performance Tools | Performance Wizard(如圖9-1所示)。
圖9-1? 效能分析,選擇分析方法
我們看到可以選擇兩種分析方法:
(1)??????? 抽樣(Sampling)
(2)??????? 代碼注入(Instrumentation)
通俗地解釋,抽樣就是當程序運行時,Visual Studio時不時看一看這個程序運行在哪一個函數內,并記錄下來,程序結束后,Visual Studio就會得出一個關于程序運行時間分布的大致的印象。這種方法的優點是不需要改動程序,運行較快,可以很快地找到瓶頸。但是不能得出精確的數據,代碼中的調用關系(CallTree)也不能準確表示。
另一方面,代碼注入就是將檢測的代碼加入到每一個函數中,這樣程序的一舉一動都被記錄在案,程序的各個效能數據都可以被精準地測量。這一方法的缺點是程序的運行時間會大大加長,還會產生很大的數據文件,數據分析的時間也相應增加。同時,注入的代碼也影響了程序真實的運行情況(這有點像量子物理學中的“測試的光線干擾了測試物體本身”的現象)。
我們一般的做法是,先用抽樣的方法找到效能瓶頸所在,然后對特定的模塊用代碼注入的方法進行詳細分析。
對程序進行效能分析,我們先要弄清下面這幾個名詞,如表9-1所示:
表9-1? 效能分析的名詞解釋
| 名? 詞 | 含? 義 |
| 調用者Caller | 函數Foo()中調用了Bar(),Foo()就是調用者 |
| 被調用函數Callee | 見上,Bar()就是被調用函數 |
| 調用關系樹Call Tree | 從程序的Main()函數開始,調用者和被調用函數就形成了一個樹形關系——調用樹 |
| 消逝時間Elapsed Time | 從用戶的角度來看程序運行所花的時間。當用戶看到一個程序沒有反應,用戶并不知道程序此時是在運行自己的代碼,還是被調度出去了,或者操作系統此時正在忙別的事情 |
| 應用程序時間Application Time | 應用程序占用CPU的時間,不包括CPU在核心態時花費的時間 |
?
續表
| 名? 詞 | 含? 義 |
| 本函數時間Exclusive Time | 所有在本函數花費的時間,不包括被調用者使用的時間 |
| 所有時間Inclusive Time | 包含本函數和所有調用者使用的時間 |
理解了上面的各種概念后,我們就不難理解“消逝的本函數時間(Elapsed Exclusive Time)”等其他組合名詞所代表的概念了。
我們先進行抽樣分析,在效能瀏覽器(Performance Explorer)中開始效能分析即可。
圖9-2是WordFreq程序處理一個30KB的文本文件時的情況:
圖9-2? 用抽樣的方法分析效能
大家可以看到最花時間的三個函數是:
WordFreq.Freq.FreqOneWord(string)
System.String.EqualsHelper(string,string)
System.Collections.ArrayList.get_Item(int32)
三個函數加起來占用了整個程序84%的時間。看來我們得分析為什么這三個函數會被調用得這么頻繁,開銷這么大了。
我們現在可以進行代碼注入的分析,同樣運行程序后,我們看看圖9-3的調用樹(Call Tree)報告。
圖9-3? 代碼注入方法產生的效能報告
?
結合實際的代碼(見代碼清單9-4),可以看到在WordFreq. FreqOneWord函數中,究竟發生了什么:
代碼清單9-4? FreqOneWord()
??????? private void FreqOneWord(string w)
??????? {
??????????? // see if we have a match, if not, add it to the end,
??????????? // then assign it initial frequency 1;
??????????? // if yes, inc the frequency by 1
??????????? for (int i = 0; i < m_wordList.Count; i++)
??????????? {
??????????????? Frequency fi = (Frequency)m_wordList[i];
?
??????????????? if (fi.str == w)
??????????????? {
??????????????????? fi.n++;
??????????????????? return;
??????????????? }
??????????? }
?
??????????? //now we have to append it to the end.
??????????? Frequency f = new Frequency();
??????????? f.str = w;
??????????? f.n = 1;
??????????? m_wordList.Add(f);
??????? }
我們可以清楚地看到:
WordFreq.Freq.FreqOneWord(string)被調用了8 150次,說明有8 150個詞被處理了。但是,System.Collections.ArrayList.Add(object)被調用了1 112次,說明有1 112個不同的詞被加入到ArrayList中。下面三個函數被調用的次數相似,它們都花了很多的時間。
System.Collections.ArrayList.get_Count()
System.Collections.ArrayList.get_Item(int32)
System.String.op_Equality(string,string)
?
果凍:(大叫起來)當我寫這一個語句的時候
????? for (int i = 0; i < m_wordList.Count; i++)
沒想到m_wordList.Count,也就是ArrayList.GetCount(),會花這么多時間,累計被調用了1631884次!
我可以馬上把代碼改成:
??????????? int count = m_wordList.Count;
for (int i = 0; i < count; i++)
這樣會如何?大家等了一會兒,代碼分析的結果出來了(如圖9-4所示):
圖9-4? 改進過的程序效能分析圖
可以看到System.Collections.ArrayList.get_Count()的調用次數和時間都大幅度地下降了。
我們可以繼續進行“效能測試,分析,改進,再效能測試”的流程,逐漸提高程序的效能和我們的編程水平。
大家也要注意避免沒有做分析就過早地進行“效能提高”,剛才有人提到我們可能要提高排序的性能,但是從圖9-4來看,System.Collections. ArrayList.Sort()只占了FreqOneWord()不到1/50的時間,如果我們不經分析就盲目優化,也許會事倍功半。
9.5? 本章討論
果凍:噫吁唏,危乎高哉!我以前一直害怕做效能分析,看來是否會用效能分析工具來提高程序質量是一個優秀程序員的標志之一。我在今天之前都是盲人騎瞎馬。
小飛:改成盲人摸象更恰當。我聽說,如果一個程序員從來沒有用過效能分析工具,那他就不是一個程序員,只是一個編程愛好者罷了。
我的WC 程序事實上是最快的。但是在今天的程序評比之前,我想更進一步,就再優化了一下,估計能把速度提高2%。沒想到出了一個小錯誤,導致報告的結果(行數、詞數、字數)僅僅差了1。由于程序不正確,因此不能參加速度評比。我才是最需要大喊“噫吁唏”的人。
阿超:這有兩個教訓:
(1)先保證正確性,再提高效能。一個“僅僅差了1”的錯誤可能會導致緩沖區溢出(Buffer Overflow)的嚴重漏洞。
(2)如果效能的提高效果在5%以下,用戶不會注意到程序效能的區別。所以要考慮那些微小的提高是否值得。
另外,WordFreq算法的時間復雜度是多少,能否再優化?請寫實際程序加以驗證。
荔荔:怎么才能快捷地得到有適量重復文字的文件,來幫助我們進行測試和效能分析?
九條:我是這樣做的,在電腦的根目錄下,運行“dir /s > c:\temp\test.txt”命令。
阿超:好主意,大家還有沒有別的辦法?
?
VS2012:
Visual Studio 2012 提供了更好的效能測試的功能, 請看這些博客:
http://www.cnblogs.com/smart-code/archive/2012/09/23/2699053.html?
http://www.cnblogs.com/Gun-N-Rose/archive/2012/09/23/2699220.html?
http://www.cnblogs.com/bigbadwolf/archive/2012/09/24/2699438.html??
http://www.cnblogs.com/Gun-N-Rose/archive/2012/09/24/2699804.html?
?
總結
以上是生活随笔為你收集整理的现代软件工程讲义 2 开发技术 - 效能分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android icon 圆角半径,iO
- 下一篇: 顶级程序员的心得–Coders at W