紧凑的AVX2寄存器,因此选择的整数是连续的,根据掩码
Compact AVX2 register so selected integers are contiguous according to mask
在优化数组压缩这个问题中,最上面的答案是:
具有最新指令集的SSE/AVX寄存器允许更好的方法。我们可以直接使用PMOVMSKB的结果,将其转换为PSHUFB之类的控制寄存器。
这是可能的Haswell (AVX2)吗?还是需要AVX512的一种?
我得到了一个包含int32的AVX2向量,以及一个比较结果的相应向量。我想以某种方式对其进行洗牌,以便在掩码中设置相应msb的元素(比较为真)在向量的低端连续。
我能看到的最好的是获得一个位掩码与_mm256_movemask_ps/vmovmskps(没有*d变体?),然后在256 AVX2矢量查找表中使用它来获得交叉车道_mm256_permutevar8x32_epi32/vpermd的shuffle掩码
首先要做的是找到一个快速标量函数。这是一个不使用分支的版本。
inline int compact(int *x, int *y, const int n) {
int cnt = 0;
for(int i=0; i<n; i++) {
int cut = x[i]!=0;
y[cnt] = cut*x[i];
cnt += cut;
}
return cnt;
}
SIMD的最佳结果可能取决于零的分布。是稀疏还是稠密。下面的代码应该可以很好地用于稀疏或密集的分布。例如长时间运行零和非零。如果分布更均匀,我不知道这个代码是否会有任何好处。但无论如何它都会给出正确的结果。
这是我测试的AVX2版本。
int compact_AVX2(int *x, int *y, int n) {
int i =0, cnt = 0;
for(i=0; i<n-8; i+=8) {
__m256i x4 = _mm256_loadu_si256((__m256i*)&x[i]);
__m256i cmp = _mm256_cmpeq_epi32(x4, _mm256_setzero_si256());
int mask = _mm256_movemask_epi8(cmp);
if(mask == -1) continue; //all zeros
if(mask) {
cnt += compact(&x[i],&y[cnt], 8);
}
else {
_mm256_storeu_si256((__m256i*)&y[cnt], x4);
cnt +=8;
}
}
cnt += compact(&x[i], &y[cnt], n-i); // cleanup for n not a multiple of 8
return cnt;
}
这是我测试的SSE2版本。
int compact_SSE2(int *x, int *y, int n) {
int i =0, cnt = 0;
for(i=0; i<n-4; i+=4) {
__m128i x4 = _mm_loadu_si128((__m128i*)&x[i]);
__m128i cmp = _mm_cmpeq_epi32(x4, _mm_setzero_si128());
int mask = _mm_movemask_epi8(cmp);
if(mask == 0xffff) continue; //all zeroes
if(mask) {
cnt += compact(&x[i],&y[cnt], 4);
}
else {
_mm_storeu_si128((__m128i*)&y[cnt], x4);
cnt +=4;
}
}
cnt += compact(&x[i], &y[cnt], n-i); // cleanup for n not a multiple of 4
return cnt;
}
这是一个完整的测试
#include <stdio.h>
#include <stdlib.h>
#if defined (__GNUC__) && ! defined (__INTEL_COMPILER)
#include <x86intrin.h>
#else
#include <immintrin.h>
#endif
#define N 50
inline int compact(int *x, int *y, const int n) {
int cnt = 0;
for(int i=0; i<n; i++) {
int cut = x[i]!=0;
y[cnt] = cut*x[i];
cnt += cut;
}
return cnt;
}
int compact_SSE2(int *x, int *y, int n) {
int i =0, cnt = 0;
for(i=0; i<n-4; i+=4) {
__m128i x4 = _mm_loadu_si128((__m128i*)&x[i]);
__m128i cmp = _mm_cmpeq_epi32(x4, _mm_setzero_si128());
int mask = _mm_movemask_epi8(cmp);
if(mask == 0xffff) continue; //all zeroes
if(mask) {
cnt += compact(&x[i],&y[cnt], 4);
}
else {
_mm_storeu_si128((__m128i*)&y[cnt], x4);
cnt +=4;
}
}
cnt += compact(&x[i], &y[cnt], n-i); // cleanup for n not a multiple of 4
return cnt;
}
int compact_AVX2(int *x, int *y, int n) {
int i =0, cnt = 0;
for(i=0; i<n-8; i+=8) {
__m256i x4 = _mm256_loadu_si256((__m256i*)&x[i]);
__m256i cmp = _mm256_cmpeq_epi32(x4, _mm256_setzero_si256());
int mask = _mm256_movemask_epi8(cmp);
if(mask == -1) continue; //all zeros
if(mask) {
cnt += compact(&x[i],&y[cnt], 8);
}
else {
_mm256_storeu_si256((__m256i*)&y[cnt], x4);
cnt +=8;
}
}
cnt += compact(&x[i], &y[cnt], n-i); // cleanup for n not a multiple of 8
return cnt;
}
int main() {
int x[N], y[N];
for(int i=0; i<N; i++) x[i] = rand()%10;
//int cnt = compact_SSE2(x,y,N);
int cnt = compact_AVX2(x,y,N);
for(int i=0; i<N; i++) printf("%d ", x[i]); printf("n");
for(int i=0; i<cnt; i++) printf("%d ", y[i]); printf("n");
}
相关文章:
- 位移操作和位掩码未检测到重复字符
- OpenCV - 带有掩码的absdiff
- 生成前缀位掩码
- 如何从__m64值的 lsb 创建 8 位掩码?
- 如何对无符号长 int 进行位掩码?
- 删除K的背景掩码-意味着Python或C++中的集群/
- 如何在C++中优雅地处理位掩码
- 将uint64_t位掩码转换为 std::布尔数组
- 使输入二进制掩码适应 ITK 网格生成器
- 如何从 getifaddr 读取子网掩码
- 优化从子位掩码生成父位掩码
- 基于模式创建位掩码作为 constexpr
- 使用二进制掩码 C++ ITK 获取感兴趣区域
- C++中的运行时位复制(位掩码)
- 根据 IP 和掩码C++打印所有 IP
- C++设置"blank"或重置 ifstrean (ios) 的异常掩码
- OpenCV 检测带有掩码的斑点
- OPENCV:如何创建多边形形状的掩码
- 递归函数,用于使用位掩码 c++ 显示集合的所有子集
- 紧凑的AVX2寄存器,因此选择的整数是连续的,根据掩码