有这个相关的问题:13.03.2017
然而,现有答案并没有比GCC手册本身更进一步。最多,我们得到:
如果使用
-mtune
,则编译器将生成可用的代码 他们中的任何一个,但将支持运行速度最快的指令序列 您指定的特定CPU。
和
-mtune=Y
选项调整生成的代码以在Y上运行得更快 在其他可能运行的CPU上。
但确切地说 GCC如何支持一个特定的体系结构,当建立时,仍然能够在其他(通常是较旧的)体系结构上运行构建,虽然速度较慢?
我只知道有一件事(但我不是计算机科学家)才能做到这一点,而且那是一个CPU调度员。但是,(对我来说)mtune
似乎并没有在幕后生成调度程序,而是其他一些机制可能会生效。
我觉得这样做有两个原因:
mtune
以外的某些选项)并测试cpuid
以在运行时检测支持的指令,而不是依赖于在构建时提供的命名体系结构。那它是如何运作的呢?
答案 0 :(得分:14)
-mtune
不会创建一个调度程序,它不需要一个:我们已经告诉编译器我们所针对的架构。
来自GCC docs:
-mtune = cpu-type
调整cpu-type适用于生成代码的所有内容,但ABI和
除外 一套可用的说明。
这意味着GCC不会使用仅在 cpu-type 1 上可用的指令,但它将生成在 cpu-type上最佳运行的代码。
要理解这最后的陈述,有必要了解架构和微架构之间的区别
该架构意味着ISA(指令集架构)并且不受-mtune
的影响
微架构是架构在硬件中的实现方式。
对于相等的指令集(读取:体系结构),由于实现的内部细节,代码序列可以在CPU(读取微架构)上最佳地运行而在另一个上不运行。
这可以使代码序列仅在一个微架构上最佳。
在生成机器代码时,GCC通常可以自由选择如何订购指令以及使用哪种变体。
它将使用启发式方法生成一系列指令,这些指令在最常见的CPU上快速运行,有时它会牺牲CPU x 的100%最佳解决方案,如果这会损害CPU y , z 和 w 。
当我们使用-mtune=x
时,我们正在微调CPU x 的GCC输出,从而产生一个100%最佳的代码(从GCC角度来看)。
作为一个具体的例子,考虑how this code is compiled:
float bar(float a[4], float b[4])
{
for (int i = 0; i < 4; i++)
{
a[i] += b[i];
}
float r=0;
for (int i = 0; i < 4; i++)
{
r += a[i];
}
return r;
}
当定位Skylake或Core2时,a[i] += b[i];
被矢量化(如果矢量不重叠):
SKYLAKE微架构
movups xmm0, XMMWORD PTR [rsi]
movups xmm2, XMMWORD PTR [rdi]
addps xmm0, xmm2
movups XMMWORD PTR [rdi], xmm0
movss xmm0, DWORD PTR [rdi]
的Core2
pxor xmm0, xmm0
pxor xmm1, xmm1
movlps xmm0, QWORD PTR [rdi]
movlps xmm1, QWORD PTR [rsi]
movhps xmm1, QWORD PTR [rsi+8]
movhps xmm0, QWORD PTR [rdi+8]
addps xmm0, xmm1
movlps QWORD PTR [rdi], xmm0
movhps QWORD PTR [rdi+8], xmm0
movss xmm0, DWORD PTR [rdi]
主要区别在于如何加载xmm
寄存器,在Core2上使用movlps
和movhps
加载两个加载而不是使用单个movups
。
在Core2微架构上,两种加载方法更好,如果你看一下Agner Fog的指令表,你会看到movups
被解码为4 uop并且每个{{1}的延迟为2个周期1}}是1 uop和1个周期的延迟
这可能是因为当时128位访问被分成两个64位访问
在Skylake上,情况正好相反:movXps
表现优于两个movups
。
所以我们必须拿起一个
总的来说,GCC选择了第一个变体,因为Core2是一个旧的微架构,但我们可以用movXps
覆盖它。
1 使用其他开关选择指令集。