在uint8x8_t neon寄存器

时间:2018-04-06 01:23:44

标签: arm neon

考虑这段代码:

uint8_t v[8] = { ... };
int ret = 256;
int ret_pos = -1;
for (int i=0; i<8; ++i)
{
    if (v[i] < ret)
    {
        ret = v[i];
        ret_pos = i;
    }
}

它找到min元素的最小值和位置(retret_pos)。在arm neon我可以使用pairwise min在v中找到min元素,但是如何找到min元素的位置?

更新:看看我自己的答案,你有什么建议改进它?

2 个答案:

答案 0 :(得分:1)

成对min将允许您在2个向量之间进行比较,以找到每个对应单词之间的最小值。例如,如果您的8个数据点(可能需要更多的矢量化代码)被分成2个向量,您可以使用成对最小值来查找4对之间的比较的最小值。

然后,您可以继续将数据拆分为较小的矢量对,或者在4个条目的新矢量上连续迭代以找到最小值。记下找到它的向量的位置,检查原始向量中的相同位置将产生最小值的位置。或者,您也可以使用矢量比较来查找此值。

答案 1 :(得分:0)

这是我花了一些时间摆弄比特和数学后的表现:

#define VMIN8(x, index, value)                               \
do {                                                         \
    uint8x8_t m = vpmin_u8(x, x);                            \
    m = vpmin_u8(m, m);                                      \
    m = vpmin_u8(m, m);                                      \
    uint8x8_t r = vceq_u8(x, m);                             \
                                                             \
    uint8x8_t z = vand_u8(vmask, r);                         \
                                                             \
    z = vpadd_u8(z, z);                                      \
    z = vpadd_u8(z, z);                                      \
    z = vpadd_u8(z, z);                                      \
                                                             \
    unsigned u32 = vget_lane_u32(vreinterpret_u32_u8(z), 0); \
    index = __lzcnt(u32);                                    \
    value = vget_lane_u8(m, 0);                              \
} while (0)


uint8_t v[8] = { ... };

static const uint8_t mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
uint8x8_t vmask = vld1_u8(mask);

uint8x8_t v8 = vld1_u8(v);
int ret;
int ret_pos;
VMIN8(v8, ret_pos, ret);

其中__lzcnt为clz(gcc中为__builtin_clz)。

以下是它的工作原理。首先使用pairwise min将uint8x8_t的所有u8字段设置为最小值:

    uint8x8_t m = vpmin_u8(x, x);
    m = vpmin_u8(m, m);
    m = vpmin_u8(m, m);

然后使用向量比较将min元素设置为所有1,将所有其他元素设置为零:

    uint8x8_t r = vceq_u8(x, m);

然后使用包含值的掩码执行逻辑AND:uint8_t mask[] {1<<7, 1<<6, 1<<5, ... 1<<1, 1<<0 };

uint8x8_t z = vand_u8(vmask, r);

然后使用pairwise add添加所有8个字节的

z = vpadd_u8(z, z);
z = vpadd_u8(z, z);
z = vpadd_u8(z, z);

然后使用clz计算第一个min元素的位置。

unsigned u32 = vget_lane_u32(vreinterpret_u32_u8(z), 0);
index = __lzcnt(u32);

然后,在实际代码中,每次循环迭代和编译器is able to perfectly interleave multiple VMIN8 calls多次使用VMIN8以避免数据停顿。