C++ 什么';最快的STERE-3采集指令序列是什么? 问题是:

C++ 什么';最快的STERE-3采集指令序列是什么? 问题是:,c++,x86,vectorization,avx2,C++,X86,Vectorization,Avx2,从内存生成32位元素的STERE-3集合的最有效序列是什么? 如果存储器被安排为: MEM = R0 G0 B0 R1 G1 B1 R2 G2 B2 R3 G3 B3 ... 我们希望获得三个YMM寄存器,其中: YMM0 = R0 R1 R2 R3 R4 R5 R6 R7 YMM1 = G0 G1 G2 G3 G4 G5 G6 G7 YMM2 = B0 B1 B2 B3 B4 B5 B6 B7 动机与讨论 标量C代码类似于 template <typename T> T Pro

从内存生成32位元素的STERE-3集合的最有效序列是什么? 如果存储器被安排为:

MEM = R0 G0 B0 R1 G1 B1 R2 G2 B2 R3 G3 B3 ...
我们希望获得三个YMM寄存器,其中:

YMM0 = R0 R1 R2 R3 R4 R5 R6 R7
YMM1 = G0 G1 G2 G3 G4 G5 G6 G7
YMM2 = B0 B1 B2 B3 B4 B5 B6 B7
动机与讨论 标量C代码类似于

template <typename T>
T Process(const T* Input) {
  T Result = 0;
  for (int i=0; i < 4096; ++i) {
    T R = Input[3*i];
    T G = Input[3*i+1];
    T B = Input[3*i+2];
    Result += some_parallelizable_algorithm<T>(R, G, B);  
  }
  return Result;
}
模板
T进程(常数T*输入){
T结果=0;
对于(int i=0;i<4096;++i){
tr=输入[3*i];
T G=输入[3*i+1];
tb=输入[3*i+2];
结果+=一些可并行的算法(R,G,B);
}
返回结果;
}
假设一些可并行化的算法是用intrinsic编写的,并被调整为尽可能快的实现:

template <typename T>
__m256i some_parallelizable_algorithm(__m256i R, __m256i G, __m256i B);
模板
__m256i一些可并行化的算法(m256i R,m256i G,m256i B);
因此T=int32\u T的向量实现可以是:

    template <>
    int32_t Process<int32_t>(const int32_t* Input) {
     __m256i Step = _mm256_set_epi32(0, 1, 2, 3, 4, 5, 6, 7);
     __m256i Result = _mm256_setzero_si256(); 
     for (int i=0; i < 4096; i+=8) {
       // R = R0 R1 R2 R3 R4 R5 R6 R7
       __m256i R = _mm256_i32gather_epi32 (Input+3*i, Step, 3);
       // G = G0 G1 G2 G3 G4 G5 G6 G7
       __m256i G = _mm256_i32gather_epi32 (Input+3*i+1, Step, 3);
       // B = B0 B1 B2 B3 B4 B5 B6 B7
       __m256i B = _mm256_i32gather_epi32 (Input+3*i+2, Step, 3);
       Result = _mm256_add_epi32 (Result, 
                                  some_parallelizable_algorithm<int32_t>(R, G, B));
     }
   // Here should be the less interesting part:
   // Perform a reduction on Result and return the result
}
模板
int32_t进程(常量int32_t*输入){
__m256i步进=_mm256_set_epi32(0、1、2、3、4、5、6、7);
__m256i结果=_mm256_setzero_si256();
对于(int i=0;i<4096;i+=8){
//R=R0 R1 R2 R3 R4 R5 R6 R7
__m256i R=_mm256_i32gather_epi32(输入+3*i,步骤3);
//G=G0 G1 G2 G3 G4 G5 G6 G7
__m256i G=_mm256_i32gather_epi32(输入+3*i+1,步骤3);
//B=B0B1 B2 B3 B4 B5 B6 B7
__m256i B=_mm256_i32gather_epi32(输入+3*i+2,步骤3);
结果=_mm256_add_epi32(结果,
一些可并行的算法(R,G,B);
}
//以下是不太有趣的部分:
//对结果执行缩减并返回结果
}
首先,可以这样做,因为32位元素有聚集指令,但16位元素或8位元素没有。 第二,也是更重要的一点,出于性能原因,应该完全避免上面的聚集指令。使用连续的宽加载并对加载的值进行洗牌以获得R、G和B向量可能更有效

    template <>
    int32_t Process<int32_t>(const int32_t* Input) {
     __m256i Result = _mm256_setzero_si256(); 
     for (int i=0; i < 4096; i+=3) {
       __m256i Ld0 = _mm256_lddqu_si256((__m256i*)Input+3*i));
       __m256i Ld1 = _mm256_lddqu_si256((__m256i*)Input+3*i+1));
       __m256i Ld2 = _mm256_lddqu_si256((__m256i*)Input+3*i+2));
       __m256i R = ???
       __m256i G = ???
       __m256i B = ???
       Result = _mm256_add_epi32 (Result, 
                                  some_parallelizable_algorithm<int32_t>(R, G, B));
     }
   // Here should be the less interesting part:
   // Perform a reduction on Result and return the result
}
模板
int32_t进程(常量int32_t*输入){
__m256i结果=_mm256_setzero_si256();
对于(int i=0;i<4096;i+=3){
__m256i Ld0=_mm256_lddqu_si256((_m256i*)输入+3*i));
__m256i Ld1=_mm256_lddqu_si256((_m256i*)输入+3*i+1));
__m256i Ld2=_mm256_lddqu_si256((_m256i*)输入+3*i+2));
__m256i R=???
__m256i G=???
__m256i B=???
结果=_mm256_add_epi32(结果,
一些可并行的算法(R,G,B);
}
//以下是不太有趣的部分:
//对结果执行缩减并返回结果
}
似乎对于power-2步幅(2,4,…)有使用UNKPCKL/UNKPCKH的已知方法,但对于步幅-3访问,我找不到任何参考

我对解决T=int32\u T、T=int16\u T和T=int8\u T的问题很感兴趣,但为了保持重点,让我们只讨论第一种情况。

描述了如何精确执行您想要的3x8情况

那篇文章介绍了浮动的情况。如果需要
int32
,则需要强制转换输出,因为没有整数版本的
\u mm256\u shuffle\u ps()

逐字复制他们的解决方案:

float *p;  // address of first vector
__m128 *m = (__m128*) p;
__m256 m03;
__m256 m14; 
__m256 m25; 
m03  = _mm256_castps128_ps256(m[0]); // load lower halves
m14  = _mm256_castps128_ps256(m[1]);
m25  = _mm256_castps128_ps256(m[2]);
m03  = _mm256_insertf128_ps(m03 ,m[3],1);  // load upper halves
m14  = _mm256_insertf128_ps(m14 ,m[4],1);
m25  = _mm256_insertf128_ps(m25 ,m[5],1);

__m256 xy = _mm256_shuffle_ps(m14, m25, _MM_SHUFFLE( 2,1,3,2)); // upper x's and y's 
__m256 yz = _mm256_shuffle_ps(m03, m14, _MM_SHUFFLE( 1,0,2,1)); // lower y's and z's
__m256 x  = _mm256_shuffle_ps(m03, xy , _MM_SHUFFLE( 2,0,3,0)); 
__m256 y  = _mm256_shuffle_ps(yz , xy , _MM_SHUFFLE( 3,1,2,0)); 
__m256 z  = _mm256_shuffle_ps(yz , m25, _MM_SHUFFLE( 3,0,3,1)); 
这是11条指令。(6次装载,5次洗牌)


在一般情况下,可以在
O(s*log(W))
指令中执行
sxw
转置。其中:

  • S
    是步幅
  • W
    是SIMD宽度
假设存在2矢量置换和半矢量插入荷载,则公式为:

(S x W load-permute)  <=  S * (lg(W) + 1) instructions
反向
3 x 16
转置存储将留给读者作为练习


由于
S=3
有些退化,因此该模式一点也不容易看到。但是如果你能看到这个模式,你就可以把它推广到任意奇数整数
S
以及任意二次幂
W

