c++ - 如何在 C/C++ 中执行 _mm256_maskstore_epi8()?

标签 c++ simd intrinsics avx avx2

问题

我想做的是,如果我有一个 27(不是 32!)的 vector int8_t:

x = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 ,21,22,23,24,25,26}

我想首先将它向右循环移位 n(不是常数),例如如果 n=1:

x2 = {26,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 ,20,21,22,23,24,25}

然后这个vector被用来做一些非常复杂的计算,但是为了简单起见,我们假设下一步只是将它循环左移n,然后存入内存。所以我应该有一个新的 vector 27 int8_t:

y = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 ,21,22,23,24,25,26}

所以有成千上万个这样的 vector ,性能在这里非常关键。我们使用的 CPU 支持 AVX2,所以我们想用它来加快速度。

我目前的解决方案

为了获得 x2,我使用了两个 _mm256_loadu_si256() 和一个 _mm256_blendv_epi8():

int8_t x[31+27+31];
for(int i=0; i<27; i++){
    x[31+i] = i;
}
__m256i mask = _mm256_set_epi32 (0x0, 0x00800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0);
__m256i x_second_part = _mm256_loadu_si256((__m256i*)(x+31+1));  //{1,2,...,26}
__m256i x_first_part  = _mm256_loadu_si256((__m256i*)(x+31-26)); //{0}
__m256i x2            = _mm256_blendv_epi8(x_second_part, x_first_part, mask); //{1,2,...,26, 0}
int8_t y[31+27+31];
_mm256_storeu_si256((__m256i*)(y+31-26), x2);
_mm256_storeu_si256((__m256i*)(y+31+1), x2);

xy 的大小被声明为 [31+27+31] 的原因是在这种情况下 _mm256_loadu_si256()_mm256_storeu_si256() 不会导致段错误。

我可以通过以下方式获取 y 的值:

for(int i=0; i<27; i++){
    cout << (int)y[31+i] << ' ';
}

新问题

不幸的是,所有的 vector 在内存中必须是连续的,例如,如果总共有两个 vector 需要处理:

x = {[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26];
     [27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53]}; 

然后我不能只使用 _mm256_storeu_si256()y 的值放回内存,因为当第二个 vector 的值写入内存时,它会覆盖一些第一个 vector 的值:

int8_t x[31+27+27+31];
int8_t y[31+27+27+31];
for(int i=0; i<27*2; i++){
    x[31+i] = i;
}
for(int i=0; i<2; i++){
    __m256i mask = _mm256_set_epi32 (0x0, 0x00800000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0);
    __m256i x_second_part = _mm256_loadu_si256((__m256i*)(x+31+27*i+1));  //{1,2,...,26}
    __m256i x_first_part  = _mm256_loadu_si256((__m256i*)(x+31+27*i-26)); //{0}
    __m256i x2            = _mm256_blendv_epi8(x_second_part, x_first_part, mask); //{1,2,...,26, 0}
    _mm256_storeu_si256((__m256i*)(y+31+27*i-26), x2);
    _mm256_storeu_si256((__m256i*)(y+31+27*i+1), x2);
}
for(int i=0; i<27; i++){
    cout << (int)y[31+i] << ' ';
}cout << endl;
for(int i=0; i<27; i++){
    cout << (int)y[31+27+i] << ' ';
}cout << endl;

会输出

0 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 

代替

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 

所以我在考虑使用maskstore。但是在Intel Intrinsic Guide我找不到 _mm256_maskstore_epi8。这让我回到主题:

如何在 C/C++ 中执行 _mm256_maskstore_epi8()?

最佳答案

还有另一种使用 AVX2 在 27 字节 vector 内实现循环移位的方法:

#include <iostream>
#include <immintrin.h>

const __m256i K0 = _mm256_setr_epi8(
    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0);

const __m256i K1 = _mm256_setr_epi8(
    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70);

inline const __m256i Shuffle(const __m256i & value, const __m256i & shuffle)
{
    return _mm256_or_si256(_mm256_shuffle_epi8(value, _mm256_add_epi8(shuffle, K0)),
        _mm256_shuffle_epi8(_mm256_permute4x64_epi64(value, 0x4E), _mm256_add_epi8(shuffle, K1)));
}

__m256i shuffles[27];

void Init()
{
    uint8_t * p = (uint8_t *)shuffles;
    for (int s = 0; s < 27; ++s)
        for (int i = 0; i < 32; ++i)
            p[s*32 + i] = i < 27 ? (27 + i - s)%27 : i;
}

void CyclicShift27(const uint8_t * src, size_t shift, uint8_t * dst)
{
    _mm256_storeu_si256((__m256i*)dst,  Shuffle(_mm256_loadu_si256((__m256i*)src), shuffles[shift]));
}

int main()
{
    Init();
    uint8_t src[32] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 }, dst[32];
    for (int j = 0; j < 27; ++j)
    {
        CyclicShift27(src, j, dst);
        std::cout << "\t";
        for (int i = 0; i < 32; i++)
            std::cout << (int)dst[i] << ' ';
        std::cout << std::endl;
    }
    return 0;
}

输出:

    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 27 28 29 30 31
    25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 27 28 29 30 31
    24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 27 28 29 30 31
    23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 27 28 29 30 31
    22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 27 28 29 30 31
    21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 27 28 29 30 31
    20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 27 28 29 30 31
    19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 27 28 29 30 31
    18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 27 28 29 30 31
    17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 27 28 29 30 31
    16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 27 28 29 30 31
    15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 27 28 29 30 31
    14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 27 28 29 30 31
    13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 12 27 28 29 30 31
    12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 11 27 28 29 30 31
    11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 10 27 28 29 30 31
    10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 9 27 28 29 30 31
    9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 8 27 28 29 30 31
    8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 7 27 28 29 30 31
    7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 6 27 28 29 30 31
    6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 5 27 28 29 30 31
    5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 4 27 28 29 30 31
    4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 3 27 28 29 30 31
    3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 2 27 28 29 30 31
    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 1 27 28 29 30 31
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 0 27 28 29 30 31

看起来比我之前的回答简单多了。

关于c++ - 如何在 C/C++ 中执行 _mm256_maskstore_epi8()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40284282/

相关文章:

c++ - BOOST_STRONG_TYPEDEF并 move 语义

gpgpu - GPGPU编程仅允许执行SIMD指令吗?

c++ - 矢量化和#pragma omp simd

c++ - _mm_cvtsd_f64 模拟高阶 float

c++ - 提取 __m128i 中每个 bool 字节的低位? bool 数组到打包位图

c++ - 在带有 std::greater 附加参数的 std::map 上使用三元运算符

c++ - 如何在QLineEdit的开头设置修复字符串?

c - Fortran 调用 C : How do I get an efficient vectorised function

C 内在效率 - 哪个更好?

java - 字符端口上的运算符 OR 到 java