在现代c++中使用内联汇编现在有一些好处吗?

Does using inline assembly in modern C++ have some benefits now?

本文关键字:c++ 汇编      更新时间:2023-10-16

我很感兴趣,如果我在现代c++中使用内联汇编和现代编译器,如GCC 4.7和VS12,我会有任何好处吗?例如,矢量化和一些小的优化?或者它只是顽皮和现代优化编译器在任何范围内使用它给我什么?

内联程序集的使用使您的代码体系结构特定。即使是像vectorization这样的东西,你现在也依赖于这样一个事实:执行这些代码的目标需要支持这些特殊/增强的指令集。

让我们假设如果有人试图将你的代码从x86移植到ARM,他们会有一段艰难的时间。

我建议有一种机制,通过这种机制,您可以检测目标是否支持向量指令集,如SSE2或SSE3,并执行您的优化代码,并在不支持增强指令集的目标上运行故障安全代码。

例如,Linux内核将arch目录下的所有特定于体系结构的代码抽象出来,并且对于不同的体系结构有相同功能的实现。如果您知道您的代码可能在多个不同的体系结构上执行,那么您可以通过提出自己的抽象来做类似的事情。

和许多事情一样,这取决于情况。

对于您可能编写的大多数代码,内联汇编不太可能有帮助。现代编译器相当聪明,通常会生成至少与您编写的代码一样好的代码。(而且他们生成它的速度比您编写和调试它的速度快得多。)

正如您所猜到的,例外是在编写某些向量代码时。虽然有些编译器会尝试对某些循环进行矢量化,但它们做得不如熟练的人做得好。在某些应用中(例如,DSP,视频编码等),这可以产生显著的差异。然而,这需要你对所编码的处理器有详细的了解——写得不好的矢量代码通常比编译器给出的标量代码表现得更差!

底线:除非你有很好的理由相信需要编写内联汇编,否则避免使用

一般情况下,你要寻找的是包含特定指令的内在函数。

这允许你访问例如vector指令而不放弃c++的类型安全,同时保留内联汇编提供的类似程度的控制。本质上,使用intrinsic时,您可以选择要使用的指令,但是编译器执行寄存器分配。

这个问题的答案有点像英语表达"一根绳子有多长"——如果不真正展开整根绳子,就不容易回答这个问题。这里也是一样,在不知道代码实际应该做什么的情况下,很难说是否有好处。

我的一般方法是用C或c++写代码,然后看看编译器是怎么做的。如果它足够快(不管它被定义为什么),那么就不需要做任何其他事情。如果速度不够快,那就看看代码,看看是否有任何琐碎的算法优化或简化,分割函数以避免在代码中间出现If语句,等等。分析代码,看看它在哪里花费时间。

在每一步改进之后[测量并跟踪变化,设置等-我通常有一个电子表格,上面的数据是"随着这个变化,它运行速度快了4%","随着这个变化,它运行速度慢了2%"]。

一旦您用尽了所有其他选项,那么您可以开始考虑内联汇编器。有时候,如果你"做正确的事",可能会有很大的改善。在其他时候,根本没有什么可获得的。

我最近写了一些内存基准测试,使用gcc。当我使用volatile(强制内存写入实际发生)时,编译器产生了相当垃圾的代码,所以当我在内联汇编器中编写相应的代码时,它要快3-4倍。然而,一旦我重新排列代码,以便在内存写入本身之后的一个步骤中实际使用写入内存,并删除volatile,编译器就像我的SSE2代码一样好。

对于复杂的SSE代码,我仍然认为编译器在某些情况下缺乏一点,但有时它可以相当聪明-它肯定会优化简单的循环到完整的SSE2代码。

我发现最大的好处是当编译器根本不明白你真正想要发生什么[或者它不能为你想要的操作生成正确的代码]。非时态移动就是一个例子——你不想在缓存中存储一些东西,因为你知道它在缓存中无论如何都不会有用。