【问题标题】:efficient way to convert scatter indices into gather indices?将分散索引转换为聚集索引的有效方法?
【发布时间】:2011-09-10 08:51:17
【问题描述】:

我正在尝试使用 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. emulate _mm_movemask_epi8() - 似乎很昂贵,需要几个班次和 ORs

  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 是一种常用技术,但所描述的这两种方法似乎都不实用。

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

【问题讨论】:

    标签: sse simd vectorization altivec stream-compaction


    【解决方案1】:

    如果你想模拟 _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。

    【讨论】:

      猜你喜欢
      • 2011-04-05
      • 1970-01-01
      • 2015-07-31
      • 1970-01-01
      • 2012-10-13
      • 2013-08-07
      • 1970-01-01
      • 2011-05-24
      相关资源
      最近更新 更多