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