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); }
原文地址:http://7856933.blog.51cto.com/7846933/1562722