8位整数的情况。

正如上面的注释中已经提到的,两条输入洗牌指令,例如
vshufps
,不需要 存在8位粒度。因此,8位解决方案与32位解决方案略有不同。下面介绍两种不同的解决方案


一种简单的方法是将8位整数“按颜色(rgb)”与6
vpblendvb
-s分组,然后 通过
vpshufb
排列:

#include <stdio.h>
#include <x86intrin.h>
/*  gcc -O3 -Wall -m64 -march=broadwell stride_3.c   */
int __attribute__ ((noinline)) print_vec_char(__m256i x);

int main() {

    char *m;
    int i;
    __m256i blnd1 = _mm256_set_epi8(0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,     0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0);
    __m256i blnd2 = _mm256_set_epi8(0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,     0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0);
    __m256i p0    = _mm256_set_epi8(13,10,7,4,1, 14,11,8,5,2, 15,12,9,6,3,0,    13,10,7,4,1, 14,11,8,5,2, 15,12,9,6,3,0);
    __m256i p1    = _mm256_set_epi8(14,11,8,5,2, 15,12,9,6,3,0, 13,10,7,4,1,    14,11,8,5,2, 15,12,9,6,3,0, 13,10,7,4,1);
    __m256i p2    = _mm256_set_epi8(15,12,9,6,3,0, 13,10,7,4,1, 14,11,8,5,2,    15,12,9,6,3,0, 13,10,7,4,1, 14,11,8,5,2);
            m     = _mm_malloc(96,32);
    for(i = 0; i < 96; i++) m[i] = i;

//        printf("m_lo  ");print_vec_char(_mm256_load_si256((__m256i*)&m[0]));printf("m_mid ");print_vec_char(_mm256_load_si256((__m256i*)&m[32]));printf("m_hi  ");print_vec_char(_mm256_load_si256((__m256i*)&m[64]));printf("\n");
//        m_lo   31  30  29  28 |  27  26  25  24 |  23  22  21  20 |  19  18  17  16 ||  15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0 
//        m_mid  63  62  61  60 |  59  58  57  56 |  55  54  53  52 |  51  50  49  48 ||  47  46  45  44 |  43  42  41  40 |  39  38  37  36 |  35  34  33  32 
//        m_hi   95  94  93  92 |  91  90  89  88 |  87  86  85  84 |  83  82  81  80 ||  79  78  77  76 |  75  74  73  72 |  71  70  69  68 |  67  66  65  64 

    __m256i t0 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[0]));
    __m256i t1 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[16]));
    __m256i t2 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[32]));

            t0 = _mm256_inserti128_si256(t0,_mm_loadu_si128((__m128i*)&m[48]),1);
            t1 = _mm256_inserti128_si256(t1,_mm_loadu_si128((__m128i*)&m[64]),1);
            t2 = _mm256_inserti128_si256(t2,_mm_loadu_si128((__m128i*)&m[80]),1);

//        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("\n");
//        t0   63  62  61  60 |  59  58  57  56 |  55  54  53  52 |  51  50  49  48 ||  15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0 
//        t1   79  78  77  76 |  75  74  73  72 |  71  70  69  68 |  67  66  65  64 ||  31  30  29  28 |  27  26  25  24 |  23  22  21  20 |  19  18  17  16 
//        t2   95  94  93  92 |  91  90  89  88 |  87  86  85  84 |  83  82  81  80 ||  47  46  45  44 |  43  42  41  40 |  39  38  37  36 |  35  34  33  32 

    __m256i u0 = _mm256_blendv_epi8( _mm256_blendv_epi8(t0,t1,blnd2), t2,blnd1);
    __m256i u1 = _mm256_blendv_epi8( _mm256_blendv_epi8(t1,t2,blnd2), t0,blnd1);
    __m256i u2 = _mm256_blendv_epi8( _mm256_blendv_epi8(t2,t0,blnd2), t1,blnd1);

