mtune实际上是如何工作的?

时间:2017-06-12 01:42:28

标签: gcc optimization cpu-architecture instructions instruction-set

有这个相关的问题:13.03.2017

然而,现有答案并没有比GCC手册本身更进一步。最多,我们得到:

  

如果使用-mtune,则编译器将生成可用的代码   他们中的任何一个,但将支持运行速度最快的指令序列   您指定的特定CPU。

  

-mtune=Y选项调整生成的代码以在Y上运行得更快   在其他可能运行的CPU上。

但确切地说 GCC如何支持一个特定的体系结构,当建立时,仍然能够在其他(通常是较旧的)体系结构上运行构建,虽然速度较慢?

我只知道有一件事(但我不是计算机科学家)才能做到这一点,而且那是一个CPU调度员。但是,(对我来说)mtune似乎并没有在幕后生成调度程序,而是其他一些机制可能会生效。

我觉得这样做有两个原因:

  1. 搜索“gcc mtune cpu dispatcher”找不到任何相关内容;和
  2. 如果它基于调度程序,我认为它可以更智能(即使通过除mtune以外的某些选项)并测试cpuid以在运行时检测支持的指令,而不是依赖于在构建时提供的命名体系结构。
  3. 那它是如何运作的呢?

1 个答案:

答案 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上使用movlpsmovhps加载两个加载而不是使用单个movups
在Core2微架构上,两种加载方法更好,如果你看一下Agner Fog的指令表,你会看到movups被解码为4 uop并且每个{{1}的延迟为2个周期1}}是1 uop和1个周期的延迟 这可能是因为当时128位访问被分成两个64位访问 在Skylake上,情况正好相反:movXps表现优于两个movups

所以我们必须拿起一个 总的来说,GCC选择了第一个变体,因为Core2是一个旧的微架构,但我们可以用movXps覆盖它。

1 使用其他开关选择指令集。

相关问题