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

ARM NEON如何实现类似_mm512_mask_compress_epi的过滤压缩逻辑?

ARM NEON 下优化掩码过滤数组的实现

需求与标量实现

目标函数需保留数组中filter[i] != 0对应的nums[i]元素,剔除其余元素。标量实现代码如下:

static size_t filter_range(int* nums, const uint8_t* filter, size_t size) {
    size_t result_offset = 0;
    for (auto i = 0; i < size; i++) {
        if (filter[i]) {
            *(nums + result_offset) = *(nums + i);
            result_offset++;
        }
    }
    return result_offset;
}

x86 AVX512架构下,_mm512_mask_compress_epi指令可直接高效完成这类掩码压缩操作,但ARM NEON架构下能否用SIMD指令最大化该函数的性能?

现有NEON尝试实现

基于ARM位掩码优化思路,已实现filter_range2函数:批量处理16个filter元素,生成标记匹配位的掩码,用Kernighan算法遍历有效位完成元素拷贝,同时对全匹配/全不匹配场景做特殊优化。但该实现性能仍不及AVX512版本:

static size_t filter_range2(int* data, const uint8_t* filter, size_t size) {
    constexpr size_t data_type_size = sizeof(int);
    size_t start_offset = 0;
    size_t result_offset = 0;

#if defined(__ARM_NEON__) || defined(__aarch64__)
    constexpr size_t kBatchNums = 128 / (8 * sizeof(uint8_t));
    while (start_offset + kBatchNums < size) {
        uint8x16_t vfilter = vld1q_u8(filter);
        const uint16x8_t non_zero_mask = vreinterpretq_u16_u8(vmvnq_u8(vceqzq_u8(vfilter)));
        const uint8x8_t res = vshrn_n_u16(non_zero_mask, 4);
        // 每个字节对应4位全1,表示4个匹配的filter元素
        uint64_t matches = vget_lane_u64(vreinterpret_u64_u8(res), 0);

        if (matches == 0) {
            // 无匹配元素,跳过
        } else if (matches == 0xffff'ffff'ffff'ffffull) {
            // 全部匹配,批量拷贝
            memmove(data + result_offset, data + start_offset, kBatchNums * data_type_size);
            result_offset += kBatchNums;
        } else {
            matches &= 0x8888'8888'8888'8888ull;
            // *Kernighan算法*遍历所有匹配位
            for (; matches > 0; matches &= matches - 1) {
                uint32_t index = __builtin_ctzll(matches) >> 2;
                *(data + result_offset++) = *(data + start_offset + index);
            }
        }

        start_offset += kBatchNums;
        filter += kBatchNums;
    }
#endif

    // 处理剩余不足批量的元素
    for (auto i = start_offset; i < size; ++i) {
        if (filter[i]) {
            *(data + result_offset) = *(data + i);
            result_offset++;
        }
    }

    return result_offset;
}

更优的NEON优化方向

现有实现的瓶颈在于部分匹配场景下的逐个元素拷贝,可通过以下方式优化:

  • 利用AArch64 NEON掩码加载/存储指令:使用vld1q_s32_x系列掩码加载指令,直接批量加载匹配元素到向量,再一次性存储到结果位置,避免逐个拷贝的开销。
  • 预计算匹配元素索引:通过SIMD指令生成匹配元素的偏移量,用gather操作批量获取元素,减少标量循环次数。
  • 优化掩码生成逻辑:直接将filter向量转换为位掩码,而非4位聚合的字节,更高效地利用位操作遍历匹配位置。

内容的提问来源于stack exchange,提问作者Zihe Liu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:25:30