//        printf("u0  ");print_vec_char(u0);printf("u1  ");print_vec_char(u1);printf("u2  ");print_vec_char(u2);printf("\n");
//        u0   63  78  93  60 |  75  90  57  72 |  87  54  69  84 |  51  66  81  48 ||  15  30  45  12 |  27  42   9  24 |  39   6  21  36 |   3  18  33   0 
//        u1   79  94  61  76 |  91  58  73  88 |  55  70  85  52 |  67  82  49  64 ||  31  46  13  28 |  43  10  25  40 |   7  22  37   4 |  19  34   1  16 
//        u2   95  62  77  92 |  59  74  89  56 |  71  86  53  68 |  83  50  65  80 ||  47  14  29  44 |  11  26  41   8 |  23  38   5  20 |  35   2  17  32 

            t0 = _mm256_shuffle_epi8(u0,p0);
            t1 = _mm256_shuffle_epi8(u1,p1);
            t2 = _mm256_shuffle_epi8(u2,p2);

          printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("\n");
//        t0   93  90  87  84 |  81  78  75  72 |  69  66  63  60 |  57  54  51  48 ||  45  42  39  36 |  33  30  27  24 |  21  18  15  12 |   9   6   3   0 
//        t1   94  91  88  85 |  82  79  76  73 |  70  67  64  61 |  58  55  52  49 ||  46  43  40  37 |  34  31  28  25 |  22  19  16  13 |  10   7   4   1 
//        t2   95  92  89  86 |  83  80  77  74 |  71  68  65  62 |  59  56  53  50 ||  47  44  41  38 |  35  32  29  26 |  23  20  17  14 |  11   8   5   2 

    return 0;
}



int __attribute__ ((noinline)) print_vec_char(__m256i x){
    char v[32];
    _mm256_storeu_si256((__m256i *)v,x);
    printf("%3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi || ",
           v[31],v[30],v[29],v[28],v[27],v[26],v[25],v[24],v[23],v[22],v[21],v[20],v[19],v[18],v[17],v[16]);
    printf("%3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi \n",
           v[15],v[14],v[13],v[12],v[11],v[10],v[9],v[8],v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0]);
    return 0;
}

不幸的是,
vpblendvb
指令通常相对较慢: 在“英特尔Skylake”
vpblendvb
上,每个周期的吞吐量为一个,并且 在AMD Ryzen和Intel Haswell上,吞吐量仅为每两个气缸一个。 Skylake-X有一个快速字节混合
vpblendmb
(每周期三个吞吐量(256位)),但在Skylake-X上一个可能更高 对使用512位矢量而不是256位矢量的解决方案感兴趣


另一种方法是将
vpshufb
vshufps
相结合,正如上面@Peter Cordes的评论所建议的那样。 在下面的代码中,数据作为12字节块加载。与第一个解决方案相比,总共需要更多的说明。 然而,第二种解决方案的性能可能比第一种解决方案要好,这取决于周围的代码 以及微架构

#include <stdio.h>
#include <x86intrin.h>
/*  gcc -O3 -Wall -m64 -march=broadwell stride_3.c   */
int __attribute__ ((noinline)) print_vec_char(__m256i x);

inline __m256i _mm256_shufps_epi32(__m256i a,__m256i b,int imm){return _mm256_castps_si256(_mm256_shuffle_ps(_mm256_castsi256_ps(a),_mm256_castsi256_ps(b),imm));}

