c语言 - GCC 在类似架构上使用 "-march=native" 发出截然不同的代码



我正在用 C 语言编写一个 OpenCL 基准测试。 目前,它使用 C 代码测量 CL 设备和系统处理器的融合乘法累加性能。然后交叉检查结果的准确性。

我编写了本机代码来利用 GCC 的自动矢量化器,它可以工作。但是,我注意到GCC使用"-march=native"标志有一些奇怪的行为。

这是我的循环:

#define BUFFER_SIZE_SQRT 4096
#define SQUARE(n) (n * n)
#define ROUNDS_PER_ITERATION 48
static float* cpu_result_matrix(const float* a, const float* b, const float* c)
{
    float* res = aligned_alloc(16, SQUARE(BUFFER_SIZE_SQRT) * sizeof(float));
    const unsigned buff_size = SQUARE(BUFFER_SIZE_SQRT);
    const unsigned round_cnt = ROUNDS_PER_ITERATION;
    float lres;
    for(unsigned i = 0; i < buff_size; i++)
    {
        lres = 0;
        for(unsigned j = 0; j < round_cnt; j++)
        {
            lres += a[i] * ((b[i] * c[i]) + b[i]);
            lres += b[i] * ((c[i] * a[i]) + c[i]);
            lres += c[i] * ((a[i] * b[i]) + a[i]);
        }
        res[i] = lres;
    }
    return res;
}

当我在 Broadwell 系统上使用 "-march=native -Ofast" 编译时,我得到了很好的矢量化 AVX 代码。

.L19:
        vmovups ymm0, YMMWORD PTR [rcx+rdx]
        mov     eax, 48
        vmovups ymm2, YMMWORD PTR [rdi+rdx]
        vaddps  ymm1, ymm0, ymm5
        vmovups ymm3, YMMWORD PTR [rsi+rdx]
        vaddps  ymm4, ymm2, ymm5
        vmulps  ymm1, ymm1, ymm2
        vfmadd132ps     ymm4, ymm1, ymm0
        vaddps  ymm1, ymm3, ymm5
        vmulps  ymm0, ymm2, ymm0
        vmulps  ymm0, ymm0, ymm1
        vfmadd132ps     ymm4, ymm0, ymm3
        vmovaps ymm1, ymm4
        vxorps  xmm0, xmm0, xmm0
        .p2align 4,,10
        .p2align 3

在打桩机系统上使用相同的标志进行编译会发出 SSE2 指令,但没有 AVX 指令,即使架构支持它也是如此。(我在这里澄清我的标题,说 Broadwell 和 Piledriver 没有什么相似之处,但它们都支持类似的矢量指令集扩展,所以发出的代码应该是相似的。

.L19:
        mov     eax, 48
        movups  xmm0, XMMWORD PTR [rcx+rdx]
        movups  xmm2, XMMWORD PTR [r13+0+rdx]
        movaps  xmm4, xmm0
        movaps  xmm1, xmm2
        movups  xmm3, XMMWORD PTR [rsi+rdx]
        addps   xmm4, xmm5
        addps   xmm1, xmm5
        mulps   xmm4, xmm2
        mulps   xmm1, xmm0
        mulps   xmm0, xmm2
        addps   xmm1, xmm4
        movaps  xmm4, xmm1
        mulps   xmm4, xmm3
        addps   xmm3, xmm5
        mulps   xmm0, xmm3
        addps   xmm4, xmm0
        pxor    xmm0, xmm0
        movaps  xmm1, xmm4
        .p2align 4,,10
        .p2align 3

我甚至可以使用 -march=broadwell 编译整个项目,并在 Piledriver 系统上运行它,它可以工作,性能提升 ~100%。

我正在使用GCC 5.1.0进行编译,而"-ftree-vectorizer-verbose"似乎不再起作用,因此编译器的行为非常不透明。我没有找到任何关于弃用标志的信息,所以我不确定为什么它不再起作用,我真的很想知道 GCC 在做什么。

整个项目在这里: https://github.com/jakogut/clperf/tree/v0.1

AVX 被禁用,因为整个 AMD 推土机系列无法有效地处理 256 位 AVX 指令。在内部,执行单元只有 128 位宽。因此,256 位操作被拆分,因此与 128 位相比没有任何好处。

上加霜的是,在 Piledriver 上,256 位存储中存在一个错误,将吞吐量降低到每 17 个周期约 1 个。


您的测试用例似乎异常。该关键循环中没有 256 位存储 - 这避免了错误。这(理论上)使SSE与打桩机的AVX相提并论。

决胜局来自打桩机支持的FMA3指令。这可能就是为什么AVX循环在打桩机上变得更快的原因。

您可以尝试的一件事是-mfma4 -mtune=bdver2,看看会发生什么。

"-march=native -Q --help=target"的输出显示,默认情况下,在打桩机(bdver2)架构上不启用AVX和AVX2标志。

布罗德韦尔:

  -mavx                                 [enabled]
  -mavx2                                [enabled]

打桩机:

  -mavx                                 [disabled]
  -mavx2                                [disabled]

最新更新