45

如果某件事使单线程程序占用了 10 倍的时间,那么您可以在其上运行分析器。您也可以使用“暂停”按钮停止它,您将确切地看到它在做什么。

即使它只比应有的慢 10%,如果你停止它更多次,不久你就会看到它反复做不必要的事情。通常问题是堆栈中间某处的函数调用并不是真正需要的。这不能衡量问题,但确实可以找到问题。

编辑:反对意见主要假设您只取 1 个样本。如果您是认真的,请取 10。平均而言,任何导致某些百分比浪费(例如 40%)的代码行都会出现在该部分样本的堆栈上。瓶颈(在单线程代码中)无法隐藏。

编辑:为了说明我的意思,许多反对意见的形式是“没有足够的样本,所以你看到的可能完全是虚假的”——关于机会的模糊想法。但是,如果任何可识别的描述(不仅仅是在例程中或例程处于活动状态)在 30% 的时间内有效,那么在任何给定样本上看到它的概率是 30%。

然后假设只取了 10 个样本。问题在 10 个样本中出现的次数服从二项分布,出现 0 次的概率为 0.028。看到它 1 次的概率是 0.121。2 次,概率为 0.233,3 次为 0.267,之后下降。由于看到它少于两次的概率是 0.028 + .121 = .139,这意味着看到它两次或更多次的概率是 1 - .139 = .861。一般规则是,如果您看到可以在两个或更多样本上修复的内容,则值得修复。

在这种情况下,在 10 个样本中看到它的机会是 86%。如果你是那 14% 的人没有看到它,那就多采集一些样本,直到你看到为止。(如果样本数量增加到20个,看到两次或更多次的机会增加到99%以上。)所以它没有被精确测量,但它已经被精确地找到了,理解这一点很重要它很容易成为分析器实际上无法找到的东西,例如涉及数据状态的东西,而不是程序计数器。

4

17 回答 17

53

Ctrl在 Java 服务器上,Breaks连续执行 2-3 次快速操作并获得所有正在运行的线程的 2-3 次线程转储始终是一个巧妙的技巧。只需查看所有线程“在哪里”,就可以非常快速地确定性能问题所在。

与我所知道的任何其他技术相比,这种技术可以在 2 分钟内揭示更多的性能问题。

于 2008-11-25T12:23:13.517 回答
34

因为有时它有效,有时它会给你完全错误的答案。分析器在找到正确答案方面的记录要好得多,而且通常可以更快地到达那里。

于 2008-11-05T19:52:34.607 回答
27

手动执行此操作并不能真正称为“快速”或“有效”,但有几种分析工具可以自动执行此操作;也称为统计分析

于 2008-11-05T19:51:56.930 回答
15

调用堆栈采样是一种非常有用的分析技术,尤其是在查看可能在任意数量的地方花费时间的大型复杂代码库时。它具有通过挂钟时间测量 CPU 使用率的优势,这对交互性很重要,并且获取每个样本的调用堆栈可以让您了解调用函数的原因。我经常使用它,但我使用自动化工具,例如Luke StackwalkerOProfile以及各种硬件供应商提供的东西。

对于我所做的工作,我更喜欢自动化工具而不是手动采样的原因是统计能力。当你有一个函数占用 40% 的运行时间时,手动抓取 10 个样本是很好的,因为平均而言你会在其中得到 4 个样本,而且总是至少有一个。但是当你有一个扁平的配置文件时,你需要更多的样本,有数百个叶函数,没有一个占用超过 1.5% 的运行时间。

假设你有一个湖泊,里面有许多不同种类的鱼。如果湖中 40% 的鱼是鲑鱼(以及 60% 的“其他”),那么你只需要钓到 10 条鱼就知道湖里有很多鲑鱼。但是如果你有数百种不同种类的鱼,而且每个种类的数量不超过 1%,那么你需要钓到十多条鱼才能说“这个湖有 0.8% 的鲑鱼和 0.6% 的鳟鱼” 。”

同样,在我开发的游戏中,有几个主要系统,每个系统都调用数百个不同实体中的数十个函数,所有这些都每秒发生 60 次。其中一些函数的时间集中到常见操作(如malloc)中,但大多数都没有,而且在任何情况下,没有一个叶子每帧占用超过 1000 μs。

