function分析困境 – Visual Studio 2010 Ultimate

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

我很擅长分析,并想知道如何通过MSDN文档certificate我可以获得有关Hot Path的更多信息;

MSDN示例:

MSDN示例

我的结果:

热门路径摘要

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

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 

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

谢谢你的任何指示。

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

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

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

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

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

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

如果我谈一些关于剖析,什么有用,什么不对,你是否介意太多?

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

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

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

子程序ABC ,…, J是10个子程序,它们每次调用一次。

顶级例程main每次调用AJA

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

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

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

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

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

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

    main – > A – > bar – > foo(100个样本,或10%)
    main – > B – > bar – > foo(100个样本,或10%)

    main – > J – > bar – > foo(100个样本,或10%)

有10条热门路径,它们都不够大,无法为您带来更多的加速。

如果您发现GUESS,如果PROFILER允许,您可以使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采样器中进行采样不会显示任何内容

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

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

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

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

无论如何,这就是我依赖随机暂停的原因 。

**我怎么得到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