Как сделать mm256 maskstore epi8 () на C/C++?

проблема

то, что я пытаюсь сделать, это, если у меня есть вектор 27 (не 32!) 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}

тогда этот вектор используется для выполнения некоторых очень сложных вычислений, но для простоты предположим, что следующий шаг-просто циклический-сдвинуть его влево на n и сохранить его в память. Поэтому у меня должен быть новый вектор 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}

таким образом, есть тысячи таких векторов, и производительность здесь очень важна. Процессор, который мы используем, поддерживает 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);

почему x и y объявляются в размере [31+27+31] заключается в том, что в это дело _mm256_loadu_si256() и _mm256_storeu_si256() не вызовет segfault.

и я могу получить значение y by:

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

новая задача

к сожалению, все векторы должны быть непрерывными в памяти, например, если есть два вектора, которые должны быть обработаны:

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 назад в память, потому что когда значение второго вектора записывается в память, оно будет перезаписать некоторые значения первого вектора:

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 Встроенное Руководство я не смог найти _mm256_maskstore_epi8. Это возвращает меня к теме:

как сделать _mm256_maskstore_epi8 () в C/C++?

2 ответов


существует еще одна реализация циклического сдвига внутри 27-байтового вектора с использованием AVX2:

#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

выглядит более простым, чем мой предыдущий ответ.


Я сделал реализацию циклического сдвига внутри 27-байтового вектора с использованием SSSE3:

#include <iostream>
#include <tmmintrin.h>

union Shuffle
{
    uint8_t s[64];
    __m128i v[4];
};

Shuffle shuffles[27];

int Shift(int value)
{
    return (value >= 0 && value < 16) ? value : -1;
}

void Init()
{
    for (int s = 0; s < 27; ++s)
    {
        for (int i = 0; i < 16; ++i)
        {
            shuffles[s].s[0 + i] = s < 16 ? Shift(i - s) : Shift(i - s + 27);
            shuffles[s].s[16 + i] = Shift(16 + i - s);
            shuffles[s].s[32 + i] = Shift(11 + i - s);
            shuffles[s].s[48 + i] = s < 11 ? Shift(i - s) : Shift(i - s + 27);
        }
    }
}

void CyclicShift27(const uint8_t * src, size_t shift, uint8_t * dst)
{
    __m128i srcLo = _mm_loadu_si128((__m128i*)(src + 0));
    __m128i srcHi = _mm_loadu_si128((__m128i*)(src + 11));
    __m128i dstLo = _mm_or_si128(_mm_shuffle_epi8(srcLo, shuffles[shift].v[0]), _mm_shuffle_epi8(srcHi, shuffles[shift].v[1]));
    __m128i dstHi = _mm_or_si128(_mm_shuffle_epi8(srcLo, shuffles[shift].v[2]), _mm_shuffle_epi8(srcHi, shuffles[shift].v[3]));
    _mm_storeu_si128((__m128i*)(dst + 0), dstLo);
    _mm_storeu_si128((__m128i*)(dst + 11), dstHi);
}

int main()
{
    Init();
    uint8_t src[27] = { 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 }, dst[27];
    for (int j = 0; j < 27; ++j)
    {
        CyclicShift27(src, j, dst);
        for (int i = 0; i < 27; 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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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

Я надеюсь, что это будет полезно.