在C/C++中使用汇编语言

Sri*_*aju 43 c c++ optimization assembly compiler-optimization

我记得在某处可以真正优化和加速代码的某些部分,程序员用汇编语言编写该部分.我的问题是 -

  1. 这种做法还在做吗?一个人怎么做?
  2. 不是用汇编语言编写有点过于繁琐和陈旧吗?
  3. 当我们编译C代码(带或不带-O3标志)时,编译器会进行一些代码优化并链接所有库并将代码转换为二进制对象文件.因此,当我们运行程序时,它已经处于最基本的形式,即二进制.那么如何诱导'汇编语言'有帮助?

我正在努力理解这个概念,非常感谢任何帮助或链接.

更新:根据dbemerlin的要求改写第3点 - 因为您可能能够编写比编译器生成的更有效的汇编代码,但除非您是汇编专家,否则您的代码运行速度会慢,因为编译器通常会比大多数人更好地优化代码.

Ton*_*roy 28

恢复汇编语言唯一有用的时间是

...和...

  • 使用这些CPU指令可以为瓶颈代码提供一些重要且有用的性能提升.

简单地使用内联汇编来执行可以在C++中轻松表达的操作 - 比如添加两个值或在字符串中搜索 - 会起到反作用,因为:

  • 编译器知道如何做到这一点同样好
    • 要验证这一点,请查看其装配输出(例如gcc -S)或反汇编机器代码
  • 你是人为地限制它在寄存器分配,CPU指令等方面的选择,因此可能需要更长的时间来准备CPU寄存器以及执行硬编码指令所需的值,然后更长时间以回到最佳分配以备将来的指令使用
    • 编译器优化器可以在指定不同寄存器的等效性能指令之间进行选择,以最大限度地减少它们之间的复制,并且可以选择寄存器,使得单个内核可以在一个周期内处理多个指令,而强制所有内容通过特定寄存器将序列化
      • 公平地说,GCC有办法表达对特定类型寄存器的需求,而不会将CPU限制在一个精确的寄存器中,仍然允许这样的优化,但它是我见过的唯一一个解决这个问题的内联汇编.
  • 如果一个新的CPU模型明年出现,另一条指令对同一逻辑操作的速度提高了1000%,那么编译器供应商更有可能更新他们的编译器以使用该指令,因此你的程序一旦重新编译就会受益,而不是你的(或者那些维护软件的人)
  • 编译器将为目标体系结构选择一种最佳方法:如果您对一个解决方案进行硬编码,那么它将需要#ifdef为您的平台的最低公分母或-ed
  • 汇编语言不像C++那样可移植,无论是跨CPU还是跨编译器,即使你似乎移植了一条指令,也可能会犯一个错误的寄存器,这对于clobber,参数传递约定等是安全的.
  • 其他程序员可能不知道或不熟悉组装

我认为值得记住的一个观点是,当C被引入时,它必须赢得许多核心汇编语言程序员,他们对生成的机器代码感到困惑.机器当时拥有较少的CPU功率和RAM,你可以打赌人们对最微小的东西感到困惑.优化器变得非常复杂并且不断改进,而x86等处理器的汇编语言变得越来越复杂,执行管道,缓存和其他因素也越来越复杂.您不能再添加每个指令周期表中的值.编译器编写者花时间考虑所有这些微妙因素(特别是那些为CPU制造商工作的因素,但这也增加了其他编译​​器的压力).它' 现在,对于汇编程序员来说,平均而言 - 对于任何非平凡的应用程序而言 - 显着提高代码效率比使用优秀的优化编译器所产生的效率要高得多,并且它们极可能会更糟糕.因此,组装的使用应限制在实际产生可测量和有用的差异的时间,值得耦合和维护成本.


sha*_*oth 14

首先,您需要描述您的程序.然后,您可以在C或C++代码中优化最常用的路径.除非优点明确,否则不要在汇编程序中重写.使用汇编程序会使代码更难维护而且便携性更低 - 除非在极少数情况下,否则它是不值得的.

  • @MovieYoda:是的,你可能会发现这些愚蠢的代码只是重写它们(仍然使用C或C++)会给你带来巨大的推动力.例如,如果你在一个循环中调用`strlen()`而字符串长度没有改变,那么在汇编程序中浪费时间会重写 - 你只需使用一个临时变量来存储长度和(魔术!)你的程序可能会显着运行快点. (4认同)
  • @MovieYoda:这是我做过的一篇文章(http://stackoverflow.com/questions/926266/performance-optimization-strategies-of-last-resort/927773#927773),展示了如何找到实际值得优化的代码,并循环挤压(就像写作asm)几乎不需要什么. (3认同)
  • @MovieYoda:不,它可以帮助你弄清楚瓶颈在哪里.这样,您就不会浪费时间来优化一段甚至不是性能主要因素的代码.通常,使用C或C++代码编写程序集只能作为最后的手段.通常,只使用不同的算法或数据结构将加速代码. (2认同)

And*_*nck 10

(1)是的,最简单的尝试方法是使用内联汇编,这是依赖于编译器的,但通常看起来像这样:

__asm
{
    mov eax, ebx
}
Run Code Online (Sandbox Code Playgroud)

(2)这是非常主观的

(3)因为您可能能够编写比编译器生成的更有效的汇编代码.

  • 您可能希望将(3)更改为"因为您可能能够编写比编译器生成的更有效的汇编代码",但除非您是汇编专家,否则您的代码运行速度会慢,因为编译器通常会比大多数人更好地优化代码能够. (6认同)
  • @dbemerlin你不需要成为优化编译器生成代码的专家.您只需找到合适的位置,并了解编译器不考虑的内容.查看生成的代码是最好的.通常,您会发现编译器可以保护不需要这种安全保护的地方.跳过循环核心中的一个负载,可能会在代码中的正确位置上进行奇迹处理. (3认同)
  • 我认为"可能"涵盖它,我认为你不能比这更具量. (2认同)
  • 我不同意(1).最简单的方法通常是使用"out of line"汇编源文件.通过这种方式,您可以获得正确的语法突出显示,并且可以使用为人类设计的汇编程序,以及更强大的宏等有用功能.我通常推荐yasm. (2认同)

kri*_*iss 5

你应该阅读经典书籍Zen of Code Optimization和后续Zen of Graphics Programming迈克尔·亚伯拉什

在第一本书中,他总结了如何使用极限编程的汇编程序。在后续文章中,他解释说程序员应该宁愿使用某种高级语言(如C),并且仅在必要时才尝试使用汇编来优化非常特定的位置。

这种改变主意的动机是,他发现,与从高级语言编译的代码(也许是使用新指令的编译器)相比,针对一代处理器的高度优化的程序可能在同一个处理器的下一代系统中变慢(某种程度上)。例如,或者现有处理器的性能和行为从处理器一代转变为另一处理器)。

另一个原因是,如今的编译器非常好,并且可以进行积极的优化,通常可以得到更高的性能,从而可以处理将C代码转换为汇编语言的算法。即使对于GPU(图形卡处理器)编程,也可以使用cuda或OpenCL使用C进行编程。

在某些情况下,您应该(必须)使用汇编程序,通常是为了对硬件进行很好的控制。但是即使在OS内核代码中,它通常也只是很小的一部分,而不是那么多的代码。