程序的“听诊器”——性能监视工具( 四 )

左边几列说明每个阶段的数据:行数、单词数、字符数 。 右边部分描述了数据阶段之间的过滤程序:用秒表示的用户时间、系统时间以及真实时间 , 后面是命令本身 。
这个性能监视结果给出了程序员感兴趣的许多信息 。 这个管道是快速的 , 处理150页的书只需3.5分钟 。 第一次排序花了这个管道57%的运行时间 , 这种经过仔细调优的实用程序很难再提速了 。 第二次排序只花了这个管道14%的时间 , 但是还有调优的余地 。 这个性能监视结果还发现了管道中隐藏的一处小错误 。 UNIX高手们会乐于找出引入空行的地方 。
这个性能监视结果也透露了文件中单词的信息:共有57 651个单词 , 但只有4731个不同的单词 。 在第一个翻译程序之后 , 每个单词有4.3个字母 。 输出表明 , 最常见的单词是“the” , 占了文件的6% 。 6个最常见的单词占了文件的18% 。 对英语中最常见的100个单词做专门处理也许还能提高速度 。 试试看从这些计数中找出其他有趣的表面规律 。
跟许多UNIX用户一样 , 我过去也用手工监视管道的性能 , 利用单词计数(wc)命令来统计文件 , 用time命令来统计进程 。 “管道性能监视工具”让这个任务自动化了 。 用管道和一些输入文件的名称作为输入 , 产生性能监视结果作为输出 。 2个小时和50行代码就足以建立这个性能监视工具 。 下一节详细阐述这个话题 。
4 开发性能监视工具开发一个真正的性能监视工具是件困难的事情 。 Peter Weinberger开发了C行计数性能监视工具 , 我们前面看到的输出就是这个工具产生的 。 他在几个月时间内断断续续干了好几周才完成这个项目 。 本节描述如何更容易地开发一个简化版本 。
Dick Sites声称他的朋友“在某个周末实现了语句计数” 。 我觉得这简直难以置信 , 于是我决定要试着为附录A描述的Awk语言(这种语言还没有性能监视工具)开发一个性能监视工具 。 几小时后 , 当我运行程序P6的Awk版本时 , 我的性能监视工具生成了如下输出 。
程序P6及性能监视工具生成的输出
BEGIN { <<<1>>>n = 1000x[0] = 2; xc = 1print 2for (i = 3; i <= n; i++) { <<<998>>>if (prime(i)){ <<<167>>>print i}}exit}function prime(n,i) { <<<998>>>for (i=0; x[i]*x[i]<=n; i++) { <<<2801>>>if (n % x[i] == 0) { <<<831>>>return 0}}{ <<<167>>> }x[xc++] = nreturn 1}在左花括号后尖括号内的数显示该语句块被执行了多少次 。 幸运的是 , 这些计数与C行计数器产生的计数一样 。
我的性能监视工具包含两个5行的Awk程序 。 第一个程序读Awk源程序并且写一个新程序 , 其中在每个语句块开始的地方给不同的计数器加1;而在执行结束时 , 一个新的END动作(见附录A)把所有计数写入一个文件 。 当这样得出的程序运行时 , 就生成一个计数文件 。 第二个程序读出这些计数 , 把这些计数合并到源文本中 。 带性能监视的程序大约比原来的程序慢25% , 而且并不是所有的Awk程序都能正确处理——为了监视几个程序的性能 , 我不得不做出整行(one-line)的修改 。 但对于所有这些缺点来说 , 搭起一个能运行的性能监视工具 , 花几小时并不算什么大投入 。
人们实现过一些快速性能监视工具 , 但鲜见报道 。 下面举几个例子 。
在1983年8月的 BYTE 杂志上 , Leas和Wintz描述了一个性能监视工具 , 用一个20行的6 800汇编语言程序来实现 。
贝尔实验室的Howard Trickey在一小时内用Lisp实现了函数计数 , 办法是修改defun , 在进入每个函数时给计数器加1 。
1978年 , Rob Pike用20行Fortran程序实现了一个时间性能监视工具 。 在CALL PROFIL(10)之后 , 后续的CPU时间被计入计数器10 。
在这些系统和许多其他系统上 , 在一晚上写出一个性能监视工具是可能的 。 在你第一次使用所得到的性能监视工具时 , 这个工具轻易就能节省超过一个晚上的工作量 。