我可以查看主干函数并看到“我们将 10% 的时间用于碰撞”,但这并不是很有帮助:我需要确切地知道碰撞的位置,所以我知道要挤压哪些函数。只是“减少碰撞”只能让你走这么远,尤其是当它意味着放弃功能时。我宁愿知道“我们在八叉树的狭窄阶段平均花费 600 微秒/帧来处理缓存未命中,因为魔法导弹移动得如此之快并且触及了很多单元格”,因为这样我就可以找到确切的修复方法:要么是更好的树,要么是更慢的导弹。

如果有 20% 的大块,手动采样会很好stricmp,但是对于我们的配置文件,情况并非如此。相反,我需要从帧的 0.6% 到帧的 0.4% 获取数百个函数。我需要每 50 μs 的函数减少 10 μs,即每秒调用 300 次。为了获得这种精度,我需要更多的样本。

但本质上,Luke Stackwalker 所做的就是您所描述的:每毫秒左右,它会暂停程序并记录调用堆栈(包括IP的精确指令和行号)。有些程序只需要数万个样本就可以进行有用的分析。

(当然,我们之前已经讨论过这个问题,但我认为这是总结辩论的好地方。)

于 2011-11-28T00:46:14.527 回答
10

程序员实际做的事情和他们推荐别人做的事情是有区别的。

我知道很多程序员(包括我自己)实际使用这种方法。它只会真正有助于发现最明显的性能问题,但它又快又脏,而且很有效。

但我不会真的告诉其他程序员这样做,因为我要花很长时间来解释所有警告。根据这种方法很容易得出不准确的结论,而且有很多地方根本行不通。(例如,该方法不会显示任何由用户输入触发的代码)。

因此,就像在法庭上使用测谎仪或“goto”声明一样,我们只是不建议您这样做,即使它们都有其用途。

于 2008-11-05T20:09:39.343 回答
10

我对双方的宗教语气感到惊讶。

剖析很棒,而且当你可以做到时,它肯定会更加精致和精确。有时你不能,有一个值得信赖的备份很好。暂停技术就像您在电动工具太远或电池耗尽时使用的手动螺丝刀。

这是一个简短的真实故事。一个应用程序(一种批处理任务)在生产环境中运行了六个月,突然间运营商打电话给开发人员,因为它“太慢了”。他们不会让我们在生产中附加采样分析器!您必须使用已安装的工具。在不停止生产过程的情况下,只需使用 Process Explorer(操作员已经安装在机器上),我们就可以看到线程堆栈的快照。您可以浏览堆栈的顶部,使用回车键将其关闭,然后再次单击鼠标获取另一个快照。您可以每秒钟左右轻松获得一个样本。

很快就可以看到栈顶最常出现在数据库客户端库 DLL(等待数据库)中,还是在另一个系统 DLL(等待系统操作)中,或者实际上在某个方法中应用程序本身。在这种情况下,如果我没记错的话,我们很快注意到应用程序在 10 次中有 8 次在系统 DLL 文件调用中读取或写入网络文件。果然,最近的“升级”改变了文件共享的性能特征。如果没有一种快速而肮脏的(系统管理员认可的)方法来查看应用程序在生产中做了什么,我们将花费更多的时间来尝试衡量问题,而不是纠正问题。

另一方面,当性能要求超出“足够好”而真正突破极限时,分析器就变得必不可少,这样您就可以尝试从所有密切相关的前 10 或 20 个热点中缩短周期。即使您只是想在项目期间保持适度的性能要求,当您可以获得正确的工具来帮助您测量和测试,甚至将它们集成到您的自动化测试过程中时,它也会非常有帮助。

但是当电源没电(可以这么说)并且电池没电时,很高兴知道如何使用手动螺丝刀。

所以直接的答案是:知道你可以从停止程序中学到什么,但也不要害怕精密工具。最重要的是知道哪些工作需要哪些工具。

于 2009-10-28T07:13:11.103 回答
8

如果我们提出“为什么不为人所知?”这个问题。那么答案将是主观的。据推测,它之所以不为人所知的原因是因为分析提供了一个长期的解决方案,而不是当前的问题解决方案。它对多线程应用程序无效,并且对于花费大量时间渲染的游戏等应用程序无效。

