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

我记得在阅读某个地方真正优化和加快某些部分的代码,程序员用汇编语言编写该部分。 我的问题是 –

  1. 这个练习还在做吗? 以及如何做到这一点?
  2. 汇编语言不是写得太麻烦了吗?
  3. 当我们编译C代码(有或没有-O3标志)时,编译器会进行一些代码优化并链接所有库并将代码转换为二进制对象文件。 所以当我们运行程序时,它已经是最基本的forms,即二进制。 那么如何诱导“汇编语言”呢?

我想了解这个概念和任何帮助或链接非常感谢。

更新:按照dbemerlin的要求改写第3点 – 因为你可能能够编写比编译器生成的更有效的汇编代码,除非你是汇编专家,否则代码会慢慢运行,因为编译器通常比大多数人能更好地优化代码。

返回汇编语言的唯一时间是何时

  • CPU指令在C ++中没有function等价物(如单指令多数据指令,BCD或十进制算术运算)

    要么

  • 出于某些莫名其妙的原因 – 优化器未能使用最好的CPU指令

…和…

  • 那些CPU指令的使用会给瓶颈代码带来一些重要和有用的性能提升。

简单地使用内联汇编来完成一个可以很容易地用C ++expression的操作 – 比如添加两个值或者在一个string中search – 是非常有效的,因为:

  • 编译器知道如何做到这一点
    • 要validation这一点,看看它的汇编输出(例如gcc -S )或拆卸机器代码
  • 你是在人为地限制它在寄存器分配,CPU指令等方面的select,所以用CPU执行你的硬编码指令所需的时间可能需要更长的时间,
    • 编译器优化器可以在指定不同寄存器的等效性能指令之间进行select以最小化它们之间的复制,并且可以select寄存器,使得单个内核可以在一个周期内处理多个指令,而通过特定寄存器强制所有内容将使其串行化
      • 公平地说,GCC有办法expression对特定types的寄存器的需求,而不会将CPU限制到一个确切的寄存器,仍然允许这样的优化,但是它是我见过的唯一的内联汇编
  • 如果一个新的CPU模型在明年推出,而另一条指令的逻辑运算速度要快1000%,那么编译器厂商更有可能更新他们的编译器来使用这个指令,因此你的程序一旦重新编译就会受益。 (或者是那些维护软件的人)
  • 编译器将为目标体系结构select一个最佳方法:如果您对一个解决scheme进行硬编码,那么它将需要成为您的平台的最低公分母或#ifdef -ed
  • 汇编语言不像C ++那样可移植,不pipe是在CPU之间还是在编译器之间,即使你看起来像是移植一个指令,也可能会犯一个错误。
  • 其他程序员可能不知道或不习惯组装

我认为值得记住的一个观点是,当C被引入时,它必须赢得很多硬编程的汇编语言程序员,这些程序员在生成的机器代码上徘徊不前。 机器拥有较less的CPU能力和内存,那么你可以打赌人们最小的事情。 优化器变得非常复杂并且不断改进,而像x86这样的处理器的汇编语言变得越来越复杂,其执行stream水线,caching以及涉及其性能的其他因素也越来越复杂。 您不能只从循环每个指令表中添加值。 编译器编写者花费时间考虑所有这些微妙的因素(特别是那些为CPU制造商工作的因素,但也增加了其他编译​​器的压力)。 对于程序devise师来说,对于任何非平凡的应用程序来说,平均代码的效率比一个好的优化编译器所产生的代码效率要好得多,而且它们绝大多数情况下会变得更糟,这是不切实际的。 所以,assembly的使用应该限制在真正产生可衡量的和有用的区别的时候,值得的耦合和维护成本。

首先,你需要分析你的程序。 然后在C或C ++代码中优化最常用的path。 除非优点清楚,否则不要在汇编器中重写 。 使用汇编程序会使代码更难以维护,而且更加便于携带 – 除非出现极less数情况,否则不值得。

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

 __asm { mov eax, ebx } 

(2)这是非常主观的

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

现在使用汇编语言的原因很less,甚至像SSE和较老的MMX这样的低级结构在gcc和MSVC中都有内置的内在函数(icc,我打赌但是我从来没有用过)。

老实说,这些日子的优化者非常具有侵略性,以至于大多数人甚至无法将其一半的编写代码的性能与汇编匹配。 您可以更改数据在内存中的sorting方式(针对本地)或通过编译器(通过#pragma )告诉编译器更多的信息,但是实际上是在编写汇编代码……怀疑您会从中得到任何额外的东西。

@VJo,请注意,使用高级C代码中的内在函数可以让您执行相同的优化,而不使用单个汇编指令。

对于它的价值,已经有关于下一个Microsoft C ++编译器的讨论,以及它们将如何从内联汇编中删除。 这就说明了需求。

您应该阅读Zen of Code Optimization的经典着作Zen of Code Optimization以及由Michael Abrash Zen of Graphics Programming

总结在第一本书中,他解释了如何使用汇编编程推到极限。 在接下来的例子中,他解释说程序员应该使用像C这样的更高级的语言,并且如果有必要的话,只用汇编来优化一些非常特殊的地方。

这种思路改变的一个动机是他看到,与从高级语言编译的代码(使用新指令的maube编译器)相比,在一代处理器上高度优化的程序在下一代相同的处理器系列中可能变得(稍微)慢例如)。

