【问题标题】:GCC emits vastly different code using "-march=native" on similar architecturesGCC 在类似架构上使用“-march=native”发出截然不同的代码
【发布时间】:2015-09-04 10:31:21
【问题描述】:

我正在用 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

在 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

【问题讨论】:

  • 你试过-v看看-march=native扩展成什么?请参阅文档中的-fopt-info-...
  • -ftree-vectorizer-verbose-fopt-info-vec-* 取代

标签: c gcc assembly sse avx


【解决方案1】:

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

雪上加霜的是,在 Piledriver 上,256 位存储中存在一个错误,将吞吐量降低到 about 1 every 17 cycles


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

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

你可以试试-mfma4-mtune=bdver2,看看会发生什么。

【讨论】:

  • 但我认为由于共享128位单元,256位操作可以从模块中的其他核心借用执行单元,因此在这种情况下效率更高。我相信 AMD 甚至宣传过(“Flex FP”)
  • 它有两个 128 位管道。所以每个周期,它可以处理 2 x 128 位指令或 1 x 256 位指令。吞吐量是一样的。相比之下,自 Sandy Bridge 以来的英特尔处理器拥有 2 个 256 位管道。
  • 使用 2 条指令来获得相同的性能并不好,因为它会占用缓存和寄存器等额外资源(因为您无法处理 256 位寄存器的上半部分)。
  • 仅供参考,我已经测试了提到的商店,确实那些慢得要命。但是,它们可以很容易地被两条vextractf128 指令模拟,它们的工作速度与普通的 128 位对应指令一样快。不过,我还没有尝试提出一个可以显示 256 位版本优势的测试用例。
  • GCC 有选项 -mavx256-split-unaligned-store 将未对齐的 256 位存储拆分为 128 位 VMOVUPS + VEXTRACTF128。另外,还有-mprefer-avx128在自动矢量化器中使用AVX128。
【解决方案2】:

“-march=native -Q --help=target”的输出表明,在 Piledriver (bdver2) 架构上默认情况下未启用 AVX 和 AVX2 标志。

布罗德韦尔:

  -mavx                                 [enabled]
  -mavx2                                [enabled]

打桩机:

  -mavx                                 [disabled]
  -mavx2                                [disabled]

【讨论】:

  • 有趣的是,debian gcc 版本 4.7.2-5 启用了 avx 并生成了这样的代码,尽管没有矢量化(它主要使用 ss 变体)。
  • PS:注意 avx2 被禁用是有原因的,因为打桩机不做 avx2 :(
  • AVX 被禁用,因为整个 Bulldozer 系列都不能有效地运行 AVX。 (不比 SSE 快)此外,在 Piledriver 上,256 位存储中存在一个错误,这使得 AVX 几乎完全无用。您的情况是异常的,因为您没有 256 位存储,而 FMA3 可能会成为一个加号。
  • 这正是我想要的,神秘主义者。谢谢你的解释!
  • @Mysticial 这似乎是一个答案,这是非常有用的信息,我不喜欢 cmets 中的重要信息,因为我看到有用的 cmets 消失了,从长远来看这对任何人都没有帮助。
猜你喜欢
  • 2016-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 2016-08-12
  • 2020-08-18
  • 2020-11-12
相关资源
最近更新 更多