此外,在单线程应用程序中,如果您有一个希望消耗最多运行时间的方法,并且您希望减少所有其他方法的运行时间,那么确定哪些次要方法将更加难以集中精力首先。

您的分析过程是一种可接受的方法,可以而且确实有效,但分析可以为您提供更多信息,并有利于向您展示更详细的性能改进和回归。

如果您有良好检测的代码,那么您可以检查的不仅仅是特定方法的持续时间;你可以看到所有的方法。

使用分析:

  • 然后,您可以在每次更改后重新运行您的方案,以确定性能改进/退化的程度。

  • 您可以在不同的硬件配置上分析代码,以确定您的生产硬件是否足够。

  • 您可以在负载和压力测试场景下分析代码,以确定信息量如何影响性能

  • 您可以让初级开发人员更轻松地可视化他们对您的代码所做的更改的影响,因为他们可以在您离开海滩或酒吧或两者兼而有之时在六个月内重新分析代码。海滩酒吧,英尺。

剖析被赋予了更多的权重,因为企业代码应该始终具有某种程度的剖析,因为它为组织提供了较长时间的好处。代码越重要,您进行的分析和测试就越多。

您的方法是有效的,另一个项目是开发人员的工具箱。它只是被分析所压倒。

于 2008-11-06T14:16:04.660 回答
8

在“调试”模式下执行程序期间点击暂停按钮可能无法提供正确的数据来执行任何性能优化。坦率地说,这是一种粗略的剖析形式。

如果你必须避免使用分析器,更好的选择是使用记录器,然后应用减速因子来“猜测”真正的问题在哪里。然而,探查器是更好的猜测工具。

在调试模式下点击暂停按钮可能无法真实反映应用程序行为的原因是因为调试器引入了额外的可执行代码,可能会减慢应用程序的某些部分。关于调试环境中应用程序速度变慢的可能原因,可以参考Mike Stall 的博客文章。该帖子阐明了某些原因,例如断点过多、创建异常对象、未优化代码等。关于未优化代码的部分很重要 - “调试”模式将导致大量优化(通常是代码内联和重新ordering) 被抛出窗口,以使调试主机(运行代码的进程)和 IDE 同步代码执行。因此,在“调试”模式下反复点击暂停可能不是一个好主意。

于 2008-11-23T02:55:08.157 回答
8

采样分析器仅在以下情况下有用

  1. 您正在监视具有少量线程的运行时。最好是一个。
  2. 每个线程的调用堆栈深度相对较小(以减少收集样本时令人难以置信的开销)。
  3. 您只关心挂钟时间,而不关心其他仪表或资源瓶颈。
  4. 您没有为管理和监视目的检测代码(因此堆栈转储请求)
  5. 您错误地认为删除堆栈帧是一种有效的性能改进策略,无论固有成本(不包括被调用者)实际上是否为零
  6. 您不必费心去学习如何在日常工作中应用软件性能工程
  7. ……
于 2009-07-27T21:42:01.577 回答
7

这些必须是您正在使用的一些琐碎示例,以便通过您的方法获得有用的结果。我想不出一个分析有用的项目(通过任何方法),用你的“快速有效”的方法会得到不错的结果。启动和停止某些应用程序所花费的时间已经使您的“快速”断言受到质疑。

同样,对于非平凡的程序,您提倡的方法是无用的。

编辑:关于“为什么不为人所知”?

根据我的经验,代码审查会避免劣质代码和算法,并且分析也会发现这些。如果您希望继续使用您的方法,那就太好了 - 但我认为对于大多数专业社区来说,这在尝试的事情清单上太远了,它永远不会得到积极的强化,因为它不能很好地利用时间。

对于小样本集,它似乎非常不准确,并且获得大样本集将花费大量时间,而这些时间本来可以更好地用于其他有用的活动。

于 2008-11-06T14:16:38.713 回答
7

堆栈跟踪快照仅允许您查看应用程序的频闪 X 射线。您可能需要更多积累的知识,而探查器可能会为您提供这些知识。

诀窍是充分了解您的工具并选择最适合手头工作的工具。

于 2009-10-17T09:21:10.667 回答
5

