Clang++/g++ 没有在 Aarch64 上对代码进行矢量化
Clang++/g++ not vectorizing code on Aarch64
我有一个带四核 Cortex-A57 的 SBC,我正在尝试使用编译器自动矢量化对 Neon 进行试验。在 Ubuntu 18.04 上同时使用 clang++ (5.0.1-4) 和 g++ (7.4.0),下面非常简单的代码没有向量化(即,它在任何时候都不使用 v 寄存器):
#include <iostream>
#include <cstdint>
int main(void)
{
const uint32_t LEN = 16;
float input_1[LEN] __attribute__((__aligned__(16))),
input_2[LEN] __attribute__((__aligned__(16))),
output [LEN] __attribute__((__aligned__(16)));
for(uint32_t i = 0; i < LEN; i++)
{
input_1[i] = i;
input_2[i] = i * 2;
}
for(uint32_t i = 0; i < LEN; i++)
output[i] = input_1[i] * input_2[i];
std::cout << output[0] << std::endl;
return 0;
}
它只是声明了 3 个数组,填充 2 个数组并将它们相乘到输出数组上。最后的 cout 是为了防止编译器摆脱一切。我没有 post objdump -d 的结果以避免向人们的屏幕发送垃圾邮件,但如果有人需要,我可以。编译行是:clang++ -O3 neon.cpp -o neon (g++ 相同)
我也试过 -mcpu=cortex-a57 但它也不会矢量化。然后我发现这个posthttps://gcc.gnu.org/bugzilla/show_bug.cgi?id=65951
评论 11 说,根据情况,如果没有性能优势,编译器可能会决定不向量化。根据您的经验,似乎是这样,还是我遗漏了什么?
====== 编辑 ======
g++ 为上述代码生成的程序集是:
.arch armv8-a
.file "neon_test.cpp"
.text
.section .text.startup,"ax",@progbits
.align 2
.p2align 3,,7
.global main
.type main, %function
main:
.LFB1563:
.cfi_startproc
stp x29, x30, [sp, -112]!
.cfi_def_cfa_offset 112
.cfi_offset 29, -112
.cfi_offset 30, -104
adrp x1, .LC0
adrp x0, :got:_ZSt4cout
movi d0, #0
add x29, sp, 0
.cfi_def_cfa_register 29
str x19, [sp, 16]
.cfi_offset 19, -96
adrp x19, :got:__stack_chk_guard
ldr q1, [x1, #:lo12:.LC0]
ldr x19, [x19, #:got_lo12:__stack_chk_guard]
ldr x0, [x0, #:got_lo12:_ZSt4cout]
ldr x1, [x19]
str x1, [x29, 104]
mov x1,0
str q1, [x29, 32]
bl _ZNSo9_M_insertIdEERSoT_
bl _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
ldr x0, [x29, 104]
ldr x1, [x19]
eor x1, x0, x1
cbnz x1, .L5
ldr x19, [sp, 16]
mov w0, 0
ldp x29, x30, [sp], 112
.cfi_remember_state
.cfi_restore 30
.cfi_restore 29
.cfi_restore 19
.cfi_def_cfa 31, 0
ret
.L5:
.cfi_restore_state
bl __stack_chk_fail
.cfi_endproc
.LFE1563:
.size main, .-main
.section .rodata.cst16,"aM",@progbits,16
.align 4
.LC0:
.word 0
.word 1073741824
.word 1090519040
.word 1099956224
.section .text.startup
.align 2
.p2align 3,,7
.type _GLOBAL__sub_I_main, %function
_GLOBAL__sub_I_main:
.LFB2050:
.cfi_startproc
stp x29, x30, [sp, -32]!
.cfi_def_cfa_offset 32
.cfi_offset 29, -32
.cfi_offset 30, -24
add x29, sp, 0
.cfi_def_cfa_register 29
str x19, [sp, 16]
.cfi_offset 19, -16
adrp x19, .LANCHOR0
add x19, x19, :lo12:.LANCHOR0
mov x0, x19
bl _ZNSt8ios_base4InitC1Ev
adrp x0, :got:_ZNSt8ios_base4InitD1Ev
mov x1, x19
ldr x19, [sp, 16]
adrp x2, __dso_handle
ldr x0, [x0, #:got_lo12:_ZNSt8ios_base4InitD1Ev]
add x2, x2, :lo12:__dso_handle
ldp x29, x30, [sp], 32
.cfi_restore 30
.cfi_restore 29
.cfi_restore 19
.cfi_def_cfa 31, 0
b __cxa_atexit
.cfi_endproc
.LFE2050:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 3
.xword _GLOBAL__sub_I_main
.bss
.align 3
.set .LANCHOR0,. + 0
.type _ZStL8__ioinit, %object
.size _ZStL8__ioinit, 1
_ZStL8__ioinit:
.zero 1
.hidden __dso_handle
.ident "GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section .note.GNU-stack,"",@progbits
您在执行实际数学运算的同一函数中生成输入。
问:那么编译器在-o3
下做了什么?
A:它在构建时进行数学计算,并将结果存储在某种 LUT 中。 (.LC0
)
您应该在外部函数中初始化输入,最好在不同的文件中,以避免这种 "build time math" 编译器作弊。
0 = 0.0f = 0.0f * 2.0f * 0.0f
1073741824 = 2.0f = 1.0f * 2.0f * 1.0f
1090519040 = 8.0f = 2.0f * 2.0f * 2.0f
1099956224 = 18.0f = 3.0f * 2.0f * 3.0f
我有一个带四核 Cortex-A57 的 SBC,我正在尝试使用编译器自动矢量化对 Neon 进行试验。在 Ubuntu 18.04 上同时使用 clang++ (5.0.1-4) 和 g++ (7.4.0),下面非常简单的代码没有向量化(即,它在任何时候都不使用 v 寄存器):
#include <iostream>
#include <cstdint>
int main(void)
{
const uint32_t LEN = 16;
float input_1[LEN] __attribute__((__aligned__(16))),
input_2[LEN] __attribute__((__aligned__(16))),
output [LEN] __attribute__((__aligned__(16)));
for(uint32_t i = 0; i < LEN; i++)
{
input_1[i] = i;
input_2[i] = i * 2;
}
for(uint32_t i = 0; i < LEN; i++)
output[i] = input_1[i] * input_2[i];
std::cout << output[0] << std::endl;
return 0;
}
它只是声明了 3 个数组,填充 2 个数组并将它们相乘到输出数组上。最后的 cout 是为了防止编译器摆脱一切。我没有 post objdump -d 的结果以避免向人们的屏幕发送垃圾邮件,但如果有人需要,我可以。编译行是:clang++ -O3 neon.cpp -o neon (g++ 相同)
我也试过 -mcpu=cortex-a57 但它也不会矢量化。然后我发现这个posthttps://gcc.gnu.org/bugzilla/show_bug.cgi?id=65951
评论 11 说,根据情况,如果没有性能优势,编译器可能会决定不向量化。根据您的经验,似乎是这样,还是我遗漏了什么?
====== 编辑 ======
g++ 为上述代码生成的程序集是:
.arch armv8-a
.file "neon_test.cpp"
.text
.section .text.startup,"ax",@progbits
.align 2
.p2align 3,,7
.global main
.type main, %function
main:
.LFB1563:
.cfi_startproc
stp x29, x30, [sp, -112]!
.cfi_def_cfa_offset 112
.cfi_offset 29, -112
.cfi_offset 30, -104
adrp x1, .LC0
adrp x0, :got:_ZSt4cout
movi d0, #0
add x29, sp, 0
.cfi_def_cfa_register 29
str x19, [sp, 16]
.cfi_offset 19, -96
adrp x19, :got:__stack_chk_guard
ldr q1, [x1, #:lo12:.LC0]
ldr x19, [x19, #:got_lo12:__stack_chk_guard]
ldr x0, [x0, #:got_lo12:_ZSt4cout]
ldr x1, [x19]
str x1, [x29, 104]
mov x1,0
str q1, [x29, 32]
bl _ZNSo9_M_insertIdEERSoT_
bl _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
ldr x0, [x29, 104]
ldr x1, [x19]
eor x1, x0, x1
cbnz x1, .L5
ldr x19, [sp, 16]
mov w0, 0
ldp x29, x30, [sp], 112
.cfi_remember_state
.cfi_restore 30
.cfi_restore 29
.cfi_restore 19
.cfi_def_cfa 31, 0
ret
.L5:
.cfi_restore_state
bl __stack_chk_fail
.cfi_endproc
.LFE1563:
.size main, .-main
.section .rodata.cst16,"aM",@progbits,16
.align 4
.LC0:
.word 0
.word 1073741824
.word 1090519040
.word 1099956224
.section .text.startup
.align 2
.p2align 3,,7
.type _GLOBAL__sub_I_main, %function
_GLOBAL__sub_I_main:
.LFB2050:
.cfi_startproc
stp x29, x30, [sp, -32]!
.cfi_def_cfa_offset 32
.cfi_offset 29, -32
.cfi_offset 30, -24
add x29, sp, 0
.cfi_def_cfa_register 29
str x19, [sp, 16]
.cfi_offset 19, -16
adrp x19, .LANCHOR0
add x19, x19, :lo12:.LANCHOR0
mov x0, x19
bl _ZNSt8ios_base4InitC1Ev
adrp x0, :got:_ZNSt8ios_base4InitD1Ev
mov x1, x19
ldr x19, [sp, 16]
adrp x2, __dso_handle
ldr x0, [x0, #:got_lo12:_ZNSt8ios_base4InitD1Ev]
add x2, x2, :lo12:__dso_handle
ldp x29, x30, [sp], 32
.cfi_restore 30
.cfi_restore 29
.cfi_restore 19
.cfi_def_cfa 31, 0
b __cxa_atexit
.cfi_endproc
.LFE2050:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 3
.xword _GLOBAL__sub_I_main
.bss
.align 3
.set .LANCHOR0,. + 0
.type _ZStL8__ioinit, %object
.size _ZStL8__ioinit, 1
_ZStL8__ioinit:
.zero 1
.hidden __dso_handle
.ident "GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section .note.GNU-stack,"",@progbits
您在执行实际数学运算的同一函数中生成输入。
问:那么编译器在-o3
下做了什么?
A:它在构建时进行数学计算,并将结果存储在某种 LUT 中。 (.LC0
)
您应该在外部函数中初始化输入,最好在不同的文件中,以避免这种 "build time math" 编译器作弊。
0 = 0.0f = 0.0f * 2.0f * 0.0f
1073741824 = 2.0f = 1.0f * 2.0f * 1.0f
1090519040 = 8.0f = 2.0f * 2.0f * 2.0f
1099956224 = 18.0f = 3.0f * 2.0f * 3.0f