GCC 在类似架构上使用“-march=native”发出截然不同的代码
GCC emits vastly different code using "-march=native" on similar architectures
我正在用 C 编写 OpenCL 基准测试。目前,它使用 C 代码测量 CL 设备和系统处理器的融合 multiply-accumulate 性能。然后交叉检查结果的准确性。
我编写了本机代码以利用 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 在做什么。
“-march=native -Q --help=target”的输出显示 AVX 和 AVX2 标志在打桩机 (bdver2) 架构上默认未启用。
布罗德韦尔:
-mavx [enabled]
-mavx2 [enabled]
打桩机:
-mavx [disabled]
-mavx2 [disabled]
AVX 被禁用,因为整个 AMD Bulldozer 系列不能有效地处理 256 位 AVX 指令。在内部,执行单元只有 128 位宽。因此 256 位操作被拆分,因此与 128 位操作相比没有任何优势。
雪上加霜的是,在 Piledriver 上,256 位存储中存在一个错误,该错误会将吞吐量降低至 about 1 every 17 cycles。
您的测试用例似乎有异常。您在该关键循环中没有 256 位存储 - 这避免了该错误。这(理论上)使 SSE 与打桩机的 AVX 相提并论。
决胜局来自打桩机支持的FMA3指令。这可能就是 AVX 循环在打桩机上变得更快的原因。
您可以尝试的一件事是 -mfma4
-mtune=bdver2
看看会发生什么。
我正在用 C 编写 OpenCL 基准测试。目前,它使用 C 代码测量 CL 设备和系统处理器的融合 multiply-accumulate 性能。然后交叉检查结果的准确性。
我编写了本机代码以利用 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 在做什么。
“-march=native -Q --help=target”的输出显示 AVX 和 AVX2 标志在打桩机 (bdver2) 架构上默认未启用。
布罗德韦尔:
-mavx [enabled]
-mavx2 [enabled]
打桩机:
-mavx [disabled]
-mavx2 [disabled]
AVX 被禁用,因为整个 AMD Bulldozer 系列不能有效地处理 256 位 AVX 指令。在内部,执行单元只有 128 位宽。因此 256 位操作被拆分,因此与 128 位操作相比没有任何优势。
雪上加霜的是,在 Piledriver 上,256 位存储中存在一个错误,该错误会将吞吐量降低至 about 1 every 17 cycles。
您的测试用例似乎有异常。您在该关键循环中没有 256 位存储 - 这避免了该错误。这(理论上)使 SSE 与打桩机的 AVX 相提并论。
决胜局来自打桩机支持的FMA3指令。这可能就是 AVX 循环在打桩机上变得更快的原因。
您可以尝试的一件事是 -mfma4
-mtune=bdver2
看看会发生什么。