对于大多数 C/C++ 编译器,有一个可传递给编译器的标志 -march=native
,它告诉编译器为主机 CPU 的微架构和 ISA 扩展调整生成的代码。即使名称不同,基于 LLVM 的编译器通常也有一个等效选项,例如 rustc
或 swiftc
。
以我自己的经验,这个标志可以为数字密集型代码提供巨大的加速,听起来它不会对你只是为你自己的机器编译的代码做出妥协。也就是说,我认为我没有看到任何默认启用它的构建系统或静态编译器:
显然,任何需要您传递它的命令行编译器可执行文件默认情况下都不会使用它。
我想不出任何默认启用此功能的 IDE。
我想不出我使用过的任何通用构建系统(cmake、automake、cargo、spm 等)默认启用它,即使是优化构建也是如此。
我可以想到几个原因,但没有一个是真正令人满意的:
使用 -march=native 不适用于将分发到其他机器的二进制文件。也就是说,我发现自己为自己的机器编译源代码的频率比为其他机器编译的要多,这并不能解释它在调试版本中缺乏使用的原因,因为它没有分发的意图。
至少在 Intel x86 CPU 上,我的理解是不经常使用 AVX 指令会降低性能或电源效率,因为 AVX 单元在不使用时会断电,需要通电才能使用,而且很多 Intel CPU downclock 运行 AVX 指令。尽管如此,它只解释了为什么不启用 AVX,而不是为什么不针对特定微架构对常规指令的处理进行调整。
由于大多数 x86 CPU 使用带有寄存器重命名的花哨的无序超标量管道,因此针对特定微架构调整代码可能并不是特别重要。不过,如果它有帮助,为什么不使用它呢?
-march=native
的事实将很有用。它应该重新开放。
保守的
如果您仔细查看列表中最古老的编译器 gcc 的默认值,您会发现它们非常保守:
默认情况下,在 x86 上,仅激活 SSE 2;甚至没有 SSE 4。
-Wall 和 -Wextra 中的标志集多年来没有改变;有新的有用警告,它们不会添加到 -Wall 或 -Wextra。
为什么?因为它会破坏东西!
整个开发链都依赖于这些便利默认值,任何更改都会带来破坏它们或生成无法在目标上运行的二进制文件的风险。
用户越多,威胁就越大,所以 gcc 的开发者非常非常保守地避免全球范围内的破坏。下一批编译器的开发人员会追随他们的长辈:它被证明是有效的。
注意:rustc
将默认为静态链接,并吹嘘您可以复制二进制文件并将其放到另一台机器上;显然 -march=native
会是一个障碍。
群众友好
事实上,这可能并不重要。您实际上自己认出了它:
以我自己的经验,这个标志可以为数字密集型代码提供巨大的加速
大多数代码都充满了虚拟调用和分支(通常是 OO 代码),而且根本不是数字密集型的。因此,对于大部分代码,SSE 2 通常就足够了。
性能真正重要的少数代码库无论如何都需要在代码和编译器级别上投入大量时间进行性能调整。如果向量化很重要,编译器不会随心所欲:开发人员将使用内置的内在函数并自己编写向量化代码,因为它比安装监控工具来确保自动向量化确实发生要便宜.
此外,即使对于数字密集型代码,主机和目标机器也可能略有不同。编译受益于大量内核,即使在较低频率下也是如此,而执行则受益于高频和可能更少的内核,除非工作易于并行化。
结论
默认不激活 -march=native
使用户更容易上手;因为即使是追求性能的人也可能不太在意它,这意味着失去的比得到的多。
在另一个历史记录中,默认值从一开始就是 -march=native
;用户将用于指定目标架构,我们不会进行此讨论。
-march=native
是破坏性标志。它使二进制文件可能在许多硬件上不兼容(基本上任何不是用于编译的 CPU 的直接后代的 CPU)。默认情况下启用它太危险了。
另一个需要考虑的重要事项是 -march=native
的主要最终用途是优化。默认优化标志是 -O0
(无优化),因此从这个角度来看,默认启用它也没有任何意义。
-O0
-O0
的推理没有意义; -march=native
对 -O0
无效,因此没有理由不默认激活它,并且它仍然有利于任何其他优化级别。
-O0
您基本上不关心优化。既然默认是“我不关心优化”,为什么默认启用主要用于优化的标志?
-O0
,编译器也需要为特定的 CPU 系列发出汇编指令。对于 -O0
,无论是 -march=native
还是 -march=<generic>
都是默认的,仍然指定同一个族,因此两者都与 -O0
完美兼容;并且每当指定另一个优化级别时,-march=native
都会有利于性能。因此,对我来说,-O0
是默认值这一事实与 -march
的默认值无关。
您是从高级用户的角度思考,但是编译器工具链的主要受众不是高级用户,而是开发人员。
大多数开发人员都有单独的开发机器和目标生产系统。在消费者应用程序的情况下,这个目标系统是其他人的机器,具有所有差异。为最常见的分母构建是一个安全的默认设置,因为它减少了仅在开发人员自己的机器之外发生的错误的机会。
当然,在某些情况下,开发人员知道他们将为具有已知架构的单个目标机器开发应用程序。但即使在这种情况下,大多数应用程序对性能不敏感,因此默认的安全选项仍然足够好用,而开发对性能敏感的应用程序的开发人员通常更愿意花时间调整他们的构建配置。
不定期副业成功案例分享
-Weverything
。使用 gcc,您必须一次启用它们 - 有几个 2-3 个警告系列,但没有数量级的改进。