c++ - 从 SIMD vector 中提取集合字节位置

标签 c++ sse simd intrinsics

我使用 SIMD 指令运行一系列计算。这些指令返回一个 16 字节的 vector 作为结果,名为 compare , 每个字节都是 0x000xff :

             0    1    2    3    4    5    6    7       15   16
compare : 0x00 0x00 0x00 0x00 0xff 0x00 0x00 0x00 ... 0xff 0x00

字节设置为 0xff意思是我需要运行函数 do_operation(i) i 是字节的位置

比如上面的compare vector 意味着,我需要运行这个操作序列:

do_operation(4);
do_operation(15);

这是迄今为止我想到的最快的解决方案:

for(...) {
        //
        // SIMD computations
        //
        __m128i compare = ... // Result of SIMD computations

        // Extract high and low quadwords for compare vector
        std::uint64_t cmp_low = (_mm_cvtsi128_si64(compare));
        std::uint64_t cmp_high = (_mm_extract_epi64(compare, 1));

        //  Process low quadword 
        if (cmp_low) {
            const std::uint64_t low_possible_positions = 0x0706050403020100;
            const std::uint64_t match_positions = _pext_u64(
                    low_possible_positions, cmp_low);
            const int match_count = _popcnt64(cmp_low) / 8;
            const std::uint8_t* match_pos_array =
                    reinterpret_cast<const std::uint8_t*>(&match_positions);

            for (int i = 0; i < match_count; ++i) {
                do_operation(i);
            }
        }

        // Process high quadword (similarly)
        if (cmp_high) { 

            const std::uint64_t high_possible_positions = 0x0f0e0d0c0b0a0908;
            const std::uint64_t match_positions = _pext_u64(
                    high_possible_positions, cmp_high);
            const int match_count = _popcnt64(cmp_high) / 8;
            const std::uint8_t* match_pos_array =
                    reinterpret_cast<const std::uint8_t*>(&match_positions);

            for(int i = 0; i < match_count; ++i) {
                do_operation(i);
            }
        }
}

我首先提取 128 位 vector (cmp_lowcmp_high)的第一个和第二个 64 位整数。然后我使用 popcount计算设置为 0xff 的字节数(设置为 1 的位数除以 8)。最后,我使用 pext得到没有零的位置,像这样:

0x0706050403020100
0x000000ff00ff0000
        |
      PEXT
        |
0x0000000000000402

我想找到一个更快的解决方案提取设置为0xff的字节的位置compare vector 。更准确地说,通常只有 0、1 或 2 个字节设置为 0xffcompare vector ,我想使用此信息来避免一些分支。

最佳答案

以下是如何减少测试数量的简要概述:

  • 首先使用函数将 128 位整数的每个字节的所有 lsb 或 msb 投影到 16 位值(例如,在 X86 cpus 上有一个 SSE2 汇编指令: pmovmskb,Intel 和 MS 编译器支持 _mm_movemask_pi8 内在函数,gcc 也有内在函数:__builtin_ia32_ppmovmskb128, );

  • 然后将该值分成 4 个半字节;

  • 定义函数来处理半字节的每个可能值(从 0 到 15)并将它们放入一个数组中;

  • 最后调用每个半字节索引的函数(有额外的参数来指示它是16位中的哪个半字节)。

关于c++ - 从 SIMD vector 中提取集合字节位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28506500/

相关文章:

c++ - double 型上的 SSE vector 运算

assembly - 首次使用 AVX 256 位向量会减慢 128 位向量和 AVX 标量操作

loops - 从 Haskell 生成矢量代码?

c++ - 两个8位数组协方差的快速实现

c++ - std::vector<> 使用不初始化基元和转发 construct_back 的 resize() 派生

c++ - 带有函数指针的数组构造函数?

c++ - 在 C++ 中限制 FPS

c++ - QStackedLayout 隐式显示添加的小部件

c - xmm0 的函数参数

c++ - 使用 union 将相同的内存分配给类成员变量