int main() {

    char *m;
    int i;
    __m256i p0    = _mm256_set_epi8(-1,-1,-1,-1, 11,8,5,2, 10,7,4,1, 9,6,3,0,     -1,-1,-1,-1, 11,8,5,2, 10,7,4,1, 9,6,3,0);
    __m256i p1    = _mm256_set_epi8(11,8,5,2, 10,7,4,1, 9,6,3,0, -1,-1,-1,-1,     11,8,5,2, 10,7,4,1, 9,6,3,0, -1,-1,-1,-1);
    __m256i p2    = _mm256_set_epi8(10,7,4,1, 9,6,3,0, -1,-1,-1,-1, 11,8,5,2,     10,7,4,1, 9,6,3,0,-1, -1,-1,-1, 11,8,5,2);
    __m256i p3    = _mm256_set_epi8(9,6,3,0, -1,-1,-1,-1, 11,8,5,2, 10,7,4,1,     9,6,3,0, -1,-1,-1,-1, 11,8,5,2, 10,7,4,1);

            m     = _mm_malloc(96+4,32);   /* 4 extra dummy bytes to avoid errors with _mm_loadu_si128((__m128i*)&m[84]) . Otherwise use maskload instead of standard load */
    for(i = 0; i < 96; i++) m[i] = i;

//        printf("m_lo  ");print_vec_char(_mm256_load_si256((__m256i*)&m[0]));printf("m_mid ");print_vec_char(_mm256_load_si256((__m256i*)&m[32]));printf("m_hi  ");print_vec_char(_mm256_load_si256((__m256i*)&m[64]));printf("\n");
//        m_lo   31  30  29  28 |  27  26  25  24 |  23  22  21  20 |  19  18  17  16 ||  15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0 
//        m_mid  63  62  61  60 |  59  58  57  56 |  55  54  53  52 |  51  50  49  48 ||  47  46  45  44 |  43  42  41  40 |  39  38  37  36 |  35  34  33  32 
//        m_hi   95  94  93  92 |  91  90  89  88 |  87  86  85  84 |  83  82  81  80 ||  79  78  77  76 |  75  74  73  72 |  71  70  69  68 |  67  66  65  64 

    __m256i t0 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[0]));
    __m256i t1 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[12]));
    __m256i t2 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[24]));
    __m256i t3 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[36]));

            t0 = _mm256_inserti128_si256(t0,_mm_loadu_si128((__m128i*)&m[48]),1);
            t1 = _mm256_inserti128_si256(t1,_mm_loadu_si128((__m128i*)&m[60]),1);
            t2 = _mm256_inserti128_si256(t2,_mm_loadu_si128((__m128i*)&m[72]),1);
            t3 = _mm256_inserti128_si256(t3,_mm_loadu_si128((__m128i*)&m[84]),1);   /* Use a masked load (_mm_maskload_epi32) here if m[99] is not a valid address */

//        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("t3  ");print_vec_char(t3);printf("\n");
//        t0   63  62  61  60 |  59  58  57  56 |  55  54  53  52 |  51  50  49  48 ||  15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0 
//        t1   75  74  73  72 |  71  70  69  68 |  67  66  65  64 |  63  62  61  60 ||  27  26  25  24 |  23  22  21  20 |  19  18  17  16 |  15  14  13  12 
//        t2   87  86  85  84 |  83  82  81  80 |  79  78  77  76 |  75  74  73  72 ||  39  38  37  36 |  35  34  33  32 |  31  30  29  28 |  27  26  25  24 
//        t3    0   0   0   0 |  95  94  93  92 |  91  90  89  88 |  87  86  85  84 ||  51  50  49  48 |  47  46  45  44 |  43  42  41  40 |  39  38  37  36 

            t0 = _mm256_shuffle_epi8(t0,p0);
            t1 = _mm256_shuffle_epi8(t1,p1);
            t2 = _mm256_shuffle_epi8(t2,p2);
            t3 = _mm256_shuffle_epi8(t3,p3);

//        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("t3  ");print_vec_char(t3);printf("\n");
//        t0    0   0   0   0 |  59  56  53  50 |  58  55  52  49 |  57  54  51  48 ||   0   0   0   0 |  11   8   5   2 |  10   7   4   1 |   9   6   3   0 
//        t1   71  68  65  62 |  70  67  64  61 |  69  66  63  60 |   0   0   0   0 ||  23  20  17  14 |  22  19  16  13 |  21  18  15  12 |   0   0   0   0 
//        t2   82  79  76  73 |  81  78  75  72 |   0   0   0   0 |  83  80  77  74 ||  34  31  28  25 |  33  30  27  24 |   0   0   0   0 |  35  32  29  26 
//        t3   93  90  87  84 |   0   0   0   0 |  95  92  89  86 |  94  91  88  85 ||  45  42  39  36 |   0   0   0   0 |  47  44  41  38 |  46  43  40  37 

    __m256i u0 = _mm256_blend_epi32(t0,t1,0b10101010);
    __m256i u1 = _mm256_blend_epi32(t2,t3,0b10101010);
    __m256i u2 = _mm256_blend_epi32(t0,t1,0b01010101);
    __m256i u3 = _mm256_blend_epi32(t2,t3,0b01010101);

