功能分析困境 - Visual Studio 2010 Ultimate

时间:2011-04-02 20:48:47

标签: c# visual-studio-2010 refactoring profiling

我正在尝试分析我的应用程序,以便在重构之前和之后监视函数的效果。我已经对我的应用程序进行了分析,并查看了摘要我注意到Hot Path列表没有提到我使用的任何函数,它只提到函数到Application.Run()

我很擅长分析,并想知道如何通过MSDN documentation显示有关热路径的更多信息;

MSDN示例:

MSDN Example

我的结果:

Hot Path Summary

我注意到在输出窗口中有许多与加载符号时失败相关的消息,其中一些在下面;

Failed to load symbols for C:\Windows\system32\USP10.dll.  
Failed to load symbols for C:\Windows\system32\CRYPTSP.dll.
Failed to load symbols for (Omitted)\WindowsFormsApplication1\bin\Debug\System.Data.SQLite.dll.
Failed to load symbols for C:\Windows\system32\GDI32.dll.  
Failed to load symbols for C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_41e6975e2bd6f2b2\comctl32.dll.
Failed to load symbols for C:\Windows\system32\msvcrt.dll. 
Failed to load symbols for C:\Windows\Microsoft.NET\Framework\v4.0.30319\nlssorting.dll.
Failed to load symbols for C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll.  Failed to load symbols for
C:\Windows\Microsoft.Net\assembly\GAC_32\System.Transactions\v4.0_4.0.0.0__b77a5c561934e089\System.Transactions.dll.
Unable to open file to serialize symbols: Error VSP1737: File could not be opened due to sharing violation: - D:\(Omitted)\WindowsFormsApplication1110402.vsp

(使用代码工具格式化,因此可读)

感谢您的任何指示。

2 个答案:

答案 0 :(得分:13)

摘要视图中显示的“热路径”是最昂贵的调用路径,基于包含样本的数量(来自函数的样本以及函数调用的函数的样本)和独占样本(仅来自函数的样本) )。 “示例”就是当探查器的驱动程序捕获堆栈时函数位于堆栈顶部的事实(这发生在非常小的时间间隔)。因此,函数的样本越多,执行的越多。

默认情况下,对于采样分析,启用了一个名为“ Just My Code ”的功能,该功能可以隐藏来自非用户模块的堆栈上的功能(它将显示1个非用户功能的深度)如果由用户函数调用;在您的情况下Application.Run)。来自未加载符号的模块或来自Microsoft的已知模块的函数将被排除在外。摘要视图中的“热门路径”表示最昂贵的堆栈没有任何内容,而不是分析器认为是您的代码(Main除外)。 MSDN中的示例显示了更多功能,因为PeopleTrax.*PeopleNS.*函数来自“用户代码”。单击摘要视图中的“显示所有代码”链接可以关闭“仅我的代码”,但我不建议这样做。

在摘要视图中查看“完成个人工作的功能”。这将显示具有最高独占样本计数的函数,因此,基于分析方案,调用最昂贵的函数。您应该在这里看到更多函数(或函数调用的函数)。此外,“功能”和“调用树”视图可能会显示更多详细信息(报告顶部有一个下拉列表,用于选择当前视图)。

至于你的符号警告,大多数都是预期的,因为它们是Microsoft模块(不包括System.Data.SQLite.dll)。虽然您不需要这些模块的符号来正确分析您的报告,但如果您在“工具 - >选项 - >调试 - >符号”中选中“Microsoft符号服务器”并重新打开报告,则这些符号模块应该加载。请注意,第一次打开报告需要更长的时间,因为需要下载和缓存符号。

关于无法将符号序列化到报告文件中的另一个警告是文件无法写入的结果,因为它是由阻止写入的其他内容打开的。符号序列化是一种优化,允许分析器在下一次分析时直接从报告文件加载符号信息。如果没有符号序列化,分析只需要执行与首次打开报告时相同的工作量。

最后,您可能还想尝试检测,而不是在性能分析会话设置中进行采样。 Instrumentation修改您指定的模块以捕获每个函数调用上的数据(请注意,这可能会导致更大,更大的.vsp文件)。仪器非常适合专注于特定代码片段的时序,而采样则是一般低开销分析数据采集的理想选择。

答案 1 :(得分:6)

如果我谈一些关于剖析,哪些有效以及哪些无效,你是否介意过多?

