You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ARM Neon上实现按位异或前缀奇偶性?含实现与fallback方案

按位异或前缀奇偶性函数的ARM Neon移植优化

背景

该函数的作用是为输入整数的每个比特位,计算其右侧所有比特位的奇偶性(异或累加结果)。以下是参考用的朴素O(n)实现:

template <unsigned_integral T>
constexpr T bitwise_exclusive_prefix_parity_naive(T x)
{
    constexpr int N = std::numeric_limits<T>::digits;

    T result = 0;
    bool parity = false;
    for (int i = 0; i < N; ++i) {
        result |= static_cast<T>(parity) << i;
        parity ^= (x >> i) & 1;
    }

    return result;
}

static_assert(bitwise_exclusive_prefix_parity_naive(0b0000'0000u) == 0b0000'0000);
static_assert(bitwise_exclusive_prefix_parity_naive(0b1111'1111u) == 0b1010'1010);
static_assert(bitwise_exclusive_prefix_parity_naive(0b1001'0000u) == 0b1110'0000);
static_assert(bitwise_exclusive_prefix_parity_naive(0b0100'1000u) == 0b0111'0000);

通过无进位乘法(CLMUL)可将复杂度优化到O(1),核心逻辑等价于计算CLMUL(x, -2)。目前已完成x86平台的实现,现需添加ARM Neon(禁用SVE2)的高效实现,相关代码进展如下:

#include <x86intrin.h> // only on x86
#include <arm_neon.h>  // only on ARM

template <std::unsigned_integral T>
constexpr T bitwise_exclusive_prefix_parity(T x)
{
    constexpr int N = std::numeric_limits<T>::digits;

#ifdef __PCLMUL__ // x86
    if (!consteval) {
        if constexpr (N <= 64) {
            const __m128i x_128 = _mm_set_epi64x(0, x);
            const __m128i neg2_128 = _mm_set_epi64x(0, -2);
            const __m128i result_128 = _mm_clmulepi64_si128(x_128, neg2_128, 0);
            return _mm_extract_epi64(result_128, 0) & T(-1);
        }
    }
#endif
    x <<= 1;
    for (int i = 1; i < N; i <<= 1) {
        x ^= x << i;
    }
    return x;
}

问题与解答

1. 如何使用vmull_p64实现该功能?是否需要运行时检查?

vmull_p64是ARM Neon的64位多项式无进位乘法指令,和x86的CLMUL逻辑完全一致,只需计算x与-2的无进位乘积,取低64位即可得到结果。

实现代码

在原有模板函数中添加ARM Neon分支:

#ifdef __ARM_FEATURE_CRYPTO // ARMv8+ Crypto扩展,包含vmull_p64
    if (!consteval) {
        if constexpr (N <= 64) {
            uint64x1_t x_neon = vcreate_u64(x);
            uint64x1_t neg2_neon = vcreate_u64(-2ULL);
            // 执行无进位多项式乘法,得到128位结果
            uint128x1_t result_neon = vmull_p64(x_neon, neg2_neon);
            // 提取低64位并转换为目标类型
            return static_cast<T>(vgetq_lane_u64(result_neon, 0));
        }
    }
#endif

关于运行时检查

  • 如果目标平台仅针对ARMv8-A及以上架构,无需运行时检查:vmull_p64是ARMv8 Crypto扩展的标配指令,支持该架构的设备通常都包含此扩展。
  • 如果需要兼容ARMv7等旧架构,则需要运行时检测:可通过Linux平台的getauxval(AT_HWCAP)检查HWCAP_NEON_CRYPTO标志,或读取系统寄存器判断是否支持多项式乘法指令。

2. 若需要运行时检查,不使用SVE2的情况下,最优的fallback实现是什么?

当前代码中已实现的分治法就是最优的fallback方案,时间复杂度为O(log n),是位并行的高效实现:

x <<= 1;
for (int i = 1; i < N; i <<= 1) {
    x ^= x << i;
}
return x;

该逻辑通过逐步将高位的奇偶性信息扩散到低位,比朴素循环效率高得多,同时支持constexpr编译期计算,完全满足可移植性要求。

内容的提问来源于stack exchange,提问作者Jan Schultke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:47:32