//        printf("u0  ");print_vec_char(u0);printf("u1  ");print_vec_char(u1);printf("u2  ");print_vec_char(u2);printf("u3  ");print_vec_char(u3);printf("\n");
//        u0   71  68  65  62 |  59  56  53  50 |  69  66  63  60 |  57  54  51  48 ||  23  20  17  14 |  11   8   5   2 |  21  18  15  12 |   9   6   3   0 
//        u1   93  90  87  84 |  81  78  75  72 |  95  92  89  86 |  83  80  77  74 ||  45  42  39  36 |  33  30  27  24 |  47  44  41  38 |  35  32  29  26 
//        u2    0   0   0   0 |  70  67  64  61 |  58  55  52  49 |   0   0   0   0 ||   0   0   0   0 |  22  19  16  13 |  10   7   4   1 |   0   0   0   0 
//        u3   82  79  76  73 |   0   0   0   0 |   0   0   0   0 |  94  91  88  85 ||  34  31  28  25 |   0   0   0   0 |   0   0   0   0 |  46  43  40  37 

            t0 = _mm256_blend_epi32(u0,u1,0b11001100);
            t1 = _mm256_shufps_epi32(u2,u3,0b00111001);
            t2 = _mm256_shufps_epi32(u0,u1,0b01001110);

        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("\n");
//        t0   93  90  87  84 |  81  78  75  72 |  69  66  63  60 |  57  54  51  48 ||  45  42  39  36 |  33  30  27  24 |  21  18  15  12 |   9   6   3   0 
//        t1   94  91  88  85 |  82  79  76  73 |  70  67  64  61 |  58  55  52  49 ||  46  43  40  37 |  34  31  28  25 |  22  19  16  13 |  10   7   4   1 
//        t2   95  92  89  86 |  83  80  77  74 |  71  68  65  62 |  59  56  53  50 ||  47  44  41  38 |  35  32  29  26 |  23  20  17  14 |  11   8   5   2 

    return 0;
}



int __attribute__ ((noinline)) print_vec_char(__m256i x){
    char v[32];
    _mm256_storeu_si256((__m256i *)v,x);
    printf("%3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi || ",
           v[31],v[30],v[29],v[28],v[27],v[26],v[25],v[24],v[23],v[22],v[21],v[20],v[19],v[18],v[17],v[16]);
    printf("%3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi \n",
           v[15],v[14],v[13],v[12],v[11],v[10],v[9],v[8],v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0]);
    return 0;
}
将这些方法的思想应用于16位的情况很容易。

使用连续的宽负载和洗牌可能更有效。是的,当然,但你是对的,如何做好这一点并不明显利用像shufps和punpckl/h这样的双输入洗牌似乎是个好主意,但可能还需要一些
pshufb
por
。也许我们可以用某种方式屏蔽和使用packuswb,然后用一个混合了R,G和B的向量做些什么?可能不会,因为屏蔽输入以克服饱和是昂贵的,而且mergin
    3 vmovdqu
    3 vinserti128-load
    6 vpblendvb   
    3 vpshufb
#include <stdio.h>
#include <x86intrin.h>
/*  gcc -O3 -Wall -m64 -march=broadwell stride_3.c   */
int __attribute__ ((noinline)) print_vec_char(__m256i x);

inline __m256i _mm256_shufps_epi32(__m256i a,__m256i b,int imm){return _mm256_castps_si256(_mm256_shuffle_ps(_mm256_castsi256_ps(a),_mm256_castsi256_ps(b),imm));}

