人们可以使用分析器,但为什么不停止程序呢?

时间:2008-11-05 19:49:24

标签: performance optimization profiling

如果某个程序正在使单线程程序占用它的10倍,那么就可以在其上运行一个分析器。您也可以通过“暂停”按钮暂停它,您将看到它正在做什么。

即使它比它应该慢了10%,如果你停止它多次,不久你会看到它反复做不必要的事情。通常问题是在堆栈中间某处不是真正需要的函数调用。这不能衡量问题,但肯定能找到它。

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

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

然后假设只拍摄了10个样本。在10个样本中看到问题的次数遵循binomial distribution,并且看到它0次的概率是.028。看到它1次的概率是.121。 2次,概率为.233,而3次则为.267,之后下降。由于看到它少于两次的概率是.028 + .121 = .139,这意味着看到它两次或更多次的概率是1 - .139 = .861。一般规则是,如果您看到可以修复两个或更多样本的内容,则值得修复。

在这种情况下,在10个样本中看到它的几率是86%。如果你是14%没有看到它的人,那就去做更多的样品。 (如果样本数量增加到20,那么两次或多次观察的几率会增加到99%以上。)所以它没有经过精确测量,但已经准确找到了,重要的是要了解它它可能很容易成为分析器无法实际找到的东西,例如涉及数据状态的东西,而不是程序计数器。

17 个答案:

答案 0 :(得分:51)

在Java服务器上,连续2-3次快速 Ctrl - 打破并获得所有正在运行的线程的2-3个软件包,这一直是一个巧妙的技巧。只需查看所有线程“在哪里”,就可以非常快速地找出性能问题所在。

这种技术可以在2分钟内发现比我所知的任何其他技术更多的性能问题。

答案 1 :(得分:32)

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

答案 2 :(得分:26)

手动执行此操作无法真正称为“快速”或“有效”,但有几种分析工具可以自动执行此操作;也称为statistical profiling

答案 3 :(得分:15)

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

我更喜欢自动化工具而不是手动采样的原因是statistical power。当你有一个函数占用40%的运行时间时,手动抓取十个样本是很好的,因为平均来说你会得到四个样本,并且总是至少有一个样本。但是当你有一个平面轮廓时,你需要更多的样本,有数百个叶子函数,没有超过运行时间的1.5%。

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

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

我可以查看 trunk 函数并查看“我们将10%的时间用于碰撞”,但这不是很有帮助:我需要确切地知道 where < / em>在碰撞中,所以我知道要挤压哪些功能。只是“少做碰撞”只会让你到目前为止,特别是当它意味着抛弃功能时。我宁愿知道“我们在the octree的狭窄阶段中对缓存未命中平均花费600μs/帧,因为魔法导弹移动得如此之快并触及大量细胞,”因为那时我可以追踪到精确修复:更好的树或更慢的导弹。

如果在stricmp中存在20%的大块,那么手动取样会没问题,但我们的配置文件并非如此。相反,我有数百个功能,我需要从帧的0.6%到帧的0.4%。我需要每50μs功能削减10μs,每秒调用300次。为了达到这种精度,我需要更多的样品。

但从内心来看,Luke Stackwalker所做的就是你所描述的:每毫秒左右,它会暂停程序并记录callstack(包括IP的精确指令和行号)。有些程序只需要成千上万的样本进行有用的分析。

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

答案 4 :(得分:10)

程序员实际做的事情和他们推荐别人做的事情之间存在差异。

我知道有很多程序员(包括我自己)实际使用这种方法。它只能帮助找到最明显的性能问题,但它快速而且肮脏且有效。

但我不会真的告诉其他程序员这样做,因为解释所有注意事项需要很长时间。根据这种方法得出一个不准确的结论太容易了,并且在许多领域它根本不起作用。 (例如,该方法不会显示由用户输入触发的任何代码。)

就像在法庭上使用谎言探测器或“goto”声明一样,我们不建议您这样做,即使它们都有它们的用途。

答案 5 :(得分:10)

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

分析很棒,当你能做到时,它肯定会更加精致和精确。有时你不能,并且有一个可靠的备份是很好的。暂停技术就像你的电动工具距离太远或者餐馆用完时使用的手动螺丝刀。

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

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

另一方面,当性能要求超出“足够好”以真正推动信封时,分析器变得至关重要,这样您就可以尝试从所有紧密结合的前十或二十个热点中削减周期。即使您只是想在项目期间保持适度的性能要求,当您可以获得正确的工具来帮助您进行测量和测试,甚至将它们集成到您的自动化测试过程中时,它可以提供极大的帮助。

但是当电源耗尽(可以这么说)并且电池耗尽时,很清楚如何使用那把手动螺丝刀。

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

答案 6 :(得分:8)

如果我们提出问题“为什么不知道它?”那么答案就是主观的。据推测,为什么不能更好地了解它是因为分析提供了长期解决方案而不是当前的问题解决方案。它对多线程应用程序无效,对于花费大量时间渲染的游戏等应用程序无效。

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

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

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

通过分析:

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

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

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

  • 您可以让初级开发人员更容易想象他们的更改对您的代码的影响,因为他们可以在您离开海滩或酒吧时,在六个月内重新分析代码,或两者兼而有之。 Beach-pub,ftw。

由于企业代码应始终具有某种程度的分析,因为分析给予组织长时间的好处,因此分析的权重更大。代码越重要,您的分析和测试就越多。

您的方法有效,另一项是开发人员的工具箱。它只是通过剖析而超过了它。

