gpt4 book ai didi

c++ - ARM neon 优化 - 摆脱多余的负载

转载 作者:太空狗 更新时间:2023-10-29 20:50:29 25 4
gpt4 key购买 nike

我正在尝试使用 arm neon 构建优化的右手矩阵乘法。这个

void transform ( glm::mat4 const & matrix, glm::vec4 const & input, glm::vec4 & output )
{
float32x4_t & result_local = reinterpret_cast < float32x4_t & > (*(&output[0]));
float32x4_t const & input_local = reinterpret_cast < float32x4_t const & > (*(&input[0] ));

result_local = vmulq_f32 ( reinterpret_cast < float32x4_t const & > ( matrix[ 0 ] ), input_local );
result_local = vmlaq_f32 ( result_local, reinterpret_cast < float32x4_t const & > ( matrix[ 1 ] ), input_local );
result_local = vmlaq_f32 ( result_local, reinterpret_cast < float32x4_t const & > ( matrix[ 2 ] ), input_local );
result_local = vmlaq_f32 ( result_local, reinterpret_cast < float32x4_t const & > ( matrix[ 3 ] ), input_local );
}

编译器 (gcc) 确实会生成 neon 指令,但是,输入参数(应该在 x1 中)似乎在每次 fmla 调用后都会重新加载到 q1:

0x0000000000400a78 <+0>:    ldr q1, [x1]
0x0000000000400a7c <+4>: ldr q0, [x0]
0x0000000000400a80 <+8>: fmul v0.4s, v0.4s, v1.4s
0x0000000000400a84 <+12>: str q0, [x2]
0x0000000000400a88 <+16>: ldr q2, [x0,#16]
0x0000000000400a8c <+20>: ldr q1, [x1]
0x0000000000400a90 <+24>: fmla v0.4s, v2.4s, v1.4s
0x0000000000400a94 <+28>: str q0, [x2]
0x0000000000400a98 <+32>: ldr q2, [x0,#32]
0x0000000000400a9c <+36>: ldr q1, [x1]
0x0000000000400aa0 <+40>: fmla v0.4s, v2.4s, v1.4s
0x0000000000400aa4 <+44>: str q0, [x2]
0x0000000000400aa8 <+48>: ldr q2, [x0,#48]
0x0000000000400aac <+52>: ldr q1, [x1]
0x0000000000400ab0 <+56>: fmla v0.4s, v2.4s, v1.4s
0x0000000000400ab4 <+60>: str q0, [x2]
0x0000000000400ab8 <+64>: ret

是否也可以规避这一点?

编译器是带有 O2 选项的 gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu。

问候

编辑:删除对 input_local 的引用就成功了:

0x0000000000400af0 <+0>:    ldr q1, [x1]
0x0000000000400af4 <+4>: ldr q0, [x0]
0x0000000000400af8 <+8>: fmul v0.4s, v1.4s, v0.4s
0x0000000000400afc <+12>: str q0, [x2]
0x0000000000400b00 <+16>: ldr q2, [x0,#16]
0x0000000000400b04 <+20>: fmla v0.4s, v1.4s, v2.4s
0x0000000000400b08 <+24>: str q0, [x2]
0x0000000000400b0c <+28>: ldr q2, [x0,#32]
0x0000000000400b10 <+32>: fmla v0.4s, v1.4s, v2.4s
0x0000000000400b14 <+36>: str q0, [x2]
0x0000000000400b18 <+40>: ldr q2, [x0,#48]
0x0000000000400b1c <+44>: fmla v0.4s, v1.4s, v2.4s
0x0000000000400b20 <+48>: str q0, [x2]
0x0000000000400b24 <+52>: ret

编辑 2:这是我目前获得的最多。

0x0000000000400ea0 <+0>:    ldr q1, [x1]
0x0000000000400ea4 <+4>: ldr q0, [x0,#16]
0x0000000000400ea8 <+8>: ldr q4, [x0]
0x0000000000400eac <+12>: ldr q3, [x0,#32]
0x0000000000400eb0 <+16>: fmul v0.4s, v0.4s, v1.4s
0x0000000000400eb4 <+20>: ldr q2, [x0,#48]
0x0000000000400eb8 <+24>: fmla v0.4s, v4.4s, v1.4s
0x0000000000400ebc <+28>: fmla v0.4s, v3.4s, v1.4s
0x0000000000400ec0 <+32>: fmla v0.4s, v2.4s, v1.4s
0x0000000000400ec4 <+36>: str q0, [x2]
0x0000000000400ec8 <+40>: ret

根据 perf,ldr 调用似乎仍然有很大的开销。

最佳答案

您直接在指针上操作(按引用调用)。如果你对指针进行操作,你应该意识到你完全受制于编译器。 ARM 的编译器并不是最好的。

可能有编译器选项可以处理这个问题,甚至编译器可以开箱即用地进行所需的优化,但最好的办法是手动进行:

  • 声明局部 vector (不带 &)
  • 将指针中的值加载到相应的 vector 中(最好是整个矩阵加上 vector )
  • 用 vector 做数学运算
  • 将 vector 存储到指针

上述过程也适用于非氖计算。编译器几乎总是会因(自动)内存操作的最轻微提示而严重瘫痪。

请记住,局部变量是您最好的 friend 。并且始终手动进行内存加载/存储。


编译器:Android clang 8.0.2 -o2

void transform(const float *matrix, const float *input, float *output)
{
const float32x4_t input_local = vld1q_f32(input);
const float32x4_t row0 = vld1q_f32(&matrix[0*4]);
const float32x4_t row1 = vld1q_f32(&matrix[1*4]);
const float32x4_t row2 = vld1q_f32(&matrix[2*4]);
const float32x4_t row3 = vld1q_f32(&matrix[3*4]);

float32x4_t rslt;
rslt = vmulq_f32(row0, input_local);
rslt = vmlaq_f32(rslt, row1, input_local);
rslt = vmlaq_f32(rslt, row2, input_local);
rslt = vmlaq_f32(rslt, row3, input_local);

vst1q_f32(output, rslt);
}

; void __fastcall transform(const float *matrix, const float *input, float *output)
EXPORT _Z9transformPKfS0_Pf
_Z9transformPKfS0_Pf
matrix = X0 ; const float *
input = X1 ; const float *
output = X2 ; float *
; __unwind {
LDR Q0, [input]
LDP Q1, Q2, [matrix]
LDP Q3, Q4, [matrix,#0x20]
FMUL V1.4S, V0.4S, V1.4S
FMUL V2.4S, V0.4S, V2.4S
FMUL V3.4S, V0.4S, V3.4S
FADD V1.4S, V1.4S, V2.4S
FADD V1.4S, V3.4S, V1.4S
FMUL V0.4S, V0.4S, V4.4S
FADD V0.4S, V0.4S, V1.4S
STR Q0, [output]
RET
; } // starts at 4

如您所见,Android clang 8.0.2 在 NEON 代码方面比之前的版本有了很大的改进。最后编译器生成加载多个寄存器的代码。我不明白为什么它不喜欢 FMLA

关于c++ - ARM neon 优化 - 摆脱多余的负载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54342915/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com