使用内在函数测试 128 位 NEON 寄存器的值是否为 0 的最快方法?

问题描述 投票:0回答:6

我正在寻找最快的方法来使用 NEON 内在函数测试 128 NEON 寄存器是否包含全零。 我目前正在使用 3 个 OR 运算和 2 个 MOV:

uint32x4_t vr = vorrq_u32(vcmp0, vcmp1);

uint64x2_t v0 = vreinterpretq_u64_u32(vr);
uint64x1_t v0or = vorr_u64(vget_high_u64(v0), vget_low_u64(v0));

uint32x2_t v1 = vreinterpret_u32_u64 (v0or);
uint32_t r = vget_lane_u32(v1, 0) | vget_lane_u32(v1, 1);

if (r == 0) { // do stuff }

gcc 将其翻译为以下汇编代码:

VORR     q9, q9, q10
VORR     d16, d18, d19
VMOV.32  r3, d16[0]
VMOV.32  r2, d16[1]
VORRS    r2, r2, r3
BEQ      ...

有人知道更快的方法吗?

neon
6个回答
8
投票

虽然这个答案可能有点晚了,但有一个简单的方法可以只用 3 条指令并且不需要额外的寄存器来进行测试:

inline uint32_t is_not_zero(uint32x4_t v)
{
    uint32x2_t tmp = vorr_u32(vget_low_u32(v), vget_high_u32(v));
    return vget_lane_u32(vpmax_u32(tmp, tmp), 0);
}

如果 128 位 NEON 寄存器中的任何位被设置,则返回值将非零。


3
投票

如果您的目标是 AArch64 NEON,则只需两条指令即可使用以下命令来获取要测试的值:

inline uint64_t is_not_zero(uint32x4_t v)
{
    uint64x2_t v64 = vreinterpretq_u64_u32(v);
    uint32x2_t v32 = vqmovn_u64(v64);
    uint64x1_t result = vreinterpret_u64_u32(v32);
    return result[0];
}

1
投票

您似乎正在寻找内在函数,这就是方法:

inline bool is_zero(int32x4_t v) noexcept
{
  v = v == int32x4{};

  return !int32x2_t(
    vtbl2_s8(
      int8x8x2_t{
        int8x8_t(vget_low_s32(v)),
        int8x8_t(vget_high_s32(v))
      },
      int8x8_t{0, 4, 8, 12}
    )
  )[0];
}

Nils Pipenbrinck 的答案有一个缺陷,因为他假设 QC、累积饱和标志是明确的。


1
投票

如果您有 AArch64,您可以更轻松地做到这一点。他们有为此设计的新说明。

inline uint32_t is_not_zero(uint32x4_t v)
{
    return vaddvq_u32(v);
}

1
投票

我会避免函数返回只能解释为布尔值的整数值。例如,更好的方法是定义一个辅助函数来返回 4 个通道的最大无符号值:

inline uint32_t max_lane_value_u32(const uint32x4_t& v)
{
#if defined(_WIN32) && defined(_ARM64_)
    // Windows 64-bit
    return neon_umaxvq32(v);
#elif defined(__LP64__)
    // Linux/Android 64-bit
    return vmaxvq_u32(v);
#else
    // Windows/Linux/Android 32-bit
    uint32x2_t result = vmax_u32(vget_low_u32(v), vget_high_u32(v));
    return vget_lane_u32(vpmax_u32(result, result), 0);
#endif
}

然后您可以使用:

if (0 == max_lane_value_u32(v))
{
    ...
}

在您的代码中,这样的函数在其他地方也可能有用。或者,您可以使用完全相同的代码编写 is_not_zero() 函数,但最好的形式是返回 bool

请注意,您需要定义辅助函数的唯一原因是 vmaxvq_u32() 在 32 位上不可用,并且在 Windows 上可能无法从 arm64_neon.h 中的 neon_umaxvq32() 起别名。


0
投票

aarch64 的正确答案是抄袭 Allan Jensen 的响应,但放入正确的内在。我确信这就是他想说的。

AJ: If you have AArch64 you can do it even easier. They have a new instruction for designed for this.

inline uint32_t is_not_zero(uint32x4_t v)
{
    return vmaxvq_u32(v);   //IRO: max not add
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.