答案 7 :(得分:7)

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

如果您必须避免使用分析器,最好的办法是使用记录器,然后应用减速因子来“猜测”真正的问题所在。然而,Profilers是更好的猜测工具。

在调试模式下点击暂停按钮的原因可能无法给出应用程序行为的真实情况,因为调试器会引入额外的可执行代码,这可能会减慢应用程序的某些部分。可以参考Mike Stall's blog post关于调试环境中应用程序减速的可能原因。该帖子揭示了某些原因,例如断点太多,异常对象的创建,未优化的代码等。关于未经优化的代码的部分很重要 - “调试”模式将导致大量优化(通常代码内嵌和重新被抛出窗口,启用调试主机(运行代码的过程)和IDE来同步代码执行。因此,在“调试”模式下反复按下暂停可能是一个坏主意。

答案 8 :(得分:7)

采样分析器仅在

时有用
  1. 您正在使用少量线程监视运行时。最好是一个。
  2. 每个线程的调用堆栈深度相对较小(以减少收集样本时难以置信的开销)。
  3. 您只关心挂钟时间而不关心其他电表或资源瓶颈。
  4. 您尚未检测用于管理和监视目的的代码(因此是堆栈转储请求)
  5. 您错误地认为删除堆栈框架是一种有效的绩效改进策略,无论固有成本(不包括被调用者)是否几乎为零
  6. 您不必费心去学习如何在工作中应用软件性能工程
  7. ....

答案 9 :(得分:6)

这些必须是您正在使用的一些简单示例,以便使用您的方法获得有用的结果。我无法想象一个项目,其中分析是有用的(通过任何方法),通过“快速有效”的方法可以获得不错的结果。启动和停止某些应用程序所需的时间已经使您的“快速”断言成为问题。

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

编辑: 关于“为什么不能更好地了解”?

根据我的经验,代码审查可以避免质量差的代码和算法,并且分析也可以找到这些代码和算法。如果你希望继续你的方法很棒 - 但我认为对于大多数专业人士来说,这是迄今为止在尝试的事情列表中,它永远不会得到积极的强化作为时间的充分利用。

对于小样本集来说似乎是非常不准确的,并且获取大量样本集会花费大量时间用于其他有用的活动。

答案 10 :(得分:6)

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

诀窍在于熟悉您的工具,并选择最适合您手头的工作。

答案 11 :(得分:5)

如果该程序位于生产并由付费客户或同事同时使用,该怎么办?分析器允许您在没有干扰的情况下进行观察(同样,因为根据Heisenberg principle,它也会受到一点打击。)

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

答案 12 :(得分:4)

单步执行代码非常适合查看详细信息和故障排除算法。这就像看到一棵树真的近距离地跟着树皮和树枝的每一条纹。

剖析可以让您了解整体情况,并快速识别故障点 - 例如向后退一步,查看整个森林并注意最高的树木。通过按执行时间长度对函数调用进行排序,可以快速识别出故障点的区域。

答案 13 :(得分:4)

多年前我用Commodore 64 BASIC这个方法。令人惊讶的是它的运作情况。

答案 14 :(得分:4)

编辑2008/11/25:好的,Vineet的回应终于让我看到了这里的问题。迟到总比没有好。

不知何故,这个想法在土地上松动,通过衡量绩效来发现性能问题。这是令人困惑的手段与目的。不知怎的,我很久以前通过单步执行整个程序来避免这种情况。我没有责备自己将速度降低到人的速度。我试图看看它是做错了还是不必要的事情。这就是如何快速制作软件 - 找到并删除不必要的操作。

这些天没有人耐心单步,但接下来最好的事情是随机选择一些周期并询问他们的原因是什么。 (这就是调用堆栈经常可以告诉你的。)如果它们中有很大一部分没有充分的理由,你就可以做些什么。

这些日子更难了,线程和异步是什么,但这就是调整软件的方式 - 通过寻找不必要的周期。不是看它有多快 - 我最后这样做。


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

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

  • 如果一个指令I在那个时间的分数P(I)的调用堆栈上,那么从程序中删除它,如果可以的话,将保存那么多。如果这不明显,请仔细考虑一下。

如果指令出现在M = 2或更多样本中,则在N中,其P(I)大约为M / N,并且肯定是显着的。

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

因此,性能调优的过程很简单,就是通过调用多个调用堆栈样本来提取指令(主要是函数调用指令)。 那些是森林里的高大树木。

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

我反对混淆,而不是反对分析者。它们为您提供了大量的统计数据,但大多数都没有给出P(I),大多数用户都没有意识到这就是重要的。

您可以谈论森林和树木,但是对于您可以通过修改代码修复的任何性能问题,您需要修改指令,特别是具有高P(I)的指令。所以你需要知道它们在哪里,最好不要玩Sherlock Holmes。堆栈采样可以准确地告诉您它们的位置。

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

答案 15 :(得分:3)

我通常将它用于超出时间片的实时程序。您无法手动停止和重新启动必须每秒运行60次的代码。

我还用它来追踪我编写的编译器的瓶颈。你不想试图手动打破这样的程序,因为你真的无法知道你是否在瓶颈所在的地方打破,或者只是在瓶颈之后当操作系统被允许回到停下来。此外,如果主要的瓶颈是你无法做任何事情,但是你想摆脱系统中所有其他大的瓶颈呢?如果你没有关于所有所在位置的良好数据,以及它们各自的相对影响,那么如何优先考虑哪些瓶颈首先进行攻击?

答案 16 :(得分:3)

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