首页 > 解决方案 > 在 arm neon 中有效地计算两个不同的数字

问题描述

我有一个由 16 个整数组成的数组,我想从这个数组中找到一对彼此之间具有最大差异的整数。可以用这个(伪)代码计算差异:

int diss(uint32_t x, uint32_t y)
{   // it could do square for each byte of the number instead.
    return
    abs(((x >> 24) & 0xFF) - ((y >> 24) & 0xFF)) + 
    abs(((x >> 16) & 0xFF) - ((y >> 16) & 0xFF)) + 
    abs(((x >>  8) & 0xFF) - ((y >>  8) & 0xFF)) + 
    abs(((x >>  0) & 0xFF) - ((y >>  0) & 0xFF));
}

void findDissimilar(uint32_t buf[16], uint32_t& x, uint32_t& y)
{
    int maxDiss = 0;
    for (int i=0; i<16; ++i)
    {
        for (int j=0; j<16; ++j)
        {
            int d = diss(buf[i], bud[j]);
            if (d > maxDiss)
            {
                maxDiss = d;
                x = buf[i];
                y = buf[j];
            }
        }
    }
}

如果重要的话,输入buf已经在霓虹灯寄存器中。在输出时,我应该得到两个整数(在霓虹灯 reg 中可能会更好)。我怎样才能在手臂霓虹灯中有效地做到这一点,我应该尝试什么方法?只是为了澄清,问题的重点是关于优化findDissimilar

标签: c++armintrinsicsneon

解决方案


diss在霓虹灯中计算是微不足道的,它可能以这种方式实现(未经测试的代码):

uint32x4_t diss_x4(uint32x4_t x4, uint32x4_t y4)
{
    uint8x16_t diff = vabdq_u8(vreinterpretq_u8_u32(x4), vreinterpretq_u8_u32(x4));
    uint16x8_t m0 = vmull_u8(vget_low_u8(diff), vget_low_u8(diff));
    uint16x8_t m1 = vmull_u8(vget_high_u8(diff), vget_high_u8(diff));
    uint16x4_t s0 = vpadd_u16(vget_low_u8(m0), vget_high_u8(m0));
    uint16x4_t s1 = vpadd_u16(vget_low_u8(m1), vget_high_u8(m1));
    uint16x4_t sx = vpadd_u16(s0, s1);
    return vmovl_u16(sx);
}

但不是那么琐碎地findDissimilar。我认为最好的方法是执行以下操作: - 在 4 个 q-registers 中加载所有 16 个整数{q0, q1, q2, q3}。- 第一个 q0 reg 包含{ buf[0], buf[1], buf[2], buf[3] } - 然后我可以循环 15 次并从 4 个输入 q reg 中提取到 qext 值,例如 vextq_u32(q0, q1, 1) 用于第一次迭代等等。- 计算 q0 和 qext 之间的最小值。

那么应该对 q1、q2、q3 执行相同的过程。

也许如果我按字节去交错{q0, q1, q2, q3}​​,它可以得到更好的优化。


推荐阅读