将汇编代码与c/c++混合使用

6

为什么C/C++经常需要与汇编语言代码一起使用?

在C/C++中无法完成哪些在混合汇编语言代码时可以实现的操作?

我有一些3D电脑游戏的源代码。其中有很多汇编代码在使用。


3
"often"这个词取决于应用领域。至少需要一些汇编语言才能编写操作系统内核,这几乎是不可能的。在嵌入式系统中,你几乎总能找到一些汇编代码片段,即使只是为了进行系统启动。机顶盒游戏机就是一种嵌入式系统... - RBerteig
3
相反,如果在文字处理器、数据库引擎、编译器或其他“普通”的应用程序中真正需要汇编语言代码,则会令人惊讶。 - RBerteig
8个回答

14

以下是我想到的,没有特定顺序:

  • 特殊指令。在嵌入式应用程序中,当DMA传输填充内存缓冲区后,我需要使高速缓存无效。在SH-4 CPU上,唯一的方法是执行特殊指令,因此内联汇编(或独立汇编函数)是唯一的选择。

  • 优化。曾经,编译器并不知道 每个 可能的技巧。在某些情况下,用手工制作的版本替换内部循环是值得的。在小型嵌入式系统(如8051、PIC等所使用的CPU)中,将内部循环转换成汇编代码可能会很有价值。我要强调的是,在现代具有流水线、多发射执行、广泛缓存等功能的处理器上,手写代码往往极难接近优化器的能力。

  • 中断处理。在嵌入式应用程序中,通常需要捕获系统事件,例如中断和异常。通常情况下,中断执行的前几条指令具有特殊职责,确保正确的事情发生的唯一方法是使用汇编语言编写处理程序的外层。例如,在ColdFire(或任何68000的后继者)上,只有第一条指令是保证执行的。为了防止嵌套中断,该指令必须修改中断优先级级别,以屏蔽当前中断的优先级。

  • 操作系统内核的某些部分。例如,任务切换要求保存当前任务的执行状态(至少大多数寄存器,包括PC和堆栈指针),并加载新任务的状态。操纵CPU的执行状态远远超出了语言的功能集,但可以用少量汇编代码来包装,以便可以在C或C++中编写其余的内核代码。

编辑:我修改了关于优化的措辞。让我强调一下,在具有大量用户群体和良好支持的编译器与合理优化功能的目标平台上,汇编编码师无法击败优化器的性能表现。

在尝试之前,先进行仔细的分析以确定瓶颈真正存在的地方。有了这些信息后,仔细检查假设和算法,因为最好的优化通常是找到更好的处理大图景的方法。然后,如果一切都失败了,在测试用例中隔离瓶颈,仔细进行基准测试,并开始汇编调整。