另一个原因是编译器相当不错,现在正在积极进行优化,通常有更多的性能来获得把C代码转换成汇编的algorithm。 即使对于GPU(graphics卡处理器)编程,您也可以使用cuda或OpenCL来使用C语言。

还有一些(罕见)的情况,你应该使用汇编,通常是为了得到非常好的硬件控制。 但即使在OS内核代码中,它通常只是非常小的部分,代码也不多。

我不认为你指定的处理器。 不同的答案取决于处理器和环境。 一般的答案是肯定的,肯定是不古老的。 一般的原因是编译器,有时他们在优化方面做得很好,但是对于特定的目标却不是很好。 有些人擅长一个目标,而不擅长其他人。 大多数情况下,这是足够好的,大多数时候你想要便携式C代码而不是非便携式汇编器。 但是你仍然发现C库仍然会手动优化memcpy和其他例程,编译器根本无法知道有一个非常快速的方法来实现它。 部分是因为那个angular色的情况在编译器优化上不值得花时间,只是在汇编器中解决它而构build系统有很多这个目标则使用C如果那个目标使用C如果那个目标使用了asm,目标使用asm。 所以还是会发生的,我认为在一些地区要永远持续下去。

X86是拥有很多历史的自己的野兽,我们正处于一个你实际上无法写出一个始终更快的汇编程序的地步,你可以绝对地优化特定处理器上特定处理器的例程一天,出来执行编译器。 除了一些特定的情况,通常是徒劳的。 教育但总体上不值得的时间。 另外注意处理器不再是瓶颈,所以一个马虎的通用C编译器就足够好了,在其他地方find性能。

其他平台,通常意味着embedded式,arm,MIPE,AVR,MSP430,PIC等您可能或可能不运行的操作系统,您可能或可能不会运行caching或其他类似的东西,您的桌面上。 所以编译器的弱点会显示出来。 还要注意,编程语言不断地从处理器发展而不是朝向它们。 即使C被认为是低级语言,也不符合指令集。 总会有时间您可以生成性能优于编译器的汇编程序段。 不一定是你的瓶颈,但在整个程序中,你可以经常在这里和那里进行改进。 你仍然必须检查这样做的价值。 在embedded式环境中,产品的成功与失败是可以区分的。 如果你的产品每单位投资25美元,需要更多的电力需求,电路板房地产,速度更快的处理器,所以你不必使用汇编程序,但你的竞争对手花费10美元或更less的单位,并愿意与C混合使用较小的内存,使用更less的电力,更便宜的零件等等。只要NRE被恢复,那么与asm解决scheme混合在一起就会长期运行。

真正的embedded式是一个专门的市场与专业工程师 另一个embedded式市场,你的embedded式linux roku,tivo等embedded式电话等都需要有便携式操作系统才能生存,因为你需要第三方开发者。 所以平台必须比embedded式系统更像台式机。 如上所述埋在C库或操作系统中可能会有一些汇编器优化,但是与桌面一样,您想要尝试抛出更多的硬件,因此软件可以是便携式的而不是手动优化的。 如果第三方成功需要汇编程序,那么您的产品线或embedded式操作系统将会失败。

我最担心的是这个知识正在以惊人的速度丧失。 因为没有人检查汇编程序,因为没有人编写汇编程序等等。没有人注意到编译器在生成代码时没有改进。 开发人员经常认为他们不得不购买更多的硬件,而不是意识到通过了解编译器或如何更好地编程,他们可以使用相同的编译器将其性能提高5%到几百%,有时使用相同的源代码。 通常5-10%使用相同的源代码和编译器。 gcc 4并不总是比gcc 3产生更好的代码,我保持这两个,因为有时gcc3会更好。 目标特定的编译器可以(并不总是)围绕gcc运行圆圈,有时使用相同的源代码不同的编译器可以看到几百个百分点的改进。 这些都来自哪里? 仍然费心寻找和/或使用汇编程序的人。 其中一些人在编译器的后端工作。 前端和中间确实很有趣,而且后端是你制作或打破节目质量和performance的地方。 即使你从不编写汇编程序,但只是时不时地看编译器的输出(gcc -O2 -s myprog.c),它会使你成为一个更好的高级程序员,并保留这些知识。 如果没有人愿意认识和编写汇编程序,那么从定义上来说,我们已经放弃了编写高级语言的编译器,一般的软件将不复存在。

