为啥 -march=native 很少使用?

Posted

技术标签:

【中文标题】为啥 -march=native 很少使用?【英文标题】:Why is -march=native used so rarely?为什么 -march=native 很少使用? 【发布时间】:2019-03-10 05:13:55 【问题描述】:

对于大多数 C/C++ 编译器,有一个标志可传递给编译器,-march=native,它告诉编译器为主机 CPU 的微架构和 ISA 扩展调整生成的代码。即使名称不同,基于 LLVM 的编译器通常也有一个等效选项,例如 rustcswiftc

根据我自己的经验,这个标志可以为数字密集型代码提供巨大的加速,并且它听起来对于你只是为你自己的机器编译的代码来说是没有妥协的。也就是说,我认为我没有看到任何默认启用它的构建系统或静态编译器:

显然,任何需要您传递它的命令行编译器可执行文件默认都不会使用它。

我想不出任何默认启用此功能的 IDE。

我想不出任何我使用过的通用构建系统(cmakeautomakecargospm 等)默认启用它,即使是优化构建。

我能想到几个原因,但没有一个是真正令人满意的:

使用-march=native 不适用于将分发到其他计算机的二进制文件。也就是说,我发现自己为自己的机器编译源代码的频率比为其他机器编译的要多,这并不能解释它在调试版本中缺乏使用的原因,因为它无意分发。

至少在 Intel x86 CPU 上,据我了解,不经常使用 AVX 指令会降低性能或电源效率,因为 AVX 单元在不使用时会断电,需要通电才能使用,许多英特尔 CPU 降频以运行 AVX 指令。尽管如此,它只解释了为什么不启用 AVX,而不是为什么不针对特定微架构对常规指令的处理进行调整。

由于大多数 x86 CPU 使用带有寄存器重命名的花哨的乱序超标量流水线,因此针对特定微架构调整代码可能并不是特别重要。不过,如果它可以有帮助,为什么不使用它呢?

【问题讨论】:

人们通常喜欢他们编译的代码在编译它的机器以外的机器上运行。 我认为 Gentoo 用户一直在使用它。除此之外 - 大多数时候不会给你那么多,二进制文件不能在其他机器上使用。不要忘记,您可能正在动态链接可能未调整的其他库,因此像这样优化您的应用程序可能一无所获。 这个“基于意见”的原因被误用在了这个问题上。正如正文所指出的那样,关闭的原因是针对其答案可能“几乎完全基于意见”的问题。这不是一个有争议的问题,关于使用-march=native 的事实将很有用。它应该重新打开。 这个问题更适合论坛讨论 @interjay:Stack Overflow 的一个目的是创建一个问题和答案的存储库,为将来寻求的其他人提供信息,而不仅仅是向提出问题的人提供信息。 【参考方案1】:

保守的

如果您仔细查看列表中最古老的编译器 gcc 的默认值,您会发现它们非常保守:

默认情况下,在 x86 上,仅激活 SSE 2;甚至没有 SSE 4。 -Wall-Wextra 中的标志集多年来没有改变;有新的有用警告,它们不会添加到 -Wall-Wextra

为什么?因为它会破坏事物!

整个开发链都依赖于这些便利的默认设置,任何更改都会带来破坏它们或生成无法在目标上运行的二进制文件的风险。

用户越多,威胁越大,所以 gcc 的开发者非常非常保守,以避免全球范围内的破坏。下一批编译器的开发者将追随他们的长辈:它已被证明是有效的。

注意:rustc 将默认为静态链接,并吹嘘您可以复制二进制文件并将其放到另一台机器上;显然-march=native 会是一个障碍。

大众友好

事实上,这可能并不重要。您实际上自己认出了它:

根据我自己的经验,这个标志可以为数字密集型代码提供巨大的加速

大多数代码都充满了虚拟调用和分支(通常是 OO 代码),而且完全不是数字密集型的。因此,对于大部分代码,SSE 2 通常就足够了。

对于少数几个对性能真正重要的代码库,无论如何都需要在代码和编译器级别上投入大量时间进行性能调优。如果向量化很重要,编译器不会随心所欲:开发人员将使用内置的内在函数并自己编写向量化代码,因为它比安装监控工具来确保自动向量化确实发生要便宜.

此外,即使是数字密集型代码,主机和目标机器也可能略有不同。编译受益于大量内核,即使在较低频率下也是如此,而执行则受益于高频和可能更少的内核,除非工作易于并行化。

结论

默认不激活-march=native,让用户更容易上手;因为即使是追求性能的人也可能不太在意,这意味着失去的比得到的多。


在另一个历史记录中,默认值从一开始就是-march=native;用户将用于指定目标架构,我们不会进行此讨论。

【讨论】:

如何在不点名的情况下请求所有这些美妙的警告? @L29Ah:使用 Clang,您可以使用 -Weverything。使用 gcc,您必须一次启用它们 - 有几个 2-3 个警告系列,但没有数量级的改进。【参考方案2】:

-march=native 是破坏性标志。它使二进制文件可能在许多硬件上不兼容(基本上任何不是用于编译的 CPU 的直接后代的 CPU)。默认启用此功能太危险了。

另一个需要考虑的重要事情是-march=native 的主要最终用途是优化。默认优化标志是-O0(无优化),因此从这个角度来看,默认启用它也没有意义。

【讨论】:

是什么让它变得危险? “默认优化标志是 -O0(无优化),因此从这个角度来看,默认启用它也没有任何意义。” ?有没有规定默认优化标志必须是-O0 -O0 的推理没有意义; -march=native-O0 无效,因此没有理由不默认激活它,它仍然有利于任何其他优化级别。 @MatthieuM。我的意思是:使用-O0,您基本上不关心优化。既然默认是“我不关心优化”为什么会默认启用一个主要用于优化的标志? @bolov:当你要求优化时,它可以默认启用,不是吗? @bolov:我想你误解了我的评论。我的观点是,无论优化级别如何,总是有指定的架构。毕竟,即使在-O0,编译器也需要为特定的 CPU 系列发出汇编指令。对于-O0,无论-march=native还是-march=<generic>都是默认的还是指定同一个family,所以两者都与-O0完美兼容;并且每当指定另一个优化级别时,-march=native 对性能有好处。所以,对我来说,-O0 是默认值这一事实与 -march 的默认值无关。【参考方案3】:

您是站在高级用户的角度思考,但编译器工具链的主要受众不是高级用户,而是开发人员。

大多数开发人员都有单独的开发机器和目标生产系统。在消费者应用程序的情况下,这个目标系统是其他人的机器,具有所有差异。为最常见的分母构建是一个安全的默认设置,因为它减少了仅在开发人员自己的机器之外发生的错误的机会。

当然,在某些情况下,开发人员知道他们将为具有已知架构的单个目标机器开发应用程序。但即使在这种情况下,大多数应用程序对性能不敏感,因此默认的安全选项仍然足够好用,而开发对性能敏感的应用程序的开发人员通常更愿意花时间调整他们的构建配置。

【讨论】:

以上是关于为啥 -march=native 很少使用?的主要内容,如果未能解决你的问题,请参考以下文章

当我使用 react-native-geolocation-service 时,为啥 android 会寻找 RNCGeolocation?

为啥我的数据流作业使用很少的 cpu 而它却无法赶上消息积压

为啥 react-native 包使用原生 SDK 而不是 JS/web 版本?

React Native 已经有了异步存储。为啥我应该在我的 react native 应用中使用 Redux 和 Redux Thunk?

React Native:为啥使用 Apollo 的 GraphQL 查询返回未定义?

为啥我使用 react-native juspay 开始付款时会出现此错误?