第二点很危险。现代优化编译器将知道并实现平台的所有适当优化。对于新平台,可能还没有这些优化,但是在用户请求下,它很快就会赶上并取代任何人工优化。因此,如果您这样做(使用#if和#error),请将汇编限制为特定版本的编译器,以便在使用新编译器重新编译时,强制重新评估您的汇编是否比当前版本的编译器更好。 - Martin York
@Martin,我个人亲身经历的大多数情况是在几年前,早在“现代优化器”出现之前,或者针对没有GCC针对桌面的动机的目标平台。必须记住,在嵌入式系统世界中,每千个桌面目标用户几乎没有任何交叉编译器的全球市场。话虽如此,你提出了一个有效的观点,我会编辑建议先进行分析,然后再进行基准测试。 - RBerteig
即使是现代编译器,它们所允许的优化范围也极其有限。这是因为它们必须始终生成可工作的代码,因此在优化代码方面非常保守。此外,代码分析仍然是一个计算上几乎不可能的问题,这也严重限制了编译器的优化能力,与程序员的汇编代码相比。由于这些事实,大多数性能关键的代码仍然严重依赖于汇编代码。 - Mavrik
@Mavrik:不,不是这样的。你需要获取一本更新的“事实”书。 - Sebastian Mach
有任何支持吗?有没有一种编译器可以很好地执行矢量化指令,而不需要特殊标志来优化浮点数学运算? - Mavrik

3
过去,编译器在优化特定架构方面表现不佳,而架构则比较简单。现在情况正好相反。对于深度流水线、分支预测处理器,人类很难编写比优化编译器更好的汇编代码。因此你几乎看不到这种情况发生。如果有的话,也会非常简短且高度针对性。
总之,你可能不需要这样做。如果你认为需要,请对代码进行剖析以确保你已经确定了一个热点——如果你只在那里花费了0.1%的执行时间,不要仅仅因为它运行缓慢就进行优化。看看是否可以改进你的设计或算法。如果你在那里找不到任何改进,或者你需要高级语言没有提供的功能,请尝试手动编写汇编代码。

3

有些事情只能用汇编语言来完成,而不能用C/C++语言实现。

这些包括:

  1. 生成软件中断(SWI或INT指令)
  2. 使用诸如SWP之类的指令来创建互斥锁
  3. 专业协处理器指令(例如用于编程MMU和管理RAM高速缓存的指令)
  4. 访问进位和溢出标志。

在汇编语言中,您还可以比C/C++更好地优化代码(例如,在Android上,memcpy是用汇编语言编写的)。


3

为什么在C/C++中经常需要使用汇编语言代码?

竞争优势。比如,如果你正在为即将成为世界第一的游戏公司编写软件。

在C/C++中无法完成什么任务,但混合使用汇编语言代码可以实现?

除非需要达到绝对的性能水平,例如每秒X帧或每秒Y亿个多边形,否则没有任何事情是不可能在C/C++中完成的。

编辑:根据其他回复,似乎嵌入式系统(iPhone、Android等)具有硬件加速器,确实需要使用汇编语言。

我有一些3D电脑游戏的源代码。其中使用了很多汇编代码。

它们要么是在80年代-90年代编写的,要么只在游戏引擎中少量使用(可能占总源代码的1%至5%)。

编辑:到目前为止,编译器自动向量化的质量仍然很差。因此,您可能会看到包含向量化内部函数的程序,由于这与实际编写汇编语言并没有太大区别(大多数内部函数都有一一映射到汇编指令),因此有些人可能会决定使用汇编语言编写。

更新:

根据传闻,RollerCoaster Tycoon 的编写使用了99%的汇编语言。
http://www.chrissawyergames.com/faq3.htm


2

你的编译器可能还不能生成新的指令,或者编译器效果不佳,或者你需要直接控制CPU。


当使用汇编代码时,我不会说你在“直接控制”CPU。 - zneak
像修改x86 CPU上的EFLAGS寄存器这样的操作怎么样? - James

1
为什么汇编语言代码经常需要与C/C++一起使用?
其实不需要。
混合使用汇编语言代码可以做哪些C/C++无法完成的事情?
访问CPU上的系统寄存器或IO端口。 访问BIOS函数。 使用特殊指令,这些指令不能直接映射到编程语言中,例如SIMD指令。 提供优化后的代码,比编译器生成的代码更好。
通常情况下,前两点只有在编写操作系统或在没有操作系统的情况下运行时才需要。
现代CPU非常复杂,你很难找到真正能够编写比编译器生成的汇编代码更好的人。许多编译器都带有库,可以让你访问更高级的功能,例如SIMD指令,因此现在通常不需要回退到汇编语言了。

1

值得一提的是:

  • C&C ++没有提供任何方便的方法来设置堆栈帧,当需要实现与脚本语言的二进制级别互操作或实现某种闭包支持时。

-1

在某些情况下,汇编语言可以比任何编译器生成的代码更加优化。


2
尽管编译器通常比你聪明。 - zneak
1
即使是这样,你可能还不够聪明。 - Joe
或者编译器有限制。大多数适用于Microchip PIC的编译器效果非常糟糕,直到你停下来意识到这种架构对编译器是多么不友好。除非内部循环是瓶颈,否则你只能忍受它,此时一个熟练的汇编程序员通常比编译器做得更好。 - RBerteig
认为编译器比你聪明,这是个坏的假设(假设你不是白痴:P)。在旧版MSVC编译器中,有些完全不需要64位数学函数的情况下也会强行插入。一个例子就是64位乘法和加法,应该简化为MUL+ ADC(在x86上),但VC6会插入_aulmul,因此除非使用PP5进行__emulu,否则您将被困在“迟钝”的数学中。这在当今大型生产编译器中不太适用(虽然MSVC 08不会生成memset内置函数,这意味着需要黑魔法C或汇编语言)。 - Necrolis
在过去的十年中,我见过数百个人中只有一个人能够编写与gcc 4.0相媲美的汇编代码,而他正在与另一位仅使用编译器和C++的性能优化大师进行激烈的竞争。因此,根据我的经验,在现实世界中几乎没有人比编译器更聪明。 - Sebastian Mach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接