想象一下,我有这个输入数组:
uint16_t input[] = { 1, 2, 3, 4, 11, 22, 33, 44 };
如何从这个输入中获得四个带有以下内容的q向量:
uint16x8_t q1 = { 11, 11, 1, 1, 11, 11, 1, 1 };
uint16x8_t q2 = { 22, 22, 2, 2, 22, 22, 2, 2 };
uint16x8_t q3 = { 33, 33, 3, 3, 33, 33, 3, 3 };
uint16x8_t q4 = { 44, 44, 4, 4, 44, 44, 4, 4 };
最好使用霓虹内在函数?
Solution that I came up with似乎产生了最佳的arm64,但是armv7似乎并不是最佳的。什么可以改进或可能完全不同的方法是可能的?
注意,我的示例代码将q1,q2,q3输出到某个内存,在我的实际代码中,我需要先计算q1,q2,q3才能使用它们。
答案 0 :(得分:2)
你的功能过于复杂。
vcombine
,因为它主要导致多个
不必要的vmov
等价物。vtrn
而不是vzip/vuzp
,因为vtrn
要快得多。void foo(uint16_t *input, uint16_t *output)
{
uint16x8_t q0, q1, q2, q3;
uint16x4x2_t data1, data2, result1, result2;
data1 = vld2_u16(input);
data1.val[0] = vrev64_u16(data1.val[0]);
data1.val[1] = vrev64_u16(data1.val[1]);
data2 = data1;
result1 = vtrn_u16(data1.val[0],data2.val[0]);
result2 = vtrn_u16(data1.val[1],data2.val[1]);
vst1_u16(&output[0], result1.val[1]);
vst1_u16(&output[4], result1.val[1]);
vst1_u16(&output[8], result2.val[1]);
vst1_u16(&output[12], result2.val[1]);
vst1_u16(&output[16], result1.val[0]);
vst1_u16(&output[20], result1.val[0]);
vst1_u16(&output[24], result2.val[0]);
vst1_u16(&output[28], result2.val[0]);
}
上述情况应该比aarch32
和aarch64
手写集会:
aarch32
vld2.16 {d4, d6}, [input]!
vrev64.16 d4, d4
vrev64.16 d6, d6
vmov d5, d4
vmov d7, d6
vmov q0, q2
vmov q1, q3
vtrn.16 q2, q0
vtrn.16 q3, q1
vst1.16 {q0, q1}, [output]!
vst1.16 {q2, q3}, [output]!
aarch64
ld2 {v2.4h, v3.4h}, [input], #16
rev64 v2.4h, v2.4h
rev64 v3.4h, v3.4h
trn2 v0.4h, v2.4h, v2.4h
trn2 v1.4h, v3.4h, v3.4h
trn1 v2.4h, v2.4h, v2.4h
trn1 v3.4h, v3.4h, v3.4h
mov v0.d[1], v0.d[0]
mov v1.d[1], v1.d[0]
mov v2.d[1], v2.d[0]
mov v3.d[1], v3.d[0]
st1 {v0.8h-v3.8h}, [output], #64
必须执行vrev
更改。
以下是"廉价"使用vtbl
的建议,如果要在循环中执行例程,这是有意义的。
void foo(uint16_t *input, uint16_t *output)
{
uint8_t *pSrc = (uint8_t *) input;
uint8_t *pDst = (uint8_t *) output;
uint8x8x2_t data;
uint8x8_t d0, d1, d2, d3;
const uint8x8_t mask0 = {8, 9, 8, 9, 0, 1, 0, 1};
const uint8x8_t mask1 = {10, 11, 10, 11, 2, 3, 2, 3};
const uint8x8_t mask2 = {12, 13, 12, 13, 4, 5, 4, 5};
const uint8x8_t mask3 = {14, 15, 14, 15, 6, 7, 6, 7};
data.val[0] = vld1_u8(pSrc++);
data.val[1] = vld1_u8(pSrc++);
d0 = vtbl2_u8(data, mask0);
d1 = vtbl2_u8(data, mask1);
d2 = vtbl2_u8(data, mask2);
d3 = vtbl2_u8(data, mask3);
vst1_u8(pDst++, d0);
vst1_u8(pDst++, d0);
vst1_u8(pDst++, d1);
vst1_u8(pDst++, d1);
vst1_u8(pDst++, d2);
vst1_u8(pDst++, d2);
vst1_u8(pDst++, d3);
vst1_u8(pDst++, d3);
}
最后但并非最不重要的,可能是单次迭代的最快例程,在汇编时可以进一步提升。
void foo(uint16_t *input, uint16_t *output)
{
uint16x4x4_t data;
data = vld4_lane_u16(input++, data, 2);
data = vld4_lane_u16(input, data, 0);
data.val[0] = vsli_n_u32(data.val[0], data.val[0], 16);
data.val[1] = vsli_n_u32(data.val[1], data.val[1], 16);
data.val[2] = vsli_n_u32(data.val[2], data.val[2], 16);
data.val[3] = vsli_n_u32(data.val[3], data.val[3], 16);
vst1_u16(output++, data.val[0]);
vst1_u16(output++, data.val[0]);
vst1_u16(output++, data.val[1]);
vst1_u16(output++, data.val[1]);
vst1_u16(output++, data.val[2]);
vst1_u16(output++, data.val[2]);
vst1_u16(output++, data.val[3]);
vst1_u16(output, data.val[3]);
}
aarch32
vld4.16 {d2[2], d3[2], d4[2], d5[2]}, [input]!
vld4.16 {d2[0], d3[0], d4[0], d5[0]}, [input]
vsli.32 q1, q1, #16
vsli.32 q2, q2, #16
vmov q0, q1
vmov q3, q2
vswp d1, d2
vswp d6, d5
vst1.16 {q0, q1}, [output]!
vst1.16 {q2, q3}, [output]
aarch64
ld4 {v0.h, v1.h, v2.h, v3.h}[2], [input], #8
ld4 {v0.h, v1.h, v2.h, v3.h}[0], [input]
sli v0.2s, v0.2s, #16
sli v1.2s, v1.2s, #16
sli v2.2s, v2.2s, #16
sli v3.2s, v3.2s, #16
mov v0.d[1], v0.d[0]
mov v1.d[1], v1.d[0]
mov v2.d[1], v2.d[0]
mov v3.d[1], v3.d[0]
st1 {v0.8h-v3.8h}, [output]