高效拆开包装,重新洗牌8条短裤

时间:2018-04-17 10:24:38

标签: c++ arm intrinsics neon

想象一下,我有这个输入数组:

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才能使用它们。

1 个答案:

答案 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]);
}

上述情况应该比aarch32aarch64

上的速度快得多

手写集会:

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]