假设我在汇编中有两个相同算法的实现。我想通过检查两个片段代码来知道哪个更快。
我认为可能会考虑的参数是:操作码数、分支数、功能帧数。
我的问题是:
- 我可以假设每个操作码执行都是一个周期吗?
- 破坏管道的分支的开销是多少?
- 调用函数的效果和开销是什么?
- ARM 和 x86 的分析有区别吗?
这个问题是理论上的,因为我有两个实现;一条长 130 条指令,一条长 184 条指令。
而且我想知道说 130 条指令长片段比 184 条指令长的实现更快是否绝对正确?
假设我在汇编中有两个相同算法的实现。我想通过检查两个片段代码来知道哪个更快。
我认为可能会考虑的参数是:操作码数、分支数、功能帧数。
我的问题是:
- 我可以假设每个操作码执行都是一个周期吗?
- 破坏管道的分支的开销是多少?
- 调用函数的效果和开销是什么?
- ARM 和 x86 的分析有区别吗?
这个问题是理论上的,因为我有两个实现;一条长 130 条指令,一条长 184 条指令。
而且我想知道说 130 条指令长片段比 184 条指令长的实现更快是否绝对正确?
不想轻率,答案是
- 不
- 这取决于您的硬件
- 这取决于您的硬件
- 是的
你真的需要在你的目标硬件上测试东西,或者有一个完全理解你的硬件的模拟器,以便以你想要的方式回答你的问题......
对于问题的最后一部分,您需要定义“更好”……更好。
由于您询问了Cortex A9,因此数据表在附录 B中有指令周期计数。这些计数通常假设内存总线足够快以保持 CPU 忙碌。实际上,这种情况很少见。许多视频/音频算法将在访问内存的方式上大获全胜。
当然,如果您想要精确计数,则不能假设这一点。但是,如果您正在决定选择哪种算法,您可以通过查看内部循环中的指令来了解最佳算法。在这里,您cache
应该允许代码按照数据表中的指令计数执行。如果计数接近,那么您可能需要查看每条指令。加载/存储更昂贵,通常是倍数等。一些算法,尤其是加密算法,通过使用不能很好地映射到C
. 例如,clz
, ror
,使用进位进行多字算术等。
查看附录 B或任何包含处理器周期计数的数据表。对于 ARM926,大约需要 3 个周期。编译器只连续生成两个条件操作码以避免分支,否则,它会分支。如果算法很大,分支可能会破坏缓存。一个硬性的答案取决于您的 CPU、缓存和内存。根据 Cortex A9 数据表 (B.5),固定分支只有一个周期开销。
这与分支开销非常相似。但是,编译器也会产生影响。Jim 指出 它是否缓存对齐功能。编译器是否执行叶函数优化等。在现代gcc
版本中,如果所有函数都是静态的,编译器通常会在有利的时候内联。如果算法特别大,则寄存器溢出可能是有利的。但是,对于您的 130/184 指令示例,这似乎不太可能。编译器选项显然会影响开销。您可以使用objdump -S
检查序言/尾声,然后确定硬件的周期数。
当然,周期计数存在技术差异。CISC
x86 还具有可变指令大小。这使分析变得复杂。在 ARM 上稍微容易一些。
通常情况下,您希望将事物归位,然后使用分析器实际运行它们。这些估计可以帮助指导算法的开发。硬件的循环/内存调整等。像instruction emulation
, page
or alignment faults
, etc 这样的东西可能占主导地位,使所有的周期计数分析变得毫无意义。如果算法在 中user space
,per-emption,可能会从运行到运行否定缓存获胜。有可能一种算法在负载较小的系统中工作得更好,而另一种算法在负载较高的情况下工作得更好。
有关获取周期计数的一些复杂情况,请参阅后处理 objdump。基本上,典型的 CPU 是几个阶段(一条管道),不同的条件会导致停顿。随着 CPU 变得更加复杂,管道通常会变得更长,这意味着有更多的条件或阶段可能会停止。但是,周期计数估计有助于指导算法的开发和评估它们。内存时序或分支预测等事情可能同样重要,具体取决于算法。即,周期盘点并非完全无用,但它们也不完整。剖析应确认实际算法时间。如果它们发生分歧,指令重新排序、预取和其他技术可能会使它们更接近。周期计数和主动分析不同的事实本身就很有帮助。
说 130 指令代码比 184 指令代码快,这绝对不是真的。在这两个平台上,很容易让 1000 条指令运行得比 100 条快,反之亦然。
1 我可以假设每个操作码执行都是一个周期吗?
首先查看广告的 mips/mhz,尽管它是一个营销数字,它可以大致了解可能的情况。如果数量大于一,则每个时钟可能有多条指令。
2 破坏管道的分支的开销是多少?
在任何一个系统上,从绝对没有影响到非常戏剧性的影响。一个时钟到数百个是潜在的惩罚。
3 调用函数的效果和开销是什么?
很大程度上取决于函数,以及调用函数的函数。根据调用约定,您可能必须将寄存器保存到堆栈中,或者重新排列寄存器的内容以准备要调用的函数的参数。如果按值传递结构,则可能需要在堆栈上制作结构的副本,传递的结构越大,副本就越大。一旦在函数中,可能需要准备堆栈帧等。涉及的因素很多。这个问答也是独立于平台的。
4 ARM和x86的分析有区别吗?
是的,不是的,两个系统都使用了流水线、分支预测等所有现代技巧来保持 mips/mhz。ARM 将提供比 x86 更好的每 mhz mips,x86 是可变指令长度可能会为每个单元缓存提供更多指令。你怎么分析缓存,和内存和外围系统在系统端的分析大体相同。根据您分析的方面,指令和核心的比较相似和不同。手臂不是微编码的,x86 可能是这样的,所以你真的看不到真正有多少寄存器,诸如此类。同时 x86 可以更好地了解带有 arm 的内存系统,因为它们通常不是片上系统。根据您购买的 ARM 芯片,您可能会失去很多芯片边界的可见性,例如,可能看不到所有内存和外围总线。(例如,x86 正在通过将 pcie 现在放在芯片上来改变这一点)对于皮质中的某些东西 - 你提到的一类你将具有类似的芯片可见性边缘,因为那些将使用更大/更便宜的基于 DRAM 的片外内存而不是微控制器比如芯片资源。
底线你的最后一个问题:
“而且我想知道,说 130 条指令长片段比 184 条指令长的实现快,这是否绝对正确?”
说 130 指令片段比 184 指令片段快是绝对不正确的。它可能更快,也可能更慢,并且可能大致相同。有了更多的信息,我们可能会做出一个很好的陈述,或者它可能仍然是不确定的。很容易选择 100 条执行速度快于 1000 条指令的指令,同样容易选择 1000 条执行速度快于 100 条指令的指令(即使我不添加分支和循环,只是线性执行)
您的问题几乎完全没有意义:这可能取决于您的输入。
大多数 CPU 都有类似于分支预测错误的惩罚(例如,传统的 ARM 在任何采用的分支上丢弃指令获取/解码,IIRC)。ARM 和 x86 还允许条件执行,这比分支更快。如果其中任何一个依赖于输入数据,那么不同的输入将遵循不同的代码路径。
也许一个版本大量使用条件执行,当条件为假时这是一种浪费。也许另一个是使用一些分析信息编译的,这些信息对特定情况不执行任何分支(除了最后的返回)。编译器可以采用相同的源并产生“优化”输出的原因有很多,这对于一个输入来说更快,而对于另一个输入来说更慢。
许多优化都有这个特性——例如,将循环的开始对齐到 16 个字节对某些处理器有帮助,但当循环只执行一次时则不然。
Cortex™ -A 系列程序员指南中的一些教科书回答了这个问题,chapter 17
.
尽管可以在您正在使用的处理器的技术参考手册 (TRM) 中找到周期时序信息,但很难计算出即使是一段微不足道的代码也需要多少个周期才能执行。指令在流水线中的移动取决于周围指令的进程,并且会受到内存系统活动的显着影响。缓存中未命中的未决加载或指令提取会使代码停顿数十个周期。标准数据处理指令(逻辑和算术)只需要一到两个周期来执行,但这并不能给出完整的画面。相反,我们必须使用分析工具或处理器内置的系统性能监视器来提取有关性能的有用信息。
另请阅读以下17.4 Cortex-A9 micro-architecture optimizations
内容非常非常回答您的问题。