我使用过 ProDelphi,主要用于确定哪些例程消耗的时间最多。它是一个 Instrumenting Profiler,这意味着它会在每个例程的开头和结尾添加一些代码。您可以通过 cmets 中的指令控制它配置的例程。您还可以分析例程的各个部分。但是这些部分必须在同一块级别开始和停止,没有进入或退出该部分。优化必须在 ProDelphi 插入它的代码的地方(你放置指令的地方)关闭,但你可以在其他任何地方打开它。
界面有点笨拙,但一旦掌握了它就会非常快。您可以使用免费版本(限于 10 个例程或部分)进行有用的工作。 ProDelphi 可以快速告诉您应该检查哪些例程。但不是为什么,或者是哪几行。
最近,我开始使用英特尔的 VTune 性能分析器。 “哇”并没有开始总结它。我印象深刻。我根本不知道所有这些都内置在现代英特尔处理器中。您是否知道它可以准确地告诉您在从更高的缓存重新加载一个字之前,一条指令需要多长时间等待 L1 数据缓存侧向查看另一个内核?如果我继续写下去,我听起来就像是一个令人窒息的产品广告。
访问英特尔并下载完整的定时演示。在网上挖掘并找到一些关于如何开始的视频。 (否则,您将面临被所有选项所阻碍的风险。)它适用于任何编译器。只需将其指向 .exe。如果您的 .exe 包含调试信息并且您将其指向源代码,它将显示源代码行。
我一直在尝试优化调用我编写的函数的内部循环。除了长度(str)之外,没有外部调用。这个内部循环每次运行数十亿次,消耗了大约一半的 CPU 时间——这是优化的完美候选者。我尝试了各种标准优化,几乎没有效果。 VTune 显示热点。我只是深入研究,直到它向我展示了我的代码生成的 ASM,以及每条指令花费了多少时间。
这是 VTune 告诉我的:
- line nnnn [delphi 代码行] ...
- addr hhhh cmp byte ptr [edx+ecx],0x14h - - - - - - - - 3 个周期
- addr hhhh ja label_x - - - - - - - - - - - - - - - - -10302 个周期
绝对值没有任何意义。 (我想我是在测量每条退役指令的周期。)相对值很清楚所有时间都去了哪里。最棒的是建议窗口。它告诉我代码停止等待数据加载到 L1 数据缓存中,实际上给了我关于如何避免停止的很好的建议。
我的错误在于认为 Core2 Quad 只是一个非常快的 8086 CPU。没有^3。代码花费了 99% 的时间来等待从内存中加载数据,因为我跳得太快了。我的算法假设内存是 RAM(随机存取)。这不是现代 CPU 的工作方式。 L1 缓存中的数据可能会在 1 或 2 个周期内被访问,但访问 L2 或 L3 缓存需要数十到数百个周期,而进入 RAM 则需要数千个周期。 但是,当您按顺序访问数据时,所有延迟都可以避免 - 因为处理器会在您请求的第一个字节之后使用数据预加载缓存。
最终结果是我重写了算法以更顺序地访问数据,并获得了 10 倍的加速,这已经足够了。当我有时间时,我确信我可以再获得 10 倍的收益。但这只是我心中的极客。足够好就足够了。
我已经知道,通过优化算法而不是代码,您可以获得最大的收益。我以为我只需要探查器告诉我需要优化什么。但我也需要它来找出瓶颈的原因,这样我就可以设计一个更快的算法。
新算法与旧算法没有根本不同。它只是存储数据,以便可以按顺序访问。例如,在一个地方,我将一个字段从记录数组移动到它自己的整数数组中——因为内部循环不需要每条记录中的其余数据。我还有一个矩形矩阵存储为动态数组的动态数组。代码使用它来随机访问兆字节的数据(而糟糕的 L1 数据缓存只有 64Kb)。我想出了如何将它作为矩阵的对角线存储在线性数组中,这是我使用数据的顺序。 (好吧,也许那部分是激进的。)
无论如何,我在 VTune 上被卖掉了。