使用内在函数测试 128 位 NEON 寄存器的值为 0 的最快方法?
Posted
技术标签:
【中文标题】使用内在函数测试 128 位 NEON 寄存器的值为 0 的最快方法?【英文标题】:Fastest way to test a 128 bit NEON register for a value of 0 using intrinsics? 【发布时间】:2013-03-13 15:29:10 【问题描述】:我正在寻找最快的方法来测试 128 NEON 寄存器是否包含全零,使用 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 ...
有人知道更快的方法吗?
【问题讨论】:
【参考方案1】:虽然这个答案可能有点晚了,但有一种简单的方法可以进行测试,只需 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 寄存器中的任何位,则返回值将非零。
【讨论】:
【参考方案2】:如果您以 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];
【讨论】:
【参考方案3】:您似乎正在寻找内在函数,这就是方法:
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_t0, 4, 8, 12
)
)[0];
Nils Pipenbrinck 的回答有一个缺陷,即他假设 QC、累积饱和标志是明确的。
【讨论】:
【参考方案4】:如果你有 AArch64,你可以更容易地做到这一点。他们为此设计了一条新指令。
inline uint32_t is_not_zero(uint32x4_t v)
return vaddvq_u32(v);
【讨论】:
不过,这似乎容易溢出。 如果您使用完整的 32 位,是的,它可能会环绕并导致误报,但如果您知道您的数字小于 2³²/4,它是安全的。 虽然使用 vmaxvq_u32 可能会更好。类似的归约指令,但返回最大值而不是总和。【参考方案5】:我会避免函数返回只应被解释为 bool 的整数值。例如,更好的方法是定义一个辅助函数来返回 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() 的别名。
【讨论】:
以上是关于使用内在函数测试 128 位 NEON 寄存器的值为 0 的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章
NEON:将 uint8_t 数组加载到 128 位寄存器中
ARM NEON 内部函数将 D(64 位)寄存器转换为 Q(128 位)寄存器的低半部分,而上半部分未定义