int main() {

    char *m;
    int i;
    __m256i p0    = _mm256_set_epi8(-1,-1,-1,-1, 11,8,5,2, 10,7,4,1, 9,6,3,0,     -1,-1,-1,-1, 11,8,5,2, 10,7,4,1, 9,6,3,0);
    __m256i p1    = _mm256_set_epi8(11,8,5,2, 10,7,4,1, 9,6,3,0, -1,-1,-1,-1,     11,8,5,2, 10,7,4,1, 9,6,3,0, -1,-1,-1,-1);
    __m256i p2    = _mm256_set_epi8(10,7,4,1, 9,6,3,0, -1,-1,-1,-1, 11,8,5,2,     10,7,4,1, 9,6,3,0,-1, -1,-1,-1, 11,8,5,2);
    __m256i p3    = _mm256_set_epi8(9,6,3,0, -1,-1,-1,-1, 11,8,5,2, 10,7,4,1,     9,6,3,0, -1,-1,-1,-1, 11,8,5,2, 10,7,4,1);

            m     = _mm_malloc(96+4,32);   /* 4 extra dummy bytes to avoid errors with _mm_loadu_si128((__m128i*)&m[84]) . Otherwise use maskload instead of standard load */
    for(i = 0; i < 96; i++) m[i] = i;

//        printf("m_lo  ");print_vec_char(_mm256_load_si256((__m256i*)&m[0]));printf("m_mid ");print_vec_char(_mm256_load_si256((__m256i*)&m[32]));printf("m_hi  ");print_vec_char(_mm256_load_si256((__m256i*)&m[64]));printf("\n");
//        m_lo   31  30  29  28 |  27  26  25  24 |  23  22  21  20 |  19  18  17  16 ||  15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0 
//        m_mid  63  62  61  60 |  59  58  57  56 |  55  54  53  52 |  51  50  49  48 ||  47  46  45  44 |  43  42  41  40 |  39  38  37  36 |  35  34  33  32 
//        m_hi   95  94  93  92 |  91  90  89  88 |  87  86  85  84 |  83  82  81  80 ||  79  78  77  76 |  75  74  73  72 |  71  70  69  68 |  67  66  65  64 

    __m256i t0 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[0]));
    __m256i t1 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[12]));
    __m256i t2 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[24]));
    __m256i t3 = _mm256_castsi128_si256(_mm_loadu_si128((__m128i*)&m[36]));

            t0 = _mm256_inserti128_si256(t0,_mm_loadu_si128((__m128i*)&m[48]),1);
            t1 = _mm256_inserti128_si256(t1,_mm_loadu_si128((__m128i*)&m[60]),1);
            t2 = _mm256_inserti128_si256(t2,_mm_loadu_si128((__m128i*)&m[72]),1);
            t3 = _mm256_inserti128_si256(t3,_mm_loadu_si128((__m128i*)&m[84]),1);   /* Use a masked load (_mm_maskload_epi32) here if m[99] is not a valid address */

//        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("t3  ");print_vec_char(t3);printf("\n");
//        t0   63  62  61  60 |  59  58  57  56 |  55  54  53  52 |  51  50  49  48 ||  15  14  13  12 |  11  10   9   8 |   7   6   5   4 |   3   2   1   0 
//        t1   75  74  73  72 |  71  70  69  68 |  67  66  65  64 |  63  62  61  60 ||  27  26  25  24 |  23  22  21  20 |  19  18  17  16 |  15  14  13  12 
//        t2   87  86  85  84 |  83  82  81  80 |  79  78  77  76 |  75  74  73  72 ||  39  38  37  36 |  35  34  33  32 |  31  30  29  28 |  27  26  25  24 
//        t3    0   0   0   0 |  95  94  93  92 |  91  90  89  88 |  87  86  85  84 ||  51  50  49  48 |  47  46  45  44 |  43  42  41  40 |  39  38  37  36 

            t0 = _mm256_shuffle_epi8(t0,p0);
            t1 = _mm256_shuffle_epi8(t1,p1);
            t2 = _mm256_shuffle_epi8(t2,p2);
            t3 = _mm256_shuffle_epi8(t3,p3);

//        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("t3  ");print_vec_char(t3);printf("\n");
//        t0    0   0   0   0 |  59  56  53  50 |  58  55  52  49 |  57  54  51  48 ||   0   0   0   0 |  11   8   5   2 |  10   7   4   1 |   9   6   3   0 
//        t1   71  68  65  62 |  70  67  64  61 |  69  66  63  60 |   0   0   0   0 ||  23  20  17  14 |  22  19  16  13 |  21  18  15  12 |   0   0   0   0 
//        t2   82  79  76  73 |  81  78  75  72 |   0   0   0   0 |  83  80  77  74 ||  34  31  28  25 |  33  30  27  24 |   0   0   0   0 |  35  32  29  26 
//        t3   93  90  87  84 |   0   0   0   0 |  95  92  89  86 |  94  91  88  85 ||  45  42  39  36 |   0   0   0   0 |  47  44  41  38 |  46  43  40  37 

    __m256i u0 = _mm256_blend_epi32(t0,t1,0b10101010);
    __m256i u1 = _mm256_blend_epi32(t2,t3,0b10101010);
    __m256i u2 = _mm256_blend_epi32(t0,t1,0b01010101);
    __m256i u3 = _mm256_blend_epi32(t2,t3,0b01010101);

//        printf("u0  ");print_vec_char(u0);printf("u1  ");print_vec_char(u1);printf("u2  ");print_vec_char(u2);printf("u3  ");print_vec_char(u3);printf("\n");
//        u0   71  68  65  62 |  59  56  53  50 |  69  66  63  60 |  57  54  51  48 ||  23  20  17  14 |  11   8   5   2 |  21  18  15  12 |   9   6   3   0 
//        u1   93  90  87  84 |  81  78  75  72 |  95  92  89  86 |  83  80  77  74 ||  45  42  39  36 |  33  30  27  24 |  47  44  41  38 |  35  32  29  26 
//        u2    0   0   0   0 |  70  67  64  61 |  58  55  52  49 |   0   0   0   0 ||   0   0   0   0 |  22  19  16  13 |  10   7   4   1 |   0   0   0   0 
//        u3   82  79  76  73 |   0   0   0   0 |   0   0   0   0 |  94  91  88  85 ||  34  31  28  25 |   0   0   0   0 |   0   0   0   0 |  46  43  40  37 

            t0 = _mm256_blend_epi32(u0,u1,0b11001100);
            t1 = _mm256_shufps_epi32(u2,u3,0b00111001);
            t2 = _mm256_shufps_epi32(u0,u1,0b01001110);

        printf("t0  ");print_vec_char(t0);printf("t1  ");print_vec_char(t1);printf("t2  ");print_vec_char(t2);printf("\n");
//        t0   93  90  87  84 |  81  78  75  72 |  69  66  63  60 |  57  54  51  48 ||  45  42  39  36 |  33  30  27  24 |  21  18  15  12 |   9   6   3   0 
//        t1   94  91  88  85 |  82  79  76  73 |  70  67  64  61 |  58  55  52  49 ||  46  43  40  37 |  34  31  28  25 |  22  19  16  13 |  10   7   4   1 
//        t2   95  92  89  86 |  83  80  77  74 |  71  68  65  62 |  59  56  53  50 ||  47  44  41  38 |  35  32  29  26 |  23  20  17  14 |  11   8   5   2 

    return 0;
}



int __attribute__ ((noinline)) print_vec_char(__m256i x){
    char v[32];
    _mm256_storeu_si256((__m256i *)v,x);
    printf("%3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi || ",
           v[31],v[30],v[29],v[28],v[27],v[26],v[25],v[24],v[23],v[22],v[21],v[20],v[19],v[18],v[17],v[16]);
    printf("%3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi | %3hhi %3hhi %3hhi %3hhi \n",
           v[15],v[14],v[13],v[12],v[11],v[10],v[9],v[8],v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0]);
    return 0;
}
    4 vmovdqu
    4 vinserti128-load
    4 vpshufb
    5 vpblendd    (vpblendd is much faster than vpblendvb on most cpu architectures)
    2 vshufps