为什么-march = native很少使用?

lcm*_*lin 21 gcc clang compiler-optimization

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

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

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

  • 我想不出任何默认启用它的IDE.

  • 我想不出任何共同构建系统我已经与(工作的cmake,automake,cargo,spm,等),使其能够在默认情况下,即使是最优化的基础之上.

我可以想到一些原因,但没有一个真的令人满意:

  • 使用-march=native不适合将分发给其他计算机的二进制文件.也就是说,我发现自己为自己的机器编译源代码比其他机器更频繁,这并不能解释它在调试版本中的缺乏用途,因为它没有分发的意图.

  • 至少在Intel x86 CPU上,我的理解是不经常使用AVX指令会降低性能或功率效率,因为AVX单元在不使用时断电,要求它上电以供使用,以及许多Intel CPU下行运行AVX指令.尽管如此,它只解释了为什么不启用AVX,而不是为什么代码不会针对特定微架构处理常规指令而调整.

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

bol*_*lov 10

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

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

  • 关于“-O0”的推理没有意义;`-march=native` 对 `-O0` 没有影响,因此没有理由不默认激活它,并且它仍然有利于任何其他优化级别。 (3认同)
  • @马修M。我的观点是:使用“-O0”,您基本上不关心优化。既然默认是“我不关心优化”,为什么默认会启用一个主要用于优化的标志呢? (2认同)
  • @bolov:我认为你误解了我的评论。我的观点是,无论优化级别如何,总是有指定的架构。毕竟,即使在“-O0”,编译器也需要为特定的 CPU 系列发出汇编指令。对于“-O0”,无论“-march=native”还是“-march=<generic>”默认仍然指定同一个系列,因此两者都与“-O0”完美兼容;每当指定另一个优化级别时,“-march=native”都有利于性能。因此,对我来说,“-O0”是默认值这一事实对于“-march”的默认值并不重要。 (2认同)

Mat*_* M. 8

保守

如果你仔细看看列表中最老的编译器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 个警告,但不是数量级的改进。 (4认同)
  • 我如何请求所有这些美妙的警告而不用逐一称呼他们的名字? (2认同)

Lie*_*yan 7

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

大多数开发人员都有单独的开发机器和目标生产系统。对于消费者应用程序,该目标系统是其他人的机器,具有所有差异。针对最常见的分母进行构建是一种安全的默认设置,因为它减少了仅在开发人员自己的计算机外部发生的错误的可能性。

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