通过gcc了解,例如,编译器的输出是传递给汇编器的汇编,汇编器将其转换为目标代码。 C编译器通常不会生成二进制文件。 合并到最终二进制文件中的对象由链接器完成,而另一个程序由编译器调用,而不是编译器的一部分。 编译器将C或C ++或ADA或其他任何东西转换为汇编器,然后汇编器和链接器工具将其作为其余部分。 像tcc这样的dynamic重新编译器必须能够以某种方式生成二进制文件,但是我认为这是一个例外而非规则。 LLVM拥有自己的运行时解决scheme,并且如果将其用作交叉编译器,则可以清楚地显示内部代码的高级别,以将代码定位到二进制path。

所以回到这一点,是的,比你想象的要多。 大多数情况下与语言不直接与指令集进行比较,然后编译器不总是产生足够快的代码。 如果你能像malloc或memcpy那样使用频繁使用的函数,可以获得数十倍的提升。 或者想要在没有硬件支持的情况下在手机上安装高清video播放器,可以平衡汇编程序的优点和缺点。 真正的embedded式市场仍然使用汇编器,有时它是全部C,但有时软件完全用汇编编码。 对于桌面x86,处理器不是瓶颈。 处理器是微码的。 即使你在表面上制作漂亮的汇编程序,它也不会在所有的x86系列处理器上运行得非常快,马虎,足够好的代码更有可能在全线运行。

我强烈build议学习非x86 x86的arm汇编器,如arm,thumb / thumb2,mips,msp430,avr。 具有编译器的目标,特别是具有gcc或llvm编译器支持的目标。 学习汇编程序,学习理解C编译器的输出,并通过实际修改该输出并对其进行testing,certificate您可以做得更好。 这些知识将有助于使您的桌面高级代码在没有汇编器的情况下更好,更快,更可靠。

看看这里 ,这个家伙使用汇编代码提高了6次性能。 所以,答案是:它仍然在做,但编译器做得不错。

这取决于。 在某些情况下(仍然)正在完成,但大部分是不值得的。 现代CPU非常复杂,编写高效的汇编代码同样复杂。 所以大多数情况下,手动编写的程序集最终会比编译器为您生成的程序慢。

假设在过去几年内发布了一个体面的编译器,通常可以调整您的C / C ++代码,以获得与使用程序集相同的性能优势。

在这里的评论和回答中,很多人都在谈论他们在集会中重写某些东西的“N倍加速”,但这本身并不意味着太多。 我通过重写C函数来评估stream体动力学方程的C函数的速度提高了13倍,通过应用许多相同的优化,如果您要通过汇编,通过了解硬件和剖析来编写它们。 最后,它足够接近CPU的理论峰值性能,在assembly中重写它是没有意义的 。 通常,这不是语言的限制因素,而是你写的实际代码。 只要你没有使用编译器遇到的“特殊”指令,就很难击败写得很好的C ++代码。

大会不是神奇的更快。 它只是让编译器退出循环。 这通常是一件坏事,除非你真的知道自己在做什么,因为编译器执行了很多优化,这些优化对于手动来说真的很痛苦。 但是在极less数情况下,编译器不理解你的代码,也不能为它生成有效的汇编, 然后 ,自己编写一些汇编可能是有用的。 除了驱动程序的开发或类似的(你需要直接操纵硬件的地方),我能想到的只有编写程序集的地方可能是值得的,如果你被一个不能生成高效的SSE代码的编译器内部函数(如MSVC)。 即使在那里,我仍然开始使用C ++中的内在函数,并对其进行分析并尽可能地调整它,但是由于编译器并不擅长这一点,因此最终可能需要重写该代码在组装。

在我的工作中,我使用embedded式目标(微控制器)上的程序集进行低级访问。

但对于个人电脑软件,我不认为这是非常有用的。

我有一个我已经完成的程序集优化的例子,但它又是一个embedded式目标。 你也可以看到PC的汇编编程的一些例子,它创build了真正的小而快的程序,但通常是不值得的(查找“汇编Windows”,你可以find一些非常小的漂亮的程序)。

我的例子是当我写一个打印机控制器,有一个本应该被称为每50微秒的function。 它必须或多或less地进行重组。 使用C我已经能够在大约35微秒的时间内完成,而在大约8微秒的时间里,我已经完成了它。 这是一个非常具体的程序,但仍然是一个真实和必要的东西。

在某些embedded式设备(电话和PDA)中,这很有用,因为编译器不是非常成熟,并且可能会产生非常慢甚至不正确的代码。 我个人不得不解决,或者编写汇编代码来修复几个不同编译器在基于ARM的embedded式平台上的错误输出。

  1. 是。 使用内联汇编或链接汇编对象模块。 您应该使用哪种方法取决于您需要编写多less汇编代码。 通常,使用内联汇编可以执行多行代码,并且如果函数不止一个,则可以切换到单独的对象模块。
  2. 当然,但有时候这是必要的。 这里突出的例子就是编写一个操作系统。
  3. 今天的大多数编译器都比任何人都可以编写汇编代码更好地优化用高级语言编写的代码。 人们大多使用它来编写代码,否则就不可能用C这样的高级语言编写代码。如果有人用它来代表其他任何一种方式,那么他在优化方面要比现代编译器好(我怀疑),或者只是简单的愚蠢,例如,他不知道使用哪个编译器标志或函数属性。