image width = 4000
image height = 2000
number of iterations = width * height / 64 = 125 000
asm volatile(
"1: \n\t"
"prfm pldl1keep, [%[src], #128] \n\t"
"LD4 {v0.16B, v1.16B, v2.16B,v3.16B}, [%[src]], #64 \n\t" //5 cycles
"MOVI v10.16B, #12 \n\t" //1 cycle
"AND v4.16B, v0.16B, v10.16B \n\t" //1 cycle
"AND v5.16B, v1.16B, v10.16B \n\t" //1 cycle
"AND v6.16B, v2.16B, v10.16B \n\t" //1 cycle
"AND v7.16B, v3.16B, v10.16B \n\t" //1 cycle
"MOVI v11.16B, #20 \n\t" //1 cycle
"SUB v8.16B, v4.16B, v11.16B \n\t" //1 cycle
"SUB v9.16B, v5.16B, v11.16B \n\t" //1 cycle
"SUB v10.16B, v6.16B, v11.16B \n\t" //1 cycle
"SUB v11.16B, v7.16B, v11.16B \n\t" //1 cycle
"ST4 {v8.16B, v9.16B, v10.16B,v11.16B}, [%[dst]], #64 \n\t" //5 cycles
"subs %[simd_it], %[simd_it], #1 \n\t" //1 cycle
"bne 1b \n\t" //4 cycles
每次迭代约25个时钟周期 125 000 * 25 = 3 125 000个周期/图像。
我在ARM NEON内联汇编中实现了示例代码(代码没有意义).每幅图像有(大约,我使用ARMv7的信息,这是ARMv8,但我不希望这个数字会高得多)3,1M时钟周期。
我使用的是频率为1Ghz的处理器。如果处理器每秒有1G的时钟周期,那么它应该在不到4ms的时间内完成3,1M个周期。但我测量的时间是14ms左右。
为什么不匹配? 同一核心上没有其他进程运行。
有条件分支,每一次迭代(bne),都会导致该管道需要重新填充。如果有条件的跳过将被删除 而取而代之的,将是写好的千行长代码。会不会快3-4倍? 谅谅
ld4
和 st4
的算法。v10
和 v11
循环外。subs
接连 b.ne
.v8
到 v15
因为它们必须按照 aapcs
. movi v4.16b, #12
movi v5.16b, #20
.balign 64
1:
ld1 {v0.16b-v3.16b}, [src], #64
subs simd_it, simd_it, #1
and v0.16b, v0.16b, v4.16b
and v1.16b, v1.16b, v4.16b
and v2.16b, v2.16b, v4.16b
and v3.16b, v3.16b, v4.16b
sub v0.16b, v0.16b, v5.16b
sub v1.16b, v1.16b, v5.16b
sub v2.16b, v2.16b, v5.16b
sub v3.16b, v3.16b, v5.16b
st1 {v0.16b-v3.16b}, [dst], #64
b.gt 1b
上面的代码每次迭代消耗16个周期,理论上说
125,000 * 16 = 2,000,000 = 2ms @ 1Ghz。
我想你测量的时间应该是12ms左右,因为我感觉你要么没有启用缓存,要么是在非缓存区读写。
每次迭代大约损失80个周期,对于4个寄存器的缓存漏掉惩罚来说,这看起来很合理。