我正在两个ARMv7处理器上测试AArch32模式的ARMv8霓虹灯代码: Cortex-A53和Cortex-A72。我使用的覆盆子Pi 3B和覆盆子Pi 4B板与32位Raspbian。
我的基准方法如下:
uint32_t x[4];
uint32_t t0 = ccnt_read();
for(int i = 0; i < 1000; i++)
armv7_neon(x);
uint32_t t1 = ccnt_read();
printf("%u\n",(t1-t0)/1000);
其中,armv7_neon函数由以下指令定义:
.global armv7_neon
.func armv7_neon, armv7_neon
.type armv7_neon, %function
armv7_neon:
vld1.32 {q0}, [r0]
vmvn.i32 q0, q0
vmov.i32 q8, #0x11111111
vshr.u32 q1, q0, #2
vshr.u32 q2, q0, #3
vmov.i32 q9, #0x20202020
vand q1, q1, q2
vmov.i32 q10, #0x40404040
vand q1, q1, q8
vmov.i32 q11, #0x80808080
veor q0, q0, q1
vmov.i32 q12, #0x02020202
vshl.u32 q1, q0, #5
vshl.u32 q2, q0, #1
vmov.i32 q13, #0x04040404
vand q1, q1, q2
vmov.i32 q14, #0x08080808
vand q3, q1, q9
vshl.u32 q1, q0, #5
vshl.u32 q2, q0, #4
veor q0, q0, q3
vand q1, q1, q2
vmov.i32 q15, #0x32323232
vand q1, q1, q10
vmov.i32 q8, #0x01010101
veor q0, q0, q1
vshl.u32 q1, q0, #2
vshl.u32 q2, q0, #1
vand q1, q1, q2
vand q3, q1, q11
vshr.u32 q1, q0, #2
vshl.u32 q2, q0, #1
veor q0, q0, q3
vand q1, q1, q2
vand q1, q1, q12
veor q0, q0, q1
vshr.u32 q1, q0, #5
vshl.u32 q2, q0, #1
vand q1, q1, q2
vand q3, q1, q13
vshr.u32 q1, q0, #1
vshr.u32 q2, q0, #2
veor q0, q0, q3
vand q1, q1, q2
vand q1, q1, q14
veor q0, q0, q1
vmvn.i32 q0, q0
vand q1, q0, q14
vand q2, q0, q15
vand q3, q0, q8
vand q8, q0, q11
vand q9, q0, q10
vand q10, q0, q13
vshl.u32 q1, q1, #1
vshl.u32 q2, q2, #2
vshl.u32 q3, q3, #5
vshr.u32 q8, q8, #6
vshr.u32 q9, q9, #4
vshr.u32 q10, q10, #2
vorr q0, q1, q2
vorr q1, q3, q8
vorr q2, q9, q10
vorr q3, q0, q1
vorr q0, q3, q2
vst1.32 {q0}, [r0]
bx lr
.endfunc
代码只使用以下选项编译:
gcc -O3 -mfpu=neon-fp-armv8 -mcpu=cortex-a53
gcc -O3 -mfpu=neon-fp-armv8 -mcpu=cortex-a72
我得到了74个和99个循环分别在皮质-A53和皮质-A72。我遇到这个博客讨论了tbl指令的Cortex-A72上的一些性能问题,但是我正在运行的代码中没有包含任何内容。
这个缺口从何而来?
发布于 2021-10-27 11:03:25
我比较了A72和A55的指令周期定时(A53上没有可用的):
vshl
和vshr
A72:吞吐量(IPC) 1,延迟3,仅在F1管道上执行
A55:吞吐量(IPC) 2,延迟2,在这两个管道上执行(尽管受到限制)
因为在您的代码中有很多这样的代码,所以它可以很好地改进它。
在程序集代码中也有一些缺点:
vadd
相比,vshl
具有更少的限制和更好的吞吐量/延迟。您应该将所有vshl
替换为立即1,代以vadd
。在SIMD上,桶形移位器的成本比算术要高。<<5
)vmvn
是不必要的。您可以将下面的所有vand
替换为vbic
。#include <arm_neon.h>
void armv7_neon(uint32_t * pData) {
const uint32x4_t cx11 = vdupq_n_u32(0x11111111);
const uint32x4_t cx20 = vdupq_n_u32(0x20202020);
const uint32x4_t cx40 = vdupq_n_u32(0x40404040);
const uint32x4_t cx80 = vdupq_n_u32(0x80808080);
const uint32x4_t cx02 = vdupq_n_u32(0x02020202);
const uint32x4_t cx04 = vdupq_n_u32(0x04040404);
const uint32x4_t cx08 = vdupq_n_u32(0x08080808);
const uint32x4_t cx32 = vdupq_n_u32(0x32323232);
const uint32x4_t cx01 = vdupq_n_u32(0x01010101);
uint32x4_t temp1, temp2, temp3, temp4, temp5, temp6;
uint32x4_t in = vld1q_u32(pData);
in = vmvnq_u32(in);
temp1 = (in >> 2) & (in >> 3);
temp1 &= cx11;
in ^= temp1;
temp1 = (in << 5) & (in + in);
temp1 &= cx20;
temp2 = (in << 5) & (in << 4);
temp2 &= cx40;
in ^= temp1;
in ^= temp2;
temp1 = (in << 2) & (in + in);
temp1 &= cx80;
temp2 = (in >> 2) & (in >> 1);
temp2 &= cx02;
in ^= temp1;
in ^= temp2;
temp1 = (in >> 5) & (in + in);
temp1 &= cx04;
temp2 = (in >> 1) & (in >> 2);
temp2 &= cx08;
in ^= temp1;
in ^= temp2;
temp1 = vbicq_u32(cx08, in);
temp2 = vbicq_u32(cx32, in);
temp3 = vbicq_u32(cx01, in);
temp4 = vbicq_u32(cx80, in);
temp5 = vbicq_u32(cx40, in);
temp6 = vbicq_u32(cx04, in);
temp1 += temp1;
temp2 <<= 2;
temp3 <<= 5;
temp4 >>= 6;
temp5 >>= 4;
temp6 >>= 2;
temp1 |= temp2 | temp3 | temp4 | temp5 | temp6;
vst1q_u32(pData, temp1);
}
您可以看到,-mcpu
选项在这里产生了明显的不同。
但是GCC从不失望:它拒绝使用vbic
,即使我明确命令它(对于Clang也是如此)。我讨厌他们俩)
我会使用反汇编,删除第二个vmvn
,并将附加的所有vand
替换为vbic
,以获得最佳性能。
请记住,在程序集中编写代码并不会自动使代码运行得更快,更新的体系结构不一定会带来更好的ICT:在ICT方面,A72很大程度上低于A53。
PS:使用-mcpu=cortex-a53
选项,生成的代码与A55的代码相同。我们可以假设A55只是armv8.2
ISA对A53的一个扩展。
https://stackoverflow.com/questions/69719403
复制相似问题