如何识别内联汇编可以提供更高执行速度的位置?

时间:2016-10-30 14:43:19

标签: c algorithm performance assembly embedded

我知道在编写一些(例如)实时应用程序时,执行速度非常重要。有时可以通过编写内联汇编来获得更高速度的执行。

我想知道什么是识别的好方法:

1)执行算法的大部分时间都丢失了

2)编写内联汇编是否真的会提高执行速度

提前谢谢。

3 个答案:

答案 0 :(得分:4)

  

1)执行算法的大部分时间都丢失了

时间不是“丢失”,只是浪费。任何算法的效率将取决于许多因素,例如:

  • 为手头的问题选择最合适的算法,
  • 编码的好坏,
  • 用什么语言编码,
  • 编译器代码生成和优化的效率,
  • 选择适当的编译器选项。

也就是说你的问题是无法回答的。它通常根据具体情况使用有问题的代码进行分析确定,但在跳转到汇编代码之前可以做很多事情。选择不当或实现的算法可能在汇编代码中运行得更快,但它仍然是一个糟糕的选择和/或实现,只需通过正确的操作就可以获得更好的结果。

  

2)编写内联汇编是否真的会提高执行速度

首先要考虑的是编写汇编代码有多好,以及您对特定目标的指令集有多熟悉?或者,为了获得任何实际利益,您需要花费多少代价。

在您发现无法获得任何显着有用的好处之前,您准备花多少时间来手工制作汇编代码,或者这样做所花费的时间导致您的项目无论如何都会失败。

还要考虑编译器优化器在其生成代码的目标的体系结构和指令集中体现了大量专业知识,并且需要大量时间和专业知识才能以任何重要方式击败它。

要考虑的另一件事是汇编代码缺乏可移植性。如果您的开发转移到不同的体系结构,那么所有昂贵生成的汇编代码代码都可能过时并且必须手动重新开发或移植(要求不良维护者实际理解代码的作用)。

我一直在编写硬实时和DSP系统,并且出于性能原因从未使用过汇编程序。我只是用它来实现无法用高级语言(如C语言)完成的事情,例如操作核心寄存器,例如程序计数器和堆栈指针(例如实时调度程序)。在一个案例中,我研究过在200MHz DSP上运行的应用程序,其中大量代码用汇编程序编写,并移植到完全用C ++编写的72MHz MCU中。这是通过结合更好的设计和使用DMA来捕获和处理样本块中的信号而不是逐个样本,从而显着降低中断率和软件开销。我经历的另一个例子是完全用PIC汇编语言编写的电子换向电机应用程序,它在C中重新加工,但通过更合适地使用可用的PWM和定时器/计数器硬件,C实现更精确,更高效,更小代码大小是100%汇编程序的实现。

实时系统较少涉及执行速度,而且更频繁地涉及确定性行为和满足最后期限。通常可以推迟复杂的处理,因此通常可以通过精心设计而不是通过微优化来实现满足期限。通常可以利用中断处理,DMA和定时器捕获等硬件功能来实现性能提升。

通常,通过首先选择速度更快的处理器来获得所需的性能提升,成本更低,更简单。我建议使用汇编程序获得必要的性能提升是绝望的最后手段,往往表明软件设计和/或实现不当或处理器选择不当。

答案 1 :(得分:2)

使用分析器确定某些相关基准的时间花在哪里。

不需要优化程序中不占执行时间的重要部分。

装配本质上是不便携的。这是一门黑色艺术,非常难以掌握和维护。实际上,随着处理器架构的发展,需要进行维护。编译器作者花了很大力气试图利用这些改进。需要非常具体的情况才能保证装配级优化的成本。某些操作系统任务可能需要访问特定的程序集级别指令,但生产代码很少证明这种方法的合理性。

在C或C ++程序中,不应将向量指令作为内联汇编进行操作,处理器供应商提供宏来封装这些指令。

如果分析显示代码中存在可识别的瓶颈,则应首先尝试在C中优化C代码,同时考虑可能更好的算法。

如果最后,由于您具备必要的技能,您决定使用内联汇编,清楚地识别使用此类非可移植实现的函数,并在纯C中保留备用参考实现,以便与其他体系结构进行比较和移植。当然,对结果代码进行基准测试,只有在增益很大时才使用它。

简而言之:

  

1)执行算法的大部分时间都丢失了

使用分析工具

  

2)编写内联汇编是否真的会提高执行速度

可能,但非常困难,而且很可能不值得努力。

答案 2 :(得分:2)

  

1)执行算法的大部分时间都丢失了

使用分析器查找热点。对于不属于重要循环的代码,甚至不值得查看编译器的asm输出。

  

2)编写内联汇编是否真的会提高执行速度

查看编译器的asm输出,看看它是否做了一些愚蠢的事情,并且你可以做得更好。这需要了解您所针对的微体系结构,因此您知道什么是慢速和快速。如果您的目标是x86,请参阅标记wiki以获取性能指南(例如Agner Fog's optimizing assembly guide, microarchitecture guide, and instruction tables以及英特尔优化手册)

正如@chqrlie指出的那样,任何手写的asm也将针对某些特定的微体系结构进行调整,并且在未来的CPU上可能不是最佳的。乱序执行通常会隐藏指令排序问题,但并非所有ARM CPU都是乱序的,因此调度非常重要。

您的第一次尝试应该是调整C以指导编译器实现相同逻辑的更智能方式,就像我in this answer一样。

如果问题是可矢量化的,但编译器没有对其进行自动矢量化,那么您的第一个操作应该是使用内在函数手动矢量化,而不是使用inline-asm。编译器可以很好地优化使用内在函数的代码。

编写内联asm(或从C调用的asm中的整个函数)应该是最后的选择。除了可移植性和可维护性问题之外,内联asm会破坏编译器优化,如常量传播。请参阅https://gcc.gnu.org/wiki/DontUseInlineAsm

如果函数的一个输入是编译时常量(在内联和链接时优化之后),C实现(带内在函数)将简化为常量输入的特殊情况。

但内联asm版本根本不会简化。编译器只将MOV常量值放入寄存器并按写入方式运行asm。在GNU C中,有时可以通过询问编译器输入是否为编译时常量来检测并避免这种情况。例如if(__builtin_constant_p(some_var)) { C implementation } else { asm(...); }。不幸的是,clang不会通过函数内联传播编译时常量,所以函数args总是错误的:(

最后,如果您,您可以击败编译器,确保在完成后运行基准测试实际上取得了成功,针对您可以提出的最佳C实现