GCC使用类似架构上的“-march = native”发出截然不同的代码

时间:2015-06-18 16:47:57

标签: c gcc assembly sse avx

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

我编写了本机代码以利用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

使用Piledriver系统上的相同标志进行编译会发出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

2 个答案:

答案 0 :(得分:18)

AVX被禁用,因为整个AMD Bulldozer系列无法有效处理256位AVX指令。在内部,执行单元只有128位宽。因此,256位操作被分开,从而没有超过128位的优势。

在Piledriver上添加侮辱伤害,256位存储中存在一个错误,将吞吐量降低到about 1 every 17 cycles

您的测试用例似乎是一个异常现象。您在该关键循环中没有256位存储 - 这可以避免该错误。这(理论上)使SSE与用于打桩机的AVX相同。

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

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

答案 1 :(得分:6)

&#34; -march = native -Q --help = target&#34;的输出揭示了Piledriver(bdver2)架构默认情况下未启用AVX和AVX2标志。

Broadwell微架构:

  -mavx                                 [enabled]
  -mavx2                                [enabled]

打桩机:

  -mavx                                 [disabled]
  -mavx2                                [disabled]