Yal*_*ang 8 sse simd vectorization altivec stream-compaction
我正在尝试使用SIMD内在函数编写流压缩(获取数组并删除空元素).循环的每次迭代一次处理8个元素(SIMD宽度).
使用SSE内在函数,我可以使用_mm_shuffle_epi8()进行相当有效的操作,它执行16条表查找(收集并行计算术语).shuffle索引是预先计算的,并使用位掩码查找.
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];
}
Run Code Online (Sandbox Code Playgroud)
我的问题是现在我想为Altivec SIMD实现这个(不要问为什么 - 错误的商业决策).Altivec没有_mm_movemask_epi8()的等价物,这是一个关键因素.所以,我需要找到一种方法
模拟_mm_movemask_epi8() - 似乎很贵,有几个班次和OR
直接生成有效的shuffle指数 -
即,索引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
Run Code Online (Sandbox Code Playgroud)
串行执行此操作非常简单,但我需要它是并行(SIMD).使用前缀sum生成散列索引似乎很容易,但由于AltiVec和SSE都没有散点指令,我需要收集索引.收集索引是散射指数的反函数,但是如何并行获得?我知道在GPU编程的开创性时代,将散射转换为收集是一种常见的技术,但这两种方法中没有一种看似实用.
也许如果不坚持压缩保留元素顺序将允许更有效的实现?我可以放弃.
如果你想模拟_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;
}
Run Code Online (Sandbox Code Playgroud)
这是5个AltiVec指令与SSE中的1个指令.你可能会失去vec_splat
并把它降到4.
归档时间: |
|
查看次数: |
2081 次 |
最近记录: |