让我们组成一个人工程序,其中一些语句正在做可以优化的工作 - 即它们并非真正必要。 他们是“瓶颈”。

子例程foo运行一个需要一秒钟的CPU绑定循环。 同时假设子程序CALL和RETURN指令与其他所有指令相比都是无关紧要或零时间。

子程序bar调用foo 10次,但这些时间中有9次是不必要的,这是您事先不知道的,直到您的注意力被指示为止才能分辨出来。

子例程ABC,...,J是10个子例程,每个子例程调用bar一次。

顶级例程main每次调用AJ。{/ p>

所以总调用树看起来像这样:

main
  A
    bar
      foo
      foo
      ... total 10 times for 10 seconds
  B
    bar
      foo
      foo
      ...
  ...
  J
    ...
(finished)

这需要多长时间?显然是100秒。

现在让我们来看看分析策略。 堆叠样本(例如1000个样本)以均匀的间隔进行。

  1. 有自我时间吗?是。 foo占用自我时间的100%。 这是一个真正的“热点”。 这有助于您找到瓶颈吗?不。因为它不在foo

  2. 热门路径是什么?好吧,堆栈样本如下所示:

    主要 - > A - >吧 - > foo(100个样本,或10%)
    主要 - > B - >吧 - > foo(100个样本,或10%)
       ...
    主要 - > J - >吧 - > foo(100个样本,或10%)

  3. 有10条热门路径,而且它们都不足以让你获得更多的加速。

    如果您发送到GUESS,如果配置文件允许,您可以bar调用树的“根”。然后你会看到这个:

    bar -> foo (1000 samples, or 100%)
    

    然后你会知道foobar各自独立负责100%的时间,因此是寻找优化的地方。 你看foo,但当然你知道问题不在那里。 然后,您查看bar并看到对foo的10次调用,您会看到其中9个是不必要的。问题解决了。

    如果您没有向GUESS发生,而且探查者只是向您显示包含每个例程的样本的百分比,您会看到:

    main 100%
    bar  100%
    foo  100%
    A    10%
    B    10%
    ...
    J    10%
    

    这会告诉您查看mainbarfoo。您发现mainfoo是无辜的。你看看bar调用foo的位置,你看到了问题,所以它已经解决了。

    如果除了显示函数之外,还可以显示调用函数的行,这一点更加清晰。这样,无论函数在源文本方面有多大,都可以找到问题。

    现在,让我们更改foo,使其sleep(oneSecond)而不是CPU绑定。这是如何改变的?

    这意味着挂钟仍需要100秒,但CPU时间为零。在仅CPU采样器中进行采样将显示 nothing

    所以现在你被告知要尝试仪器而不是采样。它包含在它告诉你的所有内容中,它还告诉你上面显示的百分比,所以在这种情况下你可以找到问题,假设bar不是很大。 (可能有理由编写小函数,但是应该让分析器满足其中之一吗?)

    实际上,采样器的主要问题是它不能在sleep(或I / O或其他阻塞)期间进行采样,并且它不会显示代码行百分比,只显示功能百分比。

    顺便说一下,1000个样本可以为您提供精确的精确百分比。假设您采用较少的样本。你究竟需要找多少瓶颈?好吧,因为瓶颈在90%的时间都在堆栈上,如果你只拿了10个样本,它就会在9个左右,所以你仍然可以看到它。 如果你甚至只拿了3个样本,它们在两个或多个样本中出现的概率是97.2%。**

    当您的目标是找到瓶颈时,高采样率会被高估。

    无论如何,这就是我依赖random-pausing的原因。

    **我是如何获得97.2%的?把它想象成掷硬币3次,这是一个非常不公平的硬币,其中“1”意味着看到了瓶颈。有8种可能性:

           #1s  probabality
    0 0 0  0    0.1^3 * 0.9^0 = 0.001
    0 0 1  1    0.1^2 * 0.9^1 = 0.009
    0 1 0  1    0.1^2 * 0.9^1 = 0.009
    0 1 1  2    0.1^1 * 0.9^2 = 0.081
    1 0 0  1    0.1^2 * 0.9^1 = 0.009
    1 0 1  2    0.1^1 * 0.9^2 = 0.081
    1 1 0  2    0.1^1 * 0.9^2 = 0.081
    1 1 1  3    0.1^0 * 0.9^3 = 0.729
    

    所以看到它2或3次的概率是.081 * 3 + .729 = .972