我对那里的所有核心低级黑客都有疑问.我在博客中遇到了这句话.我并不认为这些来源很重要(如果你真的在意,那就是Haack),因为它似乎是一个常见的陈述.
例如,许多现代3D游戏都有他们用C++和Assembly编写的高性能核心引擎.
就程序集而言 - 是用汇编编写的代码,因为你不希望编译器发出额外的指令或使用过多的字节,或者你使用的是更好的算法,你无法在C中表达(或者没有表达编译器把它们搞砸了)?
我完全明白了解低级别的东西很重要.我只想在你理解它之后理解为什么程序在汇编.
我想你误读了这句话:
例如,许多现代3D游戏都有他们用C++和Assembly编写的高性能核心引擎.
游戏(以及现在的大多数程序)都不像"用C++编写"那样"以汇编语言"编写.该博客并不是说游戏的很大一部分是在汇编中设计的,或者是程序员团队围坐并以汇编作为主要语言进行开发.
这真正意味着开发人员首先编写游戏并使其在C++中运行.然后他们对它进行分析,弄清楚瓶颈是什么,以及是否值得他们在装配中优化它们.或者,如果他们已经有经验,他们知道哪些部分会成为瓶颈,并且他们已经从他们构建的其他游戏中获得了优化的部分.
该点在汇编语言编程是一样的,因为它一直是:速度.在汇编程序中编写大量代码是荒谬的,但编译器并不知道有一些优化,而对于足够小的代码窗口,人类会做得更好.
例如,对于浮点数,编译器往往非常保守,可能不了解您的架构的一些更高级的功能.如果您愿意接受一些错误,通常可以比编译器做得更好,如果您发现花费了大量时间,那么在汇编中编写一小段代码是值得的.
以下是一些更相关的示例:
游戏中的示例
英特尔关于使用SSE内在函数优化游戏引擎的文章.最终代码使用内在函数(不是内联汇编程序),因此纯汇编的数量非常少.但他们会查看编译器的汇编器输出,以确定要优化的内容.
Quake的快速反平方根.同样,例程中没有汇编程序,但您需要了解一些有关架构的知识才能进行此类优化.作者知道哪些操作是快速的(乘法,移位),哪些是慢的(除法,sqrt).因此,他们提出了一个非常棘手的平方根实现,完全避免了缓慢的操作.
高性能计算
在游戏领域之外,科学计算领域的人们经常优化事物的废话,以使他们在最新的硬件上快速运行.把它想象成你不能在物理上作弊的游戏.
最近的一个很好的例子是Lattice Quantum Chromodynamics(Lattice QCD). 本文描述了这个问题如何归结为一个非常小的计算内核,该内核在IBM Blue Gene/L上针对PowerPC 440进行了大量优化.每个440都有两个FPU,它们支持一些特殊的三元运算,这些运算对于编译器来说很难开发.如果没有这些优化,Lattice QCD的运行速度会慢得多,如果您的问题需要昂贵的机器上数百万的CPU时间,那么成本很高.
如果您想知道为什么这很重要,请查看本工作中出现的"科学 "一文.使用Lattice QCD,这些人从第一原理计算质子的质量,并且显示去年90%的质量来自强力结合能,其余来自夸克.这是E = mc 2的行动. 这是一个总结.
对于上述所有情况,应用程序不是 100%在汇编时设计或编写的 - 甚至不是关闭的.但是当人们真正需要速度时,他们专注于编写代码的关键部分以便在特定硬件上飞行.
我多年没用汇编语言编写代码,但我可以给出一些我经常看到的理由:
并非所有编译器都可以使用某些CPU优化和指令集(例如,英特尔偶尔添加的新指令集).等待编译器编写者赶上意味着失去竞争优势.
更容易将实际代码与已知的CPU架构和优化相匹配.例如,您了解有关获取机制,缓存等的知识.这应该对开发人员来说是透明的,但事实是它不是,这就是编译器编写者可以优化的原因.
某些硬件级访问仅通过汇编语言可能/实用(例如,在编写设备驱动程序时).
对于汇编语言而言,正式推理有时比高级语言更容易,因为您已经知道代码的最终或几乎最终布局是什么.
在没有API的情况下编程某些3D图形卡(大约在20世纪90年代后期)在汇编语言中通常更加实用和有效,有时在其他语言中是不可能的.但同样,这涉及基于加速器架构的真正专家级游戏,例如以特定顺序手动移入和移出数据.
我怀疑很多人在使用高级语言时会使用汇编语言,尤其是当该语言为C时.手工优化大量通用代码是不切实际的.
汇编程序编程有一个方面是其他人没有提到的 - 你知道应用程序中的每个字节都是你自己努力的结果,而不是编译器的结果.我不会像往常一样在80年代早期回到编写汇编程序中的整个应用程序,但我有时会想念这种感觉......
通常,外行人的组装比C慢(由于C的优化),但许多游戏(我清楚地记得Doom)必须在Assembly中具有游戏的特定部分,因此它可以在普通机器上平稳运行.
这是我所指的例子.
我在第一份工作(80年代)开始使用汇编语言进行专业编程.对于嵌入式系统,内存需求 - RAM和EPROM - 很低.您可以编写简单易用的资源代码.
到80年代末,我已经切换到C.代码更容易编写,调试和维护.非常小的代码片段是用汇编语言编写的 - 对我而言,当我在自己动手的RTOS中编写上下文切换时.(除非是"科学项目",否则你不应该做的事.)
您将在某些Linux内核代码中看到汇编程序片段.最近我用自旋锁和其他同步代码浏览了它.这些代码片段需要访问原子测试和设置操作,操作缓存等.
我认为你很难在大多数通用编程中优化现代C编译器.
我同意@altCognito的意见,你可能更好地花时间思考问题并做得更好.出于某种原因,程序员经常关注微观效率并忽视宏观效率.汇编语言提高性能是一种微观效率.退回以获得更广泛的系统视图可能会暴露系统中的宏问题.解决宏观问题往往可以带来更好的性能提升.一旦宏问题得到解决,然后崩溃到微观层面.
我想微问题是在单个程序员和较小的域中控制的.改变宏观层面的行为需要与更多人沟通 - 这是程序员避免的事情.那整个牛仔与团队的关系.
"是".但是,要理解在大多数情况下,在汇编程序中编写代码的好处并不值得付出努力.在汇编中收到的回报往往比简单地专注于更加努力地思考问题而花费你的时间来考虑更好的做法.
John Carmack和Michael Abrash主要负责编写Quake以及所有进入ID游戏引擎的高性能代码,本书详细介绍了这一点.
我同意ÓlafurWaage,今天编译器非常聪明,并且经常使用许多技术来利用隐藏的架构提升.
现在,至少对于顺序代码来说,一个体面的编译器几乎总是胜过一个经验丰富的汇编语言程序员.但对于矢量代码而言,这是另一个故事.例如,广泛部署的编译器在利用x86 SSE单元的矢量并行功能方面做得不是很好.我是一名编译器编写者,并且利用SSE是我自己的理由列表,而不是信任编译器.
SSE代码在汇编方面比编译器内在函数更好,至少在MSVC中如此.(即不创建额外的数据副本)
某些指令/标志/控制根本不在C级.
例如,检查x86上的溢出是简单的溢出标志.此选项在C中不可用.
缺陷往往是每行运行(语句,代码点等); 虽然对于大多数问题来说,汇编会比使用更高级别的语言使用更多的行,但偶尔也会出现最好(最简洁,最少的行)映射到手头问题的情况.这些案例大多涉及通常的嫌疑,例如驱动程序和嵌入式系统中的位冲击.