C语言 如何确定内联汇编在哪里/为什么可以提供更高的执行速度



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

我想知道怎样才能很好地识别:

1),其中大部分时间都浪费在执行算法

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

提前谢谢你。

1),其中大部分时间都浪费在执行算法

时间不是"失去",也许只是浪费了。任何算法的效率都取决于许多因素,例如:

  • 为手头的问题选择最合适的算法,
  • 编码的好坏,
  • 用什么语言编码,
  • 编译器代码生成和优化的效率,
  • 选择合适的编译器选项。
也就是说你的问题无法回答。它通常是通过对相关代码的分析逐案确定的,但是在跳转到汇编代码之前还有很多事情可以做。一个选择或实现得不好的算法可能在汇编代码中运行得更快,但它仍然是一个糟糕的选择和/或实现,你可能通过正确的选择和/或实现得到更好的结果。

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

首先要考虑的是你在编写汇编代码方面有多擅长,以及你对特定目标的指令集有多熟悉?或者你需要聘请的专家有多贵才能获得任何真正的好处。

你准备花多少时间手工制作汇编代码,然后发现你不能获得任何显著的有用的好处,或者这样做所花费的时间在任何情况下都导致了你的项目失败。

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

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

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

实时系统通常与执行速度无关,而更多地与确定性行为和满足截止日期有关。通常复杂的处理可以被推迟,因此通常可以通过精心设计而不是通过微优化来达到最后期限。通常可以利用硬件特性,如中断处理、DMA和定时器捕获来实现性能提升。

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

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

没有必要优化程序中不占执行时间很大一部分的部分。

汇编本质上是不可移植的。这是一门黑色的艺术,很难掌握和维护。实际上,随着处理器体系结构的发展,维护是必要的。编译器编写者花费了大量的精力试图利用这些改进。需要非常具体的情况才能保证装配级优化的成本。某些操作系统任务可能需要访问特定的汇编级指令,但生产代码很少证明这种方法是正确的。

在C或c++程序中,即使vector指令也不应该作为内联汇编来操作,处理器供应商提供了宏来封装它们。

如果分析显示了代码中可识别的瓶颈,您应该首先尝试用C语言优化C代码,同时考虑可能更好的算法。

如果在最后,因为你有必要的技能可用,你决定使用内联汇编,清楚地识别使用这种不可移植实现的函数,并保留一个纯C的替代参考实现,以便比较和可移植性到其他体系结构。当然,对结果代码进行基准测试,只有在增益显著的情况下才使用它。

简而言之:

1),其中大部分时间都浪费在执行算法

使用分析工具

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

可能,但是非常困难并且很可能不值得付出努力。

1),其中大部分时间都浪费在执行算法

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

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

查看编译器的asm输出,看看它是否做了一些愚蠢的事情,你可以做得更好。这需要了解您所瞄准的微体系结构,这样您就知道什么是慢的,什么是快的。如果您的目标是x86,请参阅x86标签wiki以获取性能指南(例如Agner Fog的优化汇编指南,微架构指南和指令表,以及英特尔的优化手册)

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


你的第一个尝试应该是调整C语言,引导编译器以更智能的方式实现相同的逻辑,就像我在这个答案中所做的那样。

如果问题是可向量化的,但编译器没有自动向量化它,你的第一个行动应该是用内在函数手动向量化它,而不是用内联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,它总是为false:(

)

最后,如果你认为可以击败编译器,确保你在完成后运行一个基准测试,以对抗你能想到的最佳C实现

最新更新