【问题标题】:Efficiently compute two dissimilar numbers in arm neon在 arm neon 中有效地计算两个不同的数字
【发布时间】:2018-07-03 00:08:25
【问题描述】:

我有一个由 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

【问题讨论】:

  • 所以你正在做 4 字节的 SAD(绝对差之和),并寻找具有最大 SAD 的对?
  • 是的,类似的。我想我在 ffmpeg 或 x264 的某个地方看到过,我会尝试 grep 看看它们是否有类似的东西。
  • 是的,x264 应该有一些 4x4 SAD 运动搜索,包括详尽的搜索。但它将分别查看不同的颜色平面,用于 4x4 像素的块。因此,运动搜索将检查每个字节偏移量,而不仅仅是 4 个字节的步长。但是,如果您可以将其与混洗/未对齐负载分开,您可能会了解一些有用的指令,用于使用 NEON 进行字节 SAD。
  • x264 有多种搜索模式可以快速寻找好的匹配,不一定是最小 SAD:dia(对角线)、hex(六边形)、umh(不均匀的多六边形) , 和esa(详尽无遗,但不是对每个对齐方式的强力检查)。 tesa 找到最小成本 DCT 转换残差,而不是最小 SAD。另请注意,x264 将具有 8x8 和 16x16 SAD,用于在更大的块上进行运动搜索。
  • 你是如何使用结果的?您是否想要多个 16 元素缓冲区的结果 SIMD 向量,还是一次只用于一个 16 字节向量? (不确定这是否会产生影响,但很容易。并且可能存在吞吐量与延迟之间的权衡。)为了确认,您只想要数字本身,而不是位置?

标签: c++ arm intrinsics neon


【解决方案1】:

diss 在 neon 中计算很简单,它可能会以这种方式实现(未经测试的代码):

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 寄存器 {q0, q1, q2, q3} 中加载所有 16 个整数。 - 第一个 q0 reg 包含 { buf[0], buf[1], buf[2], buf[3] } - 然后我可以循环 15 次并从 4 个输入 q regs 中提取到 qext 值,例如 vextq_u32(q0, q1, 1) 用于第一次迭代等。 - 计算 q0 和 qext 之间的最小值。

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

也许如果我按字节对{q0, q1, q2, q3} 进行解交错,它可能会得到更好的优化。

【讨论】:

  • 那么NEON有SAD指令吗?如果您想将此作为答案发布,您至少应该说出它是什么。你不能只做字节元素减法并取带符号的绝对值,因为这只适用于 128 或更小的绝对无符号差异。
  • @PeterCordes 不确定您的意思。相异性计算在这里不相关(我更新了示例代码),findDissimilarity 在霓虹灯中很复杂,我描述了可能的解决方案。虽然没有尝试实现它
  • 你说这是微不足道的,所以我认为必须有一些直接的硬件支持。无论如何,是的 vabdq_u8 给你绝对的差异;这就是我想知道的。函数的其余部分(2 次乘法和 3 次加法)看起来并不便宜,显然它需要你一些工作才能想出,所以我很难说它是微不足道的。
  • vabal_u8(绝对差异和累积)有用吗?它显然水平累积成一个uint16x8_t 向量。
  • 我一开始不会在 ARMv7 上使用 NEON,甚至不会在 ARMv6 上使用。 usad8 是 ARMv8 上没有的一条强大指令。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-26
  • 2013-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多