如果该程序正在生产中并被付费客户或同事同时使用怎么办?剖析器允许您在不干扰的情况下进行观察(同样多,因为根据海森堡原理,它当然也会受到一点影响)。

分析还可以为您提供更丰富、更详细的准确报告。从长远来看,这将更快。

于 2008-11-05T19:53:34.137 回答
4

编辑 2008/11/25:好的,Vineet 的回应终于让我明白了这里的问题所在。迟到总比不到好。

不知何故,通过测量性能发现性能问题的想法在这片土地上变得松散了。那是混淆手段和目的。不知何故,我很久以前就通过单步执行整个程序来避免这种情况。我并没有责备自己把它放慢到人类的速度。我试图看看它是否做错了或不必要的事情。这就是如何使软件快速 - 查找并删除不必要的操作。

这些天没有人有耐心单步执行,但下一个最好的事情是随机选择多个周期并询问他们的原因是什么。(这就是调用堆栈通常可以告诉您的。)如果其中很大一部分没有充分的理由,您可以采取一些措施。

这些天来,线程和异步变得更加困难,但这就是调整软件的方式——通过寻找不必要的周期。不是看它有多快——我最后这样做了。


这就是为什么对调用堆栈进行采样不能给出错误的答案,以及为什么不需要很多样本。

在感兴趣的时间间隔内,当程序花费的时间超出您的预期时,调用堆栈会持续存在,即使您没有对其进行采样。

  • 如果一条指令 I 在调用堆栈上的部分时间为 P(I),如果可以的话,将其从程序中删除将节省那么多。如果这不明显,请考虑一下。

如果指令出现在 M = 2 个或更多样本上,在 N 中,它的 P(I) 大约为 M/N,并且绝对是显着的。

您看不到指令的唯一方法是当指令不在调用堆栈上时神奇地计时所有样本。它只存在一小部分时间的简单事实就是将它暴露给您的探测器。

因此,性能调优的过程是一个简单的问题,即通过打开调用堆栈的多个样本来挑选指令(主要是函数调用指令)。那是森林里的大树。

请注意,我们不必关心调用图、函数需要多长时间、调用次数或递归。

我反对混淆,而不是反对分析器。他们给了你很多统计数据,但大多数没有给出 P(I),而且大多数用户没有意识到这很重要。

您可以谈论森林和树木,但是对于您可以通过修改代码来解决的任何性能问题,您需要修改指令,特别是具有高 P(I) 的指令。所以你需要知道那些在哪里,最好不要扮演福尔摩斯。堆栈采样会告诉您它们的确切位置。

这种技术很难在多线程、事件驱动或生产系统中使用。这就是分析器(如果他们报告 P(I))可以真正提供帮助的地方。

于 2008-11-05T20:00:11.657 回答
4

单步执行代码非常适合查看细节和故障排除算法。这就像真正近距离观察一棵树,并分别追踪每根树皮和树枝。

通过剖析,您可以看到全局,并快速识别故障点——例如后退一步,查看整个森林并注意最高的树木。通过按执行时间长度对函数调用进行排序,您可以快速识别出问题点所在的区域。

于 2008-11-05T20:38:30.673 回答
4

多年前,我将这种方法用于Commodore 64 BASIC 。令人惊讶的是它的效果如何。

于 2008-11-06T23:05:27.057 回答
3

我通常在超出时间片的实时程序上使用它。您不能手动停止和重新启动必须每秒运行 60 次的代码。

我还用它来追踪我编写的编译器中的瓶颈。您不想尝试手动破坏这样的程序,因为您真的无法知道您是在瓶颈所在的位置中断,还是在允许操作系统重新进入的瓶颈之后的位置中断停下来。此外,如果主要瓶颈是您无能为力的事情,但您想摆脱系统中所有其他较大的瓶颈怎么办?当您没有关于它们都在哪里以及它们各自的相对影响是什么的良好数据时,您如何确定首先要攻击哪些瓶颈?

于 2008-11-06T14:51:04.120 回答
3

程序越大,分析器就越有用。如果您需要优化包含数千个条件分支的程序,分析器可能是必不可少的。输入最大的测试数据样本,完成后将分析数据导入 Excel。然后根据实际数据检查您对可能热点的假设。总有惊喜。

于 2008-11-23T00:45:37.577 回答