<?xml version="1.0" encoding="GB2312" ?>
<?xml-stylesheet type="text/xsl" href="../../article.xsl" ?>

<article>

<title>VC补遗之Profile篇</title>

<author>晨光（Morning）</author>

<keywords>
  <keyword>VC</keyword>
  <keyword>Profile</keyword>
  <keyword>性能优化</keyword>
</keywords>

<from type="原作"/>
<copyright/>

<paragraph>
(续篇)
</paragraph>

<paragraph name="Profile的作用">
帮助你分析并发现程序运行的瓶颈，找到耗时所在，同时也能帮助你发现不会被执行的代码。从而最终实现程序的优化。
</paragraph>

<paragraph name="Profile的组成">
Profile包括3个命令行工具：PREP，PROFILE，PLIST。可以以命令行方式运行Profile，其过程是：PREP读取应用程序的可执行文件并生成一个.PBI文件和一个.PBT文件；PROFILE根据.PBI文件，实际运行并分析程序，生成.PBO输出文件；PREP再根据.PBO文件和.PBT文件，生成新的.PBT文件；PLIST根据.PBT文件生成可阅读的输出。
</paragraph>

<paragraph name="Profile的具体功能">
- Function timing：对程序花费在执行特定函数上的时间进行评估。可以通过Profile对话框激活该功能。分析结果中，Func Time一栏以秒为单位记录了函数运行所花时间，下一栏显示了该函数时间占总运行时间的百分比；Func+Child Time栏记录了函数及其所调用的子函数运行所花的总时间，下一栏显示了前述时间占总运行时间的百分比；Hit Count栏记录函数被调用的次数；Function栏显示函数的名称。
</paragraph>

<paragraph>
- Function coverage：记录特定函数是否被调用，可以用来确定代码中的未执行部分。可以通过Profile对话框激活该功能。分析结果列出所有被分析的函数，并使用*号标记执行过的函数。
</paragraph>

<paragraph>
- Function counting：记录程序调用特定函数的次数。在Profile对话框中选择Custom，并在Custome Settings中指定fcount.bat（位于VC98\bin目录下）。需要注意的是，在指定fcount.bat所在目录时，最好不要用长文件名的方式，这样有可能出错，比如要将c:\Program Files写成c:\Progra~1。
</paragraph>

<paragraph>
- Line counting：记录程序所执行的代码中特定行的次数。在Profile对话框中选择Custom，并在Custome Settings中指定lcount.bat（位于VC98\bin目录下）。该功能使用.EXE中的调试信息启动Profile，因此不需要.MAP文件。分析结果中，Line栏标示源代码的行号，Hit Count栏记录该行执行次数，下一栏显示了该行执行次数占所有代码行执行次数的百分比，Source Line显示了对应的源代码。
</paragraph>

<paragraph>
- Line coverage：记录代码中的特定行是否被执行，可以用来确定代码中的未执行部分。可以通过Profile对话框激活该功能。分析结果列出所有被分析的代码行，并使用*号标记执行过的行。由于Line coverage只记录代码行是否被执行过，所以其执行开销要比Line counting小。
</paragraph>

<paragraph>
此外，Profile对话框还提供了Merge功能，用以把多次运行Profile之后的统计结果组合起来。如果你正在使用Function coverage功能，则会看到是否测试了所有函数；如果你正在使用Function timing功能，则会看到以往分析与本次分析所有合并运行的累计时间。
</paragraph>

<paragraph name="IDE环境下Profile的使用">
- 对于涉及函数分析的功能
<list>
<li>选择Project->Settings->Link，选择Enable profiling复选框</li>
<li>重建项目</li>
<li>选择Build->Profile，弹出Profile对话框</li>
<li>做必要设置后，选择OK，开始运行程序</li>
</list>
</paragraph>

<paragraph>
- 对于涉及行分析的功能
<list>
<li>选择Project->Settings->Link，选择Enable profiling复选框和Generate debug info复选框</li>
<li>选择Project->Settings->C/C++，选择Line Numbers Only</li>
<li>重建项目</li>
<li>选择Build->Profile，弹出Profile对话框</li>
<li>做必要设置后，选择OK，开始运行程序</li>
</list>
</paragraph>


<paragraph name="配置Profile的三种方式">
- 修改profiler.ini文件
</paragraph>

<paragraph>
profiler.ini位于VC98\bin目录下，在其[profiler]段中，你可以指定不参与分析的LIB文件或OBJ文件。比如：
<code>
[profiler]
exclude:user32.lib
exclude:gdi32.lib
</code>
</paragraph>

<paragraph>
- 在Profile对话框中指定选项
</paragraph>

<paragraph>
若你选择了Funciton timing、Function coverage或Line coverage选项，则你可以在Advanced settings中指定进一步的范围，比如：你希望Profile只分析SampleApp.cpp文件中特定范围内的代码，可以在Advanced settings中填入，<em>/EXCALL /INC SampleApp.cpp(30-67)</em>。又如：你希望file1.obj和file2.obj不参与分析，则可以在Advanced settings中填入，<em>/EXC file1.obj /EXC file2.obj</em>。再如：你希望只描述指定函数，则可以在Advanced settings中填入，<em>/SF ?SampleFunc@@YAXPAH@@</em>，紧跟SF参数的是特定函数的修饰符名，获取该名称的最简单的方式是在创建项目时生成的MAP文件中查找。
</paragraph>

<paragraph>
SF，EXCALL，EXC，INC都是PREP的命令行参数，有关其他参数的详细说明可以通过在命令行提示符输入PREP /H得到。
</paragraph>

<paragraph>
- 编写批命令文件
</paragraph>

<paragraph>
可以参考fcount.bat、fcover.bat、ftime.bat、lcount.bat以及lcover.bat
</paragraph>

<paragraph name="从Profile中输出数据">
PLIST /T命令允许PLIST将.PBT文件内容以制表格式输出到文本文件中，该格式适合输入到电子表格或数据库中。比如：PLIST /T MYPROG > MYPROG.TXT，生成的MYPROG.TXT可以利用profiler.xlm（位于VC98\bin目录下）导入到Microsoft Excel电子表格中。
</paragraph>

<paragraph name="注意">
- 通常，分析整个程序的意义不大，因为大多数Windows应用程序，主要时间花费在消息等待上，因此精确定位要分析的代码，可以加快Profile的执行速度，提高其分析准确度。在Profile执行期间尽量关闭其他不相干的应用程序。
</paragraph>

<paragraph>
- 若启用了远程调试，则不能够从Build菜单中调用Profile功能。
</paragraph>

<paragraph>
- 对于inline函数，编译器以实际代码替换函数调用，因此inline函数不生成.MAP文件或CALL指令，所以当执行这样的函数时，Profile将无法得知，花费时间、运行次数等数据都归属于调用该函数的函数。Profile可以提供有关inline函数的行一级的运行次数和覆盖信息。
</paragraph>

<paragraph>
- 对于多线程应用程序，Profile的行为取决于你所选择的分析方式，对于Line counting和Line coverage，Profile并未区分线程之间有何不同，它将包含当前运行的所有线程。对于Function timing、Function coverage和Function counting，分析结果取决于线程，你可以用以下方式分析一个独立线程：
<list>
<li>将线程的主函数声明为初始函数（用PREP /SF选项）</li>
<li>包含程序中的所有函数（不要使用PREP /EXC选项）</li>
</list>
</paragraph>

<paragraph>
否则，分析结果很难解释。
</paragraph>

<paragraph>
(完)
</paragraph>

</article>