【问题标题】:Analyzing Code for Efficiency?分析代码以提高效率?
【发布时间】:2010-10-27 18:35:40
【问题描述】:

您使用哪些工具来确定代码的效率?您是否使用运行大量测试的本地应用程序或某些商业产品?您是否使用自己的知识来测试代码的某些区域,或者使用一些工具来分析代码的弱点?

【问题讨论】:

    标签: performance profiling code-analysis


    【解决方案1】:

    插入:让我们看看“统计显着性”。

    假设某处有函数调用指令。你不一定能看到它——一个类、一个宏或编译器可能已经把它塞进去了。 附近还有其他对同一个函数的调用,但是这个调用是在一个循环中,或者它的参数使得这个调用需要很长时间。事实上,如此多的时间,如果可以使此调用花费零时间,那么总执行时间将减少一定数量,例如 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 的复合加速因子。

    【讨论】:

    • 这种技术肯定是有效的。本周我通过另一个 Stack Overflow 帖子 *.com/questions/375913 发现了它,并在我的一个应用程序上进行了尝试,该应用程序被许多内部和外部用户使用。结果是在第一次迭代中整体加速了 7 倍 (!) - 在一个特定(典型)数据集上从 16.5 分钟到 2.2 分钟。
    • @Peter:为了找到问题,您需要采集多少堆栈样本?如果你得到了 7 倍的加速,这意味着每个样本(至少)有 6/7 的概率显示它。
    • @Mike Dunlavey:我只需要 3-4 个。那个时候就已经很清楚了。在下一次迭代中,我花了 20 次,它给了我一个想法,让我的应用程序中的另一种操作模式(实际上是整个应用程序中最耗时的)加速了 3.3 倍(msquant.sourceforge.net )。
    • @Peter:这看起来是一个非常有趣的产品。
    【解决方案2】:

    这称为profiling。对于各种不同的语言,有许多现成的工具可帮助您确定应用程序的瓶颈所在。例如,TPTP 的 Java 工具集可以向您展示性能瓶颈在哪些地方下降到单个方法级别,如果您愿意的话。当然,有时您真正需要的只是读取系统计时器,以大致了解一段代码。

    【讨论】:

      【解决方案3】:

      分析器对于查看您花费最多时间的代码非常有用。市面上有许多分析工具,通常它们特定于您所在的平台/开发环境。

      对于小案例,我在代码中使用了简单的计时器(动作结束时的系统时间 - 动作开始时的系统时间)。

      一条重要规则:永远不要假设您刚刚进行的性能优化实际上会运行得更快。始终验证!

      【讨论】:

        【解决方案4】:

        老实说,我使用NUnit。如果我的代码花费的时间太长或似乎无法扩展,我会编写一个测试来模拟应用程序中性能不佳的部分。然后我看看在代码中进行强度替换以减少运行时间并验证我没有破坏任何东西。

        如果这仍然不能满足您的需求,那么您需要找到并应用分析器,但您至少会有一个测试用例,您可以在无需打开/加载应用程序的情况下尝试您的假设,跟踪调用与手头任务无关的应用程序元素。

        【讨论】:

        • 您可以使用 MbUnit 给出执行每个测试所需的确切时间。但是,对于运行时分析,ANTS Performance Profiler 比任何单元测试框架都高效。
        • 是的。我同意你需要两者。一种用于识别热点,另一种用于确保减少计算工作量的更改不会导致错误操作。
        【解决方案5】:

        我使用 Valgrind 及其工具 Callgrind。这是一个很棒的工具。 Valgrind 基本上是一个虚拟机:

        Valgrind 本质上是一个虚拟的 使用即时 (JIT) 的机器 编译技术,包括 动态重新编译。无从 原始程序曾经运行过 直接在主机处理器上。 相反,Valgrind 首先翻译 编程为一个临时的、更简单的形式 称为中间表示 (IR),与处理器无关, 基于 SSA 的表格。转换后, 一个工具(见下文)是免费的 它想要的任何转换 在 IR 上,在 Valgrind 翻译之前 IR 回到机器代码并让 主处理器运行它。虽然 它可以使用动态翻译(即 是,主机和目标处理器是 来自不同的架构),它 没有。 Valgrind 重新编译二进制文件 在主机和目标上运行的代码(或 模拟)相同的CPU 架构。

        Callgrind 是一个基于此的分析器。主要好处是您不必运行应用程序数小时即可获得可靠的结果。几秒钟就足够了,因为 Callgrind 是一个非探测分析器,

        另一个基于 Valgrind 的工具是 Massif。我用它来分析堆内存使用情况。它非常好用,它可以为您提供内存使用情况的快照 - 详细信息 WHAT 拥有多少内存百分比,以及是谁将它放在那里的。

        还有一个 Valgrind 工具——DRD(和 Helgrind)。我使用它们来跟踪我的代码中的死锁和数据竞争,以及线程 API 滥用。

        【讨论】:

          【解决方案6】:

          我确实使用工具来完成这项工作。否则我发现自己很难做到......(这是我的意见,我实际上从未尝试过)

          在 Linux 上,我使用 Valgrind,它提供了一些有用的工具来分析您的代码。至于 Valgrind 主页:

          它在以下平台上运行:X86/Linux、AMD64/Linux、PPC32/Linux、PPC64/Linux。

          .. 它是免费的(就像免费啤酒一样)并且是开源的。

          虽然我没有太多使用它们,但在另一个平台上您可以使用Purify/Quantify(IBM 产品)。它们是商业工具。正如 Will 报告的那样,它们是在 PurifyPlus 包中提供的,Quantify 将帮助您分析您的应用程序。

          这就是我用过的所有东西...... :-)

          【讨论】:

          • 我认为 Purify 与 Quantify 相关联,后者是一个分析器。不过我不确定,但它们都是 PurifyPlus 的一部分。
          • 好点。谢谢威尔!我用你的评论更新了这个问题。
          【解决方案7】:

          这个问题模棱两可。从运行时或内存的角度来看高效? 运行特定代码段的上下文、应用程序的架构以及可能被抛出的数据也是所有因素。

          顺便说一句,有人提到净化;还有它的姊妹产品 Quantify。

          【讨论】:

            【解决方案8】:

            我听说过关于 Yourkit 用于 Java 分析的好消息。对于网站,我尝试了JMeter,它具有用于简单参数化分析的基本功能。

            【讨论】:

              【解决方案9】:

              对于 .NET,我会推荐 NDepend

              【讨论】:

              • 我认为 NDepend 更适合静态代码分析。它现在有运行时性能检测?
              • @MikeJ,你说得对,NDepend 对静态代码进行分析。我不认为这个问题是特定于运行时的。顺便说一句,在我的工作中,我们使用 ANTS Performance Profiler (red-gate.com/products/ants_performance_profiler/index.htm) 进行运行时分析。
              最近更新 更多