【发布时间】:2010-10-27 18:35:40
【问题描述】:
您使用哪些工具来确定代码的效率?您是否使用运行大量测试的本地应用程序或某些商业产品?您是否使用自己的知识来测试代码的某些区域,或者使用一些工具来分析代码的弱点?
【问题讨论】:
标签: performance profiling code-analysis
您使用哪些工具来确定代码的效率?您是否使用运行大量测试的本地应用程序或某些商业产品?您是否使用自己的知识来测试代码的某些区域,或者使用一些工具来分析代码的弱点?
【问题讨论】:
标签: performance profiling code-analysis
插入:让我们看看“统计显着性”。
假设某处有函数调用指令。你不一定能看到它——一个类、一个宏或编译器可能已经把它塞进去了。 附近还有其他对同一个函数的调用,但是这个调用是在一个循环中,或者它的参数使得这个调用需要很长时间。事实上,如此多的时间,如果可以使此调用花费零时间,那么总执行时间将减少一定数量,例如 90%。 (不可能?一点也不。)时间会精确吗?不会。调用图会查明它吗?不。电话计数会查明吗?不会。因为问题不在函数层面,而是在调用指令层面。
不知何故,程序在某个时间点随机停止,并检查其状态。如果指令可以“归零”,它会在 90% 的时间内停止吗?当然——有 90% 的概率,指令将被精确定位在堆栈上等待其“工作”完成。
事实上,如果你随机停止它 20 次,那条指令平均会在堆栈上 18 次,标准偏差为 +/- 1.3 次。
这在统计上是否显着?你敢打赌。
你需要大量样本吗?你敢打赌你不需要。
假设百分比很小,例如 10% 或 5%。 同样的原则也适用。
事实上,无论抽取多少样本,超过 1 个样本的任何指令都具有统计显着性,并且是“热点”、“瓶颈”或任何你想称之为的指令。如果您可以删除它、减少调用它或以某种方式减少它,它将节省大量时间。 (有些你不能,比如“call _main”,但有些你可以。你只需要找到它们。)
当然,我的代码永远不会这么愚蠢,不是吗?那么,证明它。
好的,现在回到故事上来。 . .
原始答案:我早在很久以前就听说过分析器,我认为它们一定很整洁,但我无法使用它们/它。我正在研究一个嵌入式处理器(一个 8086 intel 芯片),它似乎需要很长时间才能在显示屏上绘制一些浮点数。硬件人员建议从他们的大量提供 - 添加计时器芯片,这样我就可以看到事情需要多长时间。然而,一个周末,我用一个英特尔“Blue Box”在线仿真器启动它并让它运行起来。虽然它很慢,但我想知道“它到底在做什么?”。所以我只是停下来找出答案。 PC 在浮点库中(没有 FP 芯片)。这并不奇怪,因为它正在绘制浮点数,但我想知道更多。所以我(费力地)读取十六进制内存以跟踪调用堆栈。你猜怎么了?它正在取要绘制的数字,除以 10,转换为整数,转换回浮点数,减法等等,只是为了得到下一个要绘制的数字。不用说,有更好的方法来做到这一点,从而实现了大约 10 倍的加速。 这是在 一 (1) 个样本中发现的!
在另一种情况下,在 68K 芯片上,出现了一些缓慢。同样,分析器不可用,但调试器“adb”可用,所以虽然它很慢,但我停止了几次。 PC 在数学库中,实际上在 32 位整数乘法例程中。查找堆栈,我发现了这段代码:
struct {...} a[...];
int i;
for (i = 0; i < ...; ++i){ ... a[i] ... }
那里没有乘法的调用 - 发生了什么?结果是,对于a[i],编译器必须将i 乘以数组元素大小。由于i 是 32 位(在该编译器中),它会生成对 32 位乘法例程的调用,并且堆栈会精确定位该调用指令。通过将i 声明为short,循环速度提高了两倍!
有什么意义?如果您在程序运行缓慢时随机抽取样本,PC 会告诉您它在做什么,但调用堆栈会告诉您为什么,并直接引导您解决问题。现在,除非问题真的很严重,否则您可能需要采集多个样本。任何出现在 >1 个样本上的陈述都是您可以怀疑的。请注意,它甚至可以精确定位 语句、指令,而不是像函数这样的大块代码。 This technique 可能“又快又脏”,但非常有效。
添加:如果您反复执行此操作,您可以在同一个软件中解决一个又一个问题。例如,如果您获得 3 倍的加速,以前可能无关紧要的小性能问题现在会消耗 3 倍的剩余时间。这使它们更容易被样本击中。您可能必须添加一个临时的外部循环以使其运行足够长的时间进行采样。通过这种方式,我看到了more than 40 times 的复合加速因子。
【讨论】:
分析器对于查看您花费最多时间的代码非常有用。市面上有许多分析工具,通常它们特定于您所在的平台/开发环境。
对于小案例,我在代码中使用了简单的计时器(动作结束时的系统时间 - 动作开始时的系统时间)。
一条重要规则:永远不要假设您刚刚进行的性能优化实际上会运行得更快。始终验证!
【讨论】:
老实说,我使用NUnit。如果我的代码花费的时间太长或似乎无法扩展,我会编写一个测试来模拟应用程序中性能不佳的部分。然后我看看在代码中进行强度替换以减少运行时间并验证我没有破坏任何东西。
如果这仍然不能满足您的需求,那么您需要找到并应用分析器,但您至少会有一个测试用例,您可以在无需打开/加载应用程序的情况下尝试您的假设,跟踪调用与手头任务无关的应用程序元素。
【讨论】:
我使用 Valgrind 及其工具 Callgrind。这是一个很棒的工具。 Valgrind 基本上是一个虚拟机:
Valgrind 本质上是一个虚拟的 使用即时 (JIT) 的机器 编译技术,包括 动态重新编译。无从 原始程序曾经运行过 直接在主机处理器上。 相反,Valgrind 首先翻译 编程为一个临时的、更简单的形式 称为中间表示 (IR),与处理器无关, 基于 SSA 的表格。转换后, 一个工具(见下文)是免费的 它想要的任何转换 在 IR 上,在 Valgrind 翻译之前 IR 回到机器代码并让 主处理器运行它。虽然 它可以使用动态翻译(即 是,主机和目标处理器是 来自不同的架构),它 没有。 Valgrind 重新编译二进制文件 在主机和目标上运行的代码(或 模拟)相同的CPU 架构。
Callgrind 是一个基于此的分析器。主要好处是您不必运行应用程序数小时即可获得可靠的结果。几秒钟就足够了,因为 Callgrind 是一个非探测分析器,
另一个基于 Valgrind 的工具是 Massif。我用它来分析堆内存使用情况。它非常好用,它可以为您提供内存使用情况的快照 - 详细信息 WHAT 拥有多少内存百分比,以及是谁将它放在那里的。
还有一个 Valgrind 工具——DRD(和 Helgrind)。我使用它们来跟踪我的代码中的死锁和数据竞争,以及线程 API 滥用。
【讨论】:
我确实使用工具来完成这项工作。否则我发现自己很难做到......(这是我的意见,我实际上从未尝试过)
在 Linux 上,我使用 Valgrind,它提供了一些有用的工具来分析您的代码。至于 Valgrind 主页:
它在以下平台上运行:X86/Linux、AMD64/Linux、PPC32/Linux、PPC64/Linux。
.. 它是免费的(就像免费啤酒一样)并且是开源的。
虽然我没有太多使用它们,但在另一个平台上您可以使用Purify/Quantify(IBM 产品)。它们是商业工具。正如 Will 报告的那样,它们是在 PurifyPlus 包中提供的,Quantify 将帮助您分析您的应用程序。
这就是我用过的所有东西...... :-)
【讨论】:
这个问题模棱两可。从运行时或内存的角度来看高效? 运行特定代码段的上下文、应用程序的架构以及可能被抛出的数据也是所有因素。
顺便说一句,有人提到净化;还有它的姊妹产品 Quantify。
【讨论】:
对于 .NET,我会推荐 NDepend。
【讨论】: