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

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

  • 根据我的经验,这个标志可以为数字密集型代码提供巨大的加速,对于只是在自己的计算机上编译的代码来说,它似乎是没有任何妥协的免费选择。尽管如此,我认为我没有看到任何构建系统或静态编译器默认启用它:

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

    • 我想不出任何默认启用该标志的 IDE。

    • 我不能想出任何我使用过的常见构建系统(例如 cmakeautomakecargospm 等),即使进行优化构建,也不会默认启用它。

  • 我能想到一些原因,但没有一个真正令人满意:

    • 使用-march=native对于要分发到其他机器的二进制文件是不合适的。尽管如此,我经常为自己的机器编译源代码,而不是为他人的机器编译,这并不能解释为什么在调试构建中它没有被使用,因为在调试构建中没有分发的意图。

    • 至少在英特尔x86 CPU上,我的理解是很少使用AVX指令可能会降低性能或功率效率,因为当AVX单元不使用时会关闭电源,需要启动电源才能使用,而且很多英特尔CPU会下调时钟频率来运行AVX指令。但这仅说明为什么不会启用AVX,而不是为什么代码不会针对特定微体系结构的常规指令处理进行调整。

    • 由于大多数x86 CPU使用高级乱序超标量流水线和寄存器重命名技术,因此为特定微体系结构调整代码可能并不是特别重要。不过,如果它能有所帮助,为什么不使用呢?


    32
    人们通常希望他们编译的代码能在不同于编译机器的其他机器上运行。 - user2100815
    2
    我认为 Gentoo 用户经常使用它。除此之外,大多数情况下并没有太多用处,二进制文件不能在其他机器上使用。不要忘记你可能会与其他未调整的库进行动态链接,因此像这样优化你的应用程序可能毫无意义。 - user1143634
    9
    “基于观点”的关闭原因被错误地应用到了这个问题上。正如文本所指出的,该关闭原因适用于那些答案很可能“几乎完全基于观点”的问题。这不是一个有争议的问题,使用“-march=native”的事实对于展示是有用的。它应该被重新开放。 - Eric Postpischil
    2
    这个问题更适合在讨论论坛上讨论。 - M.M
    5
    Stack Overflow的一个目的是创建问题和答案库,为将来寻求帮助的其他人提供信息,而不仅仅是为一个人提供信息。 - Eric Postpischil
    显示剩余3条评论
    4个回答

    48

    保守

    如果你仔细观察gcc的默认设置,这是你列表中最古老的编译器,你会发现它们非常保守:

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

    为什么?因为那会破坏事情!

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

    用户越多,威胁就越大,因此gcc的开发人员非常保守,以避免全球性的破坏。而下一批编译器的开发人员则追随前辈的脚步:这已被证明是有效的。

    注:rustc将默认静态链接,并自豪地宣称您只需复制二进制文件并将其放在另一台机器上即可;显然,-march=native会成为障碍。

    大众友好

    事实上,这可能并不重要。你自己也认识到了:

    在我的经验中,此标志对于数值密集型代码可以提供巨大的加速

    大部分代码都充满了虚拟调用和分支(通常是面向对象的代码),并且与数值密集型无关。因此,对于大多数代码,SSE 2 往往已经足够。
    少数需要性能的代码库将需要投入大量时间进行性能调优,无论是在代码还是编译器级别。如果矢量化很重要,开发人员不会把它留给编译器的意愿:他们将使用内置指令并自己编写矢量化代码,因为这比设置监控工具以确保自动矢量化确实发生更便宜。
    此外,即使对于数值密集型的代码,主机和目标机器可能存在轻微差异。编译受益于许多核心,即使频率较低,而执行则受益于高频率,可能少些核心,除非工作很容易进行并行处理。
    结论
    默认情况下不激活 -march=native 使用户更容易入门;即使是追求性能的用户也可能不太关心它,这意味着有更多可以失去的东西而不是获得。
    在一个曾经默认为 -march=native 的替代历史中; 用户将习惯于指定目标架构,并且我们不必进行这个讨论。

    2
    我如何请求所有这些精彩的警告,而不需要逐个调用它们的名称? - L29Ah
    4
    使用Clang编译器,你可以使用选项-Weverything来启用所有警告。而使用gcc编译器,则需要逐个启用警告选项,尽管也有2-3个警告组系列可供选择,但不会带来数量级的改进。 - Matthieu M.

    19

    -march=native 是一个破坏性标志。它使得二进制文件在很多硬件上不兼容(基本上任何不是编译用的CPU直系后代的CPU)。默认情况下启用这个标志太危险了。

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


    什么使它危险?“默认的优化标志是-O0(无优化),因此从这个角度来看,默认情况下启用它也没有意义。”是否有一项法律规定默认的优化标志必须为-O0 - Stargateur
    3
    关于-O0的推理毫无意义;使用-O0-march=native没有任何效果,因此默认情况下激活它是有好处的,并且仍然有益于任何其他优化级别。 - Matthieu M.
    1
    @MatthieuM。我的观点是:使用“-O0”基本上意味着您不关心优化。既然默认值是“我不关心优化”,那么为什么一个主要用于优化的标志会在默认情况下启用呢? - bolov
    @bolov:当您要求进行优化时,它可以默认启用,不是吗? - Matthieu M.
    2
    @bolov:我认为你误解了我的评论。我的观点是,无论优化级别如何,始终会指定一种架构。毕竟,即使在-O0,编译器也需要为特定的CPU系列发出汇编指令。对于-O0,无论-march=native还是-march=<generic>都是默认规定相同系列,因此两者都与-O0完全兼容;而每当指定另一个优化级别时,-march=native对性能有益。所以,对我来说,-O0是默认值对-march的默认值并不重要。 - Matthieu M.
    显示剩余2条评论

    7
    您是从高级用户的角度思考问题,但编译工具链的主要受众不是高级用户,而是开发人员。
    大多数开发人员都有单独的开发机器和目标生产系统。对于消费者应用程序来说,这个目标系统是其他人的机器,存在各种差异。构建最常见的共同点是一个安全的默认选项,因为它减少了只在开发人员自己的机器之外发生的错误的机会。
    当然,也有一些情况,开发人员知道他们将为已知架构的单个目标机器开发应用程序。但即使在这种情况下,大多数应用程序也不会对性能敏感,因此安全选项作为默认值仍然足够好,而那些致力于调整构建配置的开发人员通常更愿意花时间。

    0
    答案已经回答,只是展示了O3和march=native之间的区别。我正在使用大量数学创建3D视频。原始版本没有设置O3和march=native。
    100 out of 900 %11.2222 time left: 0:0:7 time since: 1
    200 out of 900 %22.3333 time left: 0:0:6 time since: 2
    300 out of 900 %33.4444 time left: 0:0:5 time since: 3
    400 out of 900 %44.5556 time left: 0:0:6 time since: 5
    500 out of 900 %55.6667 time left: 0:0:4 time since: 6
    600 out of 900 %66.7778 time left: 0:0:3 time since: 7
    700 out of 900 %77.8889 time left: 0:0:2 time since: 8
    800 out of 900 %89 time left: 0:0:1 time since: 9
    Finished it took 0:0:10
    

    如果我使用O3优化和march=native,输出如下:
    100 out of 900 %11.2222 time left: 0:0:0 time since: 0
    200 out of 900 %22.3333 time left: 0:0:3 time since: 1
    300 out of 900 %33.4444 time left: 0:0:1 time since: 1
    400 out of 900 %44.5556 time left: 0:0:2 time since: 2
    500 out of 900 %55.6667 time left: 0:0:1 time since: 2
    600 out of 900 %66.7778 time left: 0:0:1 time since: 3
    700 out of 900 %77.8889 time left: 0:0:1 time since: 4
    800 out of 900 %89 time left: 0:0:0 time since: 4
    Finished it took 0:0:5
    

    所以O3优化确实有帮助。

    根据新评论进行编辑,程序自昨天以来已经发展了一些,因此时间略有增加。现在是O3 march=native:

    100 out of 900 %11.2222 time left: 0:0:15 time since: 2
    200 out of 900 %22.3333 time left: 0:0:10 time since: 3
    300 out of 900 %33.4444 time left: 0:0:9 time since: 5
    400 out of 900 %44.5556 time left: 0:0:7 time since: 6
    500 out of 900 %55.6667 time left: 0:0:6 time since: 8
    600 out of 900 %66.7778 time left: 0:0:4 time since: 9
    700 out of 900 %77.8889 time left: 0:0:3 time since: 11
    800 out of 900 %89 time left: 0:0:1 time since: 12
    Finished it took 0:0:14
    

    如果我去掉 march=native:
    100 out of 900 %11.2222 time left: 0:0:15 time since: 2
    200 out of 900 %22.3333 time left: 0:0:13 time since: 4
    300 out of 900 %33.4444 time left: 0:0:11 time since: 6
    400 out of 900 %44.5556 time left: 0:0:9 time since: 8
    500 out of 900 %55.6667 time left: 0:0:7 time since: 10
    600 out of 900 %66.7778 time left: 0:0:5 time since: 12
    700 out of 900 %77.8889 time left: 0:0:3 time since: 14
    800 out of 900 %89 time left: 0:0:1 time since: 16
    Finished it took 0:0:18
    

    你第一次运行时是比较了gcc -march=native而没有使用任何-O选项吗?默认情况下是-O0,没有优化,更不用说自动向量化了,在这种情况下-march=native最常用。(尽管也适用于变量计数移位和位操作)。-march=native在没有-O2-O3的情况下几乎没有影响;你应该比较使用默认通用目标架构和调整的-O3-O3 -march=native。如果你想尝试允许一些FP舍入差异之类的东西,可能还要加上-ffast-math - Peter Cordes
    1
    干杯。是的,你说得对,使用 -march=native 和 -O3 的结果与仅使用 -march=native 的结果有很大的不同。根据这个输出编辑了结果。 - user2753089

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