sse - 将分散索引转换为聚集索引的有效方法?

标签 sse simd vectorization altivec stream-compaction

我正在尝试使用 SIMD 内在函数编写流压缩(获取一个数组并删除空元素)。循环的每次迭代一次处理 8 个元素(SIMD 宽度)。

通过 SSE 内在函数,我可以使用 _mm_shuffle_epi8() 相当有效地完成此操作,它执行 16 个条目的表查找(以并行计算术语收集)。随机索引是预先计算的,并使用位掩码进行查找。

for (i = 0; i < n; i += 8)
{
  v8n_Data = _mm_load_si128(&data[i]);
  mask = _mm_movemask_epi8(&is_valid[i]) & 0xff;     // is_valid is byte array
  v8n_Compacted = _mm_shuffle_epi8(v16n_ShuffleIndices[mask]);
  _mm_storeu_si128(&compacted[count], v8n_Compacted);

  count += bitCount[mask];
}

我现在的问题是我也想为 Altivec SIMD 实现这个(不要问为什么 - 误导的业务决策)。 Altivec 没有 _mm_movemask_epi8() 的等效项,这是一个关键成分。所以,我需要找到一种方法

  1. 模拟 _mm_movemask_epi8() - 似乎很昂贵,需要多次转变和 OR

  2. 直接高效生成随机索引 -

即索引i将是未压缩数据中第i个有效元素的索引

element_valid:   0 0 1 0 1 0 0 1 0
gather_indices:  x x x x x x 6 4 1
scatter_indices: 3 3 2 2 1 1 1 0 0

串行执行此操作很简单,但我需要并行(SIMD)。使用前缀和生成分散索引似乎很容易,但由于 AltiVec 和 SSE 都没有分散指令,因此我需要收集索引。聚集索引是分散索引的反函数,但如何并行获得呢?我知道在 GPU 编程的先驱时期,converting scatters to gathers是一种常见的技术,但是这两种描述的方法似乎都不实用。

也许如果不坚持压缩保留元素顺序将允许更有效的实现?我可以放弃。

最佳答案

如果您想模拟 _mm_movemask_epi8 并且只需要来自 8 字节元素的 8 位标量掩码,那么您可以使用 AltiVec 执行以下操作:

#include <stdio.h>

int main(void)
{
    const vector unsigned char vShift = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0 };
                                            // constant shift vector

    vector unsigned char isValid = { 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                                            // sample input

    vector unsigned char v1 = vec_sl(isValid, vShift);
                                            // shift input values
    vector unsigned int v2 = vec_sum4s(v1, (vector unsigned int)(0));
    vector signed int v3 = vec_sum2s((vector signed int)v2, (vector signed int)(0));
                                            // sum shifted values
    vector signed int v4 = vec_splat(v3, 1);
    unsigned int mask __attribute__ ((aligned(16)));
    vec_ste((vector unsigned int)v4, 0, &mask);
                                            // store sum in scalar

    printf("v1 = %vu\n", v1);
    printf("v2 = %#vlx\n", v2);
    printf("v3 = %#vlx\n", v3);
    printf("v4 = %#vlx\n", v4);
    printf("mask = %#x\n", mask);

    return 0;
}

这是 5 条 AltiVec 指令,而 SSE 中只有 1 条指令。您可能会丢失 vec_splat 并将其减少到 4。

关于sse - 将分散索引转换为聚集索引的有效方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6270641/

相关文章:

c++ - 从脚本中较高的函数调用脚本中较低的函数

c - 快速计算 __m128i 寄存器中设置位的数量

gcc - 循环中的 BB 太多,无法矢量化是什么意思?

python - 如何有效地找出序数中的数字是什么?

assembly - 添加具有饱和度的 32 位字

尽可能快地比较缓冲区

c++ - GCC -msse2 不生成 SIMD 代码

c - 两个 __m128i 的两个位到一个 __m128i 的四个位 -SSE

assembly - 使用Intel SIMD SSE加载非连续值

python - 在 numpy 二维数组中重新分配多余的值