考虑以下代码,第一个代码段:
void run_new(const float* src, float* dst,
size_t IH, size_t IW, size_t OH, size_t OW,
size_t N) {
rep(n, N) {
const float* src_ptr = src + IW * IH * n;
float* outptr = dst;
const float* r0 = src_ptr;
const float* r1 = src_ptr + IW;
float32x4_t k0123 = vdupq_n_f32(3.f);
rep(h, OH) {
size_t width = OW >> 2;
asm volatile(
"dup v21.4s, %4.s[0] \n"
"dup v22.4s, %4.s[1] \n"
"dup v23.4s, %4.s[2] \n"
"dup v24.4s, %4.s[3] \n"
"mov x3, xzr \n"
"0: \n"
"ldr q0, [%1] \n"
"ld1 {v1.4s, v2.4s}, [%2], #32 \n"
"add x3, x3, #0x1 \n"
"cmp %0, x3 \n"
"ld1 {v3.4s, v4.4s}, [%3], #32 \n"
"fmla v0.4s, v1.4s, v21.4s \n" // src[i] * k[i]
"fmla v0.4s, v2.4s, v22.4s \n"
"fmla v0.4s, v3.4s, v23.4s \n"
"fmla v0.4s, v4.4s, v24.4s \n"
"str q0, [%1], #16 \n"
"bne 0b \n"
: "+r"(width), "+r"(outptr), "+r"(r0), "+r"(r1)
: "w"(k0123)
: "cc", "memory", "x3", "v0", "v1", "v2", "v3", "v4", "v21", "v22", "v23", "v24");
}
}
}
第二个代码段:
void run_origin(const float* src, float* dst,
size_t IH, size_t IW, size_t OH, size_t OW,
size_t N) {
rep(n, N) {
const float* src_ptr = src + IW * IH * n;
float* outptr = dst;
const float* r0 = src_ptr;
const float* r1 = src_ptr + IW;
float32x4_t k0123 = vdupq_n_f32(3.f);
rep(h, OH) {
size_t width = OW >> 2;
asm volatile(
"dup v21.4s, %4.s[0] \n"
"dup v22.4s, %4.s[1] \n"
"dup v23.4s, %4.s[2] \n"
"dup v24.4s, %4.s[3] \n"
"mov x3, xzr \n"
"mov x4, xzr \n"
"0: \n"
"add x19, %2, x4 \n"
"ldr q0, [%1] \n" // load dst 0, 1, 2, 3
"ld1 {v1.4s, v2.4s}, [x19]\n" // 1, 2, 4, 6
"add x3, x3, #0x1 \n"
"cmp %0, x3 \n"
"add x19, %3, x4 \n"
"ld1 {v3.4s, v4.4s}, [x19]\n"
"fmla v0.4s, v1.4s, v21.4s \n" // src[i] * k[i]
"fmla v0.4s, v2.4s, v22.4s \n"
"fmla v0.4s, v3.4s, v23.4s \n"
"fmla v0.4s, v4.4s, v24.4s \n"
"add x4, x4, #0x20 \n"
"str q0, [%1], #16 \n"
"bne 0b \n"
"add %2, %2, x4 \n"
"add %3, %3, x4 \n"
: "+r"(width), "+r"(outptr), "+r"(r0), "+r"(r1)
: "w"(k0123)
: "cc", "memory", "x3", "x4", "x19", "v0", "v1", "v2", "v3", "v4", "v21", "v22", "v23", "v24");
}
}
}
Test performance of arm neon assembly
中的所有代码我在xiaomi5s
,xiaomi6
,redmi
上测试了这两个代码的效果,效果的细节是:
N:12 IH:224 IW:224 OH:112 OW:112
xiaomi5s中的以下测试。
N:12 IH:48-256 IW:224
N:12 IH:224 IW:48-256
IC:2-12 IH:224 IW:224
我对xiaomi5s
中的性能测试感到困惑,为什么xiaomi5上第一个代码的性能如此糟糕。
我想这可能是因为如果等待正常寄存器(如ld1 {v3.4s, v4.4s}, [x19]
等待由x19
add x19, %3, x4
计算的clang++ -std=c++11 -Ofast
,霓虹灯管道就会被打破,但我是不太确定。
补充细节:
编译选项(clang版本:5.0.0):ldr q0, [%2]
。
ld1 v0.4s, [%2]
更改为run_origin
,但结果相同,fmla v0.4s, v1.4s, v21.4s
的效果可能会快一点,约为1%-3%。N:12 IH:224 IW:224 OH:112 OW:112
perf来源:342.96631 mflops --- asm:4288.51646 mflops ---加速:12.50419
smlsl2 v0.2d, v1.4s, v21.4s
更改为fmla v0.4s, v1.4s, v21.4s
,但结果相同。
N:12 IH:224 IW:224 OH:112 OW:112
性能起源:348.03699 mflops --- asm:4245.18804 mflops ---加速:12.19752
fadd v0.4s, v1.4s, v21.4s
更改为{{1}},原始代码变得更快。N:12 IH:224 IW:224 OH:112 OW:112
perf来源:743.95433 mflops --- asm:4756.65769 mflops ---加速:6.39375
答案 0 :(得分:0)
一个疯狂的猜测是,瓶颈与内核/缓存子系统中的核心一样可能。也许第一种情况会阻止自动预加载(或者xiaomi5缺少此功能或禁用它)?
尝试添加pld
(或更确切地说prfm
)指令可能会很有趣,尽管我从未发现它们至少对Cortex-A9有很多帮助。
检查fmla
是否是瓶颈的简单方法是注释掉部分或全部数据处理指令(当然,输出会出错!)
答案 1 :(得分:-1)
我仍然不像NEON32那样熟悉NEON64,但我的代码中有几件事我不会做:
为什么使用VFP指令" ldr"?在VFP和NEON之间切换可能会花费大量周期,特别是如果这些指令是存储器访问的指令。两者共享寄存器并不意味着它们是相同的单元。将其更改为LD1 ...... 4s
你想要32位还是64位?选择x3或w3,然后坚持下去。
您确定要与fmla融合乘法吗?也许是或者可能没有,但请注意融合倍数会花费更多......
欢呼声