如何在AVX2中按位shift/rotate(左右)实现车道交叉
How to implement lane crossing logical bit-wise shift/rotate (left and right) in AVX2
根据 this answer,我创建了以下测试程序:
#include <iso646.h>
#include <immintrin.h>
#include <stdio.h>
#define SHIFT_LEFT( N ) \
\
inline __m256i shift_left_##N ( __m256i A ) { \
\
if ( N == 0 ) return A; \
else if ( N < 16 ) return _mm256_alignr_epi8 ( A, _mm256_permute2x128_si256 ( A, A, _MM_SHUFFLE ( 0, 0, 2, 0 ) ), ( uint8_t ) ( 16 - N ) ); \
else if ( N == 16 ) return _mm256_permute2x128_si256 ( A, A, _MM_SHUFFLE ( 0, 0, 2, 0 ) ); \
else return _mm256_slli_si256 ( _mm256_permute2x128_si256 ( A, A, _MM_SHUFFLE ( 0, 0, 2, 0 ) ), ( uint8_t ) ( N - 16 ) ); \
}
void print ( const size_t n ) {
size_t i = 0x8000000000000000;
while ( i ) {
putchar ( ( int ) ( n & i ) + ( int ) ( 48 ) );
i >>= 1;
putchar ( ( int ) ( n & i ) + ( int ) ( 48 ) );
i >>= 1;
putchar ( ' ' );
}
}
SHIFT_LEFT ( 2 );
int main ( ) {
__m256i a = _mm256_set_epi64x ( 0x00, 0x00, 0x00, 0x03 );
__m256i b = shift_left_2 ( a );
size_t * c = ( size_t * ) &b;
print ( c [ 3 ] ); print ( c [ 2 ] ); print ( c [ 1 ] ); print ( c [ 0 ] ); putchar ( '\n' );
return 0;
}
据我所知,上面的程序没有给出预期的(我的)输出。我对这些功能如何协同工作感到困惑(阅读说明)。是我做错了什么,还是 shift_left() 的实现有误?
EDIT1:我开始意识到(并在评论中确认)这段代码只打算最多移动 32 个字节(并且是字节),所以它不满足我的目标。这就留下了问题,"How to implement lane crossing logical bit-wise shift (left and right) in AVX2"。
EDIT2:快进:与此同时,我不太清楚它是如何工作的,并且已经编写了我需要的代码。我已经发布了代码(移动和旋转)并接受它作为答案。
可能不是您所期待的那种答案。但这里有一个相当有效的解决方案,实际上适用于 run-time 偏移量。
费用为:
- 预处理: ~12 - 14 条指令
- 旋转: 5 条指令
- 移位: 6 条指令
为了移动或旋转任何东西,您必须首先预处理移动量。一旦你有了它,你就可以有效地执行 shifts/rotations.
由于预处理步骤非常昂贵,此解决方案利用一个对象来保存预处理后的移位量,以便在移位相同的量时可以多次重复使用。
为了提高效率,对象应该在与执行移位的代码相同范围内的堆栈上。这允许编译器将对象的所有字段提升到寄存器中。此外,建议 force-inline class.
的所有方法
#include <stdint.h>
#include <immintrin.h>
class LeftShifter_AVX2{
public:
LeftShifter_AVX2(uint32_t bits){
// Precompute all the necessary values.
permL = _mm256_sub_epi32(
_mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7),
_mm256_set1_epi32(bits / 32)
);
permR = _mm256_sub_epi32(permL, _mm256_set1_epi32(1));
bits %= 32;
shiftL = _mm_cvtsi32_si128(bits);
shiftR = _mm_cvtsi32_si128(32 - bits);
__m256i maskL = _mm256_cmpgt_epi32(_mm256_setzero_si256(), permL);
__m256i maskR = _mm256_cmpgt_epi32(_mm256_setzero_si256(), permR);
mask = _mm256_or_si256(maskL, _mm256_srl_epi32(maskR, shiftR));
}
__m256i rotate(__m256i x) const{
__m256i L = _mm256_permutevar8x32_epi32(x, permL);
__m256i R = _mm256_permutevar8x32_epi32(x, permR);
L = _mm256_sll_epi32(L, shiftL);
R = _mm256_srl_epi32(R, shiftR);
return _mm256_or_si256(L, R);
}
__m256i shift(__m256i x) const{
return _mm256_andnot_si256(mask, rotate(x));
}
private:
__m256i permL;
__m256i permR;
__m128i shiftL;
__m128i shiftR;
__m256i mask;
};
测试程序:
#include <iostream>
using namespace std;
void print_u8(__m256i x){
union{
__m256i v;
uint8_t s[32];
};
v = x;
for (int c = 0; c < 32; c++){
cout << (int)s[c] << " ";
}
cout << endl;
}
int main(){
union{
__m256i x;
char buffer[32];
};
for (int c = 0; c < 32; c++){
buffer[c] = (char)c;
}
print_u8(x);
print_u8(LeftShifter_AVX2(0).shift(x));
print_u8(LeftShifter_AVX2(8).shift(x));
print_u8(LeftShifter_AVX2(32).shift(x));
print_u8(LeftShifter_AVX2(40).shift(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
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
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
0 0 0 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
0 0 0 0 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
Right-shift 非常相似。我将把它留作 reader.
的练习
以下代码在AVX2中实现lane-crossing逻辑bit-wiseshift/rotate(左右):
// Prototypes...
__m256i _mm256_sli_si256 ( __m256i, int );
__m256i _mm256_sri_si256 ( __m256i, int );
__m256i _mm256_rli_si256 ( __m256i, int );
__m256i _mm256_rri_si256 ( __m256i, int );
// Implementations...
__m256i left_shift_000_063 ( __m256i a, int n ) { // 6
return _mm256_or_si256 ( _mm256_slli_epi64 ( a, n ), _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_permute4x64_epi64 ( _mm256_srli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 2, 1, 0, 0 ) ), _MM_SHUFFLE ( 3, 3, 3, 0 ) ) );
}
__m256i left_shift_064_127 ( __m256i a, int n ) { // 7
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 2, 1, 0, 0 ) );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 0, 0 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 3, 3, 3, 0 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 3, 3, 0, 0 ) ); // 6
return _mm256_or_si256 ( f, g );
}
__m256i left_shift_128_191 ( __m256i a, int n ) { // 7
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 1, 0, 0, 0 ) );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 0, 0 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 3, 3, 0, 0 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 3, 0, 0, 0 ) );
return _mm256_or_si256 ( f, g );
}
__m256i left_shift_192_255 ( __m256i a, int n ) { // 5
return _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_slli_epi64 ( _mm256_permute4x64_epi64 ( a, _MM_SHUFFLE ( 0, 0, 0, 0 ) ), n ), _MM_SHUFFLE ( 3, 0, 0, 0 ) );
}
__m256i _mm256_sli_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? left_shift_000_063 ( a, n ) : left_shift_064_127 ( a, n % 64 );
else return n < 192 ? left_shift_128_191 ( a, n % 64 ) : left_shift_192_255 ( a, n % 64 );
}
__m256i right_shift_000_063 ( __m256i a, int n ) { // 6
return _mm256_or_si256 ( _mm256_srli_epi64 ( a, n ), _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_permute4x64_epi64 ( _mm256_slli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 0, 3, 2, 1 ) ), _MM_SHUFFLE ( 0, 3, 3, 3 ) ) );
}
__m256i right_shift_064_127 ( __m256i a, int n ) { // 7
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 3, 3, 2, 1 ) );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 3, 3, 3, 2 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 0, 3, 3, 3 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 0, 0, 3, 3 ) );
return _mm256_or_si256 ( f, g );
}
__m256i right_shift_128_191 ( __m256i a, int n ) { // 7
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 3, 2, 3, 2 ) );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 3, 2, 1, 3 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 0, 0, 3, 3 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 0, 0, 0, 3 ) );
return _mm256_or_si256 ( f, g );
}
__m256i right_shift_192_255 ( __m256i a, int n ) { // 5
return _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_srli_epi64 ( _mm256_permute4x64_epi64 ( a, _MM_SHUFFLE ( 0, 0, 0, 3 ) ), n ), _MM_SHUFFLE ( 0, 0, 0, 3 ) );
}
__m256i _mm256_sri_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? right_shift_000_063 ( a, n ) : right_shift_064_127 ( a, n % 64 );
else return n < 192 ? right_shift_128_191 ( a, n % 64 ) : right_shift_192_255 ( a, n % 64 );
}
__m256i left_rotate_000_063 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_slli_epi64 ( a, n ), _mm256_permute4x64_epi64 ( _mm256_srli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 2, 1, 0, 3 ) ) );
}
__m256i left_rotate_064_127 ( __m256i a, int n ) { // 6
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 2, 1, 0, 3 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
return _mm256_or_si256 ( d, e );
}
__m256i left_rotate_128_191 ( __m256i a, int n ) { // 6
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 0, 3, 2, 1 ) );
return _mm256_or_si256 ( d, e );
}
__m256i left_rotate_192_255 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_srli_epi64 ( a, 64 - n ), _mm256_permute4x64_epi64 ( _mm256_slli_epi64 ( a, n ), _MM_SHUFFLE ( 0, 3, 2, 1 ) ) );
}
__m256i _mm256_rli_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? left_rotate_000_063 ( a, n ) : left_rotate_064_127 ( a, n % 64 );
else return n < 192 ? left_rotate_128_191 ( a, n % 64 ) : left_rotate_192_255 ( a, n % 64 );
}
__m256i right_rotate_000_063 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_srli_epi64 ( a, n ), _mm256_permute4x64_epi64 ( _mm256_slli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 0, 3, 2, 1 ) ) );
}
__m256i right_rotate_064_127 ( __m256i a, int n ) { // 6
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 0, 3, 2, 1 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
return _mm256_or_si256 ( d, e );
}
__m256i right_rotate_128_191 ( __m256i a, int n ) { // 6
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 2, 1, 0, 3 ) );
return _mm256_or_si256 ( d, e );
}
__m256i right_rotate_192_255 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_slli_epi64 ( a, 64 - n ), _mm256_permute4x64_epi64 ( _mm256_srli_epi64 ( a, n ), _MM_SHUFFLE ( 2, 1, 0, 3 ) ) );
}
__m256i _mm256_rri_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? right_rotate_000_063 ( a, n ) : right_rotate_064_127 ( a, n % 64 );
else return n < 192 ? right_rotate_128_191 ( a, n % 64 ) : right_rotate_192_255 ( a, n % 64 );
}
我已经尝试使 _mm256_permute4x64_epi64 操作(无论如何必须有两个)部分重叠,这应该将整体延迟保持在最低水平。
评论者提供的大部分建议和/或线索都有助于将代码组合在一起,感谢这些。显然,欢迎提出改进和/或任何其他意见。
我认为 Mystical 的回答很有趣,但是太复杂了,无法有效地用于广义 shifting/rotating 使用 f.e。在图书馆。
根据 this answer,我创建了以下测试程序:
#include <iso646.h>
#include <immintrin.h>
#include <stdio.h>
#define SHIFT_LEFT( N ) \
\
inline __m256i shift_left_##N ( __m256i A ) { \
\
if ( N == 0 ) return A; \
else if ( N < 16 ) return _mm256_alignr_epi8 ( A, _mm256_permute2x128_si256 ( A, A, _MM_SHUFFLE ( 0, 0, 2, 0 ) ), ( uint8_t ) ( 16 - N ) ); \
else if ( N == 16 ) return _mm256_permute2x128_si256 ( A, A, _MM_SHUFFLE ( 0, 0, 2, 0 ) ); \
else return _mm256_slli_si256 ( _mm256_permute2x128_si256 ( A, A, _MM_SHUFFLE ( 0, 0, 2, 0 ) ), ( uint8_t ) ( N - 16 ) ); \
}
void print ( const size_t n ) {
size_t i = 0x8000000000000000;
while ( i ) {
putchar ( ( int ) ( n & i ) + ( int ) ( 48 ) );
i >>= 1;
putchar ( ( int ) ( n & i ) + ( int ) ( 48 ) );
i >>= 1;
putchar ( ' ' );
}
}
SHIFT_LEFT ( 2 );
int main ( ) {
__m256i a = _mm256_set_epi64x ( 0x00, 0x00, 0x00, 0x03 );
__m256i b = shift_left_2 ( a );
size_t * c = ( size_t * ) &b;
print ( c [ 3 ] ); print ( c [ 2 ] ); print ( c [ 1 ] ); print ( c [ 0 ] ); putchar ( '\n' );
return 0;
}
据我所知,上面的程序没有给出预期的(我的)输出。我对这些功能如何协同工作感到困惑(阅读说明)。是我做错了什么,还是 shift_left() 的实现有误?
EDIT1:我开始意识到(并在评论中确认)这段代码只打算最多移动 32 个字节(并且是字节),所以它不满足我的目标。这就留下了问题,"How to implement lane crossing logical bit-wise shift (left and right) in AVX2"。
EDIT2:快进:与此同时,我不太清楚它是如何工作的,并且已经编写了我需要的代码。我已经发布了代码(移动和旋转)并接受它作为答案。
可能不是您所期待的那种答案。但这里有一个相当有效的解决方案,实际上适用于 run-time 偏移量。
费用为:
- 预处理: ~12 - 14 条指令
- 旋转: 5 条指令
- 移位: 6 条指令
为了移动或旋转任何东西,您必须首先预处理移动量。一旦你有了它,你就可以有效地执行 shifts/rotations.
由于预处理步骤非常昂贵,此解决方案利用一个对象来保存预处理后的移位量,以便在移位相同的量时可以多次重复使用。
为了提高效率,对象应该在与执行移位的代码相同范围内的堆栈上。这允许编译器将对象的所有字段提升到寄存器中。此外,建议 force-inline class.
的所有方法#include <stdint.h>
#include <immintrin.h>
class LeftShifter_AVX2{
public:
LeftShifter_AVX2(uint32_t bits){
// Precompute all the necessary values.
permL = _mm256_sub_epi32(
_mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7),
_mm256_set1_epi32(bits / 32)
);
permR = _mm256_sub_epi32(permL, _mm256_set1_epi32(1));
bits %= 32;
shiftL = _mm_cvtsi32_si128(bits);
shiftR = _mm_cvtsi32_si128(32 - bits);
__m256i maskL = _mm256_cmpgt_epi32(_mm256_setzero_si256(), permL);
__m256i maskR = _mm256_cmpgt_epi32(_mm256_setzero_si256(), permR);
mask = _mm256_or_si256(maskL, _mm256_srl_epi32(maskR, shiftR));
}
__m256i rotate(__m256i x) const{
__m256i L = _mm256_permutevar8x32_epi32(x, permL);
__m256i R = _mm256_permutevar8x32_epi32(x, permR);
L = _mm256_sll_epi32(L, shiftL);
R = _mm256_srl_epi32(R, shiftR);
return _mm256_or_si256(L, R);
}
__m256i shift(__m256i x) const{
return _mm256_andnot_si256(mask, rotate(x));
}
private:
__m256i permL;
__m256i permR;
__m128i shiftL;
__m128i shiftR;
__m256i mask;
};
测试程序:
#include <iostream>
using namespace std;
void print_u8(__m256i x){
union{
__m256i v;
uint8_t s[32];
};
v = x;
for (int c = 0; c < 32; c++){
cout << (int)s[c] << " ";
}
cout << endl;
}
int main(){
union{
__m256i x;
char buffer[32];
};
for (int c = 0; c < 32; c++){
buffer[c] = (char)c;
}
print_u8(x);
print_u8(LeftShifter_AVX2(0).shift(x));
print_u8(LeftShifter_AVX2(8).shift(x));
print_u8(LeftShifter_AVX2(32).shift(x));
print_u8(LeftShifter_AVX2(40).shift(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
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
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
0 0 0 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
0 0 0 0 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
Right-shift 非常相似。我将把它留作 reader.
的练习以下代码在AVX2中实现lane-crossing逻辑bit-wiseshift/rotate(左右):
// Prototypes...
__m256i _mm256_sli_si256 ( __m256i, int );
__m256i _mm256_sri_si256 ( __m256i, int );
__m256i _mm256_rli_si256 ( __m256i, int );
__m256i _mm256_rri_si256 ( __m256i, int );
// Implementations...
__m256i left_shift_000_063 ( __m256i a, int n ) { // 6
return _mm256_or_si256 ( _mm256_slli_epi64 ( a, n ), _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_permute4x64_epi64 ( _mm256_srli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 2, 1, 0, 0 ) ), _MM_SHUFFLE ( 3, 3, 3, 0 ) ) );
}
__m256i left_shift_064_127 ( __m256i a, int n ) { // 7
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 2, 1, 0, 0 ) );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 0, 0 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 3, 3, 3, 0 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 3, 3, 0, 0 ) ); // 6
return _mm256_or_si256 ( f, g );
}
__m256i left_shift_128_191 ( __m256i a, int n ) { // 7
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 1, 0, 0, 0 ) );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 0, 0 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 3, 3, 0, 0 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 3, 0, 0, 0 ) );
return _mm256_or_si256 ( f, g );
}
__m256i left_shift_192_255 ( __m256i a, int n ) { // 5
return _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_slli_epi64 ( _mm256_permute4x64_epi64 ( a, _MM_SHUFFLE ( 0, 0, 0, 0 ) ), n ), _MM_SHUFFLE ( 3, 0, 0, 0 ) );
}
__m256i _mm256_sli_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? left_shift_000_063 ( a, n ) : left_shift_064_127 ( a, n % 64 );
else return n < 192 ? left_shift_128_191 ( a, n % 64 ) : left_shift_192_255 ( a, n % 64 );
}
__m256i right_shift_000_063 ( __m256i a, int n ) { // 6
return _mm256_or_si256 ( _mm256_srli_epi64 ( a, n ), _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_permute4x64_epi64 ( _mm256_slli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 0, 3, 2, 1 ) ), _MM_SHUFFLE ( 0, 3, 3, 3 ) ) );
}
__m256i right_shift_064_127 ( __m256i a, int n ) { // 7
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 3, 3, 2, 1 ) );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 3, 3, 3, 2 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 0, 3, 3, 3 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 0, 0, 3, 3 ) );
return _mm256_or_si256 ( f, g );
}
__m256i right_shift_128_191 ( __m256i a, int n ) { // 7
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 3, 2, 3, 2 ) );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 3, 2, 1, 3 ) );
__m256i f = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), d, _MM_SHUFFLE ( 0, 0, 3, 3 ) );
__m256i g = _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), e, _MM_SHUFFLE ( 0, 0, 0, 3 ) );
return _mm256_or_si256 ( f, g );
}
__m256i right_shift_192_255 ( __m256i a, int n ) { // 5
return _mm256_blend_epi32 ( _mm256_setzero_si256 ( ), _mm256_srli_epi64 ( _mm256_permute4x64_epi64 ( a, _MM_SHUFFLE ( 0, 0, 0, 3 ) ), n ), _MM_SHUFFLE ( 0, 0, 0, 3 ) );
}
__m256i _mm256_sri_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? right_shift_000_063 ( a, n ) : right_shift_064_127 ( a, n % 64 );
else return n < 192 ? right_shift_128_191 ( a, n % 64 ) : right_shift_192_255 ( a, n % 64 );
}
__m256i left_rotate_000_063 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_slli_epi64 ( a, n ), _mm256_permute4x64_epi64 ( _mm256_srli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 2, 1, 0, 3 ) ) );
}
__m256i left_rotate_064_127 ( __m256i a, int n ) { // 6
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 2, 1, 0, 3 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
return _mm256_or_si256 ( d, e );
}
__m256i left_rotate_128_191 ( __m256i a, int n ) { // 6
__m256i b = _mm256_slli_epi64 ( a, n );
__m256i c = _mm256_srli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 0, 3, 2, 1 ) );
return _mm256_or_si256 ( d, e );
}
__m256i left_rotate_192_255 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_srli_epi64 ( a, 64 - n ), _mm256_permute4x64_epi64 ( _mm256_slli_epi64 ( a, n ), _MM_SHUFFLE ( 0, 3, 2, 1 ) ) );
}
__m256i _mm256_rli_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? left_rotate_000_063 ( a, n ) : left_rotate_064_127 ( a, n % 64 );
else return n < 192 ? left_rotate_128_191 ( a, n % 64 ) : left_rotate_192_255 ( a, n % 64 );
}
__m256i right_rotate_000_063 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_srli_epi64 ( a, n ), _mm256_permute4x64_epi64 ( _mm256_slli_epi64 ( a, 64 - n ), _MM_SHUFFLE ( 0, 3, 2, 1 ) ) );
}
__m256i right_rotate_064_127 ( __m256i a, int n ) { // 6
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 0, 3, 2, 1 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
return _mm256_or_si256 ( d, e );
}
__m256i right_rotate_128_191 ( __m256i a, int n ) { // 6
__m256i b = _mm256_srli_epi64 ( a, n );
__m256i c = _mm256_slli_epi64 ( a, 64 - n );
__m256i d = _mm256_permute4x64_epi64 ( b, _MM_SHUFFLE ( 1, 0, 3, 2 ) );
__m256i e = _mm256_permute4x64_epi64 ( c, _MM_SHUFFLE ( 2, 1, 0, 3 ) );
return _mm256_or_si256 ( d, e );
}
__m256i right_rotate_192_255 ( __m256i a, int n ) { // 5
return _mm256_or_si256 ( _mm256_slli_epi64 ( a, 64 - n ), _mm256_permute4x64_epi64 ( _mm256_srli_epi64 ( a, n ), _MM_SHUFFLE ( 2, 1, 0, 3 ) ) );
}
__m256i _mm256_rri_si256 ( __m256i a, int n ) {
if ( n < 128 ) return n < 64 ? right_rotate_000_063 ( a, n ) : right_rotate_064_127 ( a, n % 64 );
else return n < 192 ? right_rotate_128_191 ( a, n % 64 ) : right_rotate_192_255 ( a, n % 64 );
}
我已经尝试使 _mm256_permute4x64_epi64 操作(无论如何必须有两个)部分重叠,这应该将整体延迟保持在最低水平。
评论者提供的大部分建议和/或线索都有助于将代码组合在一起,感谢这些。显然,欢迎提出改进和/或任何其他意见。
我认为 Mystical 的回答很有趣,但是太复杂了,无法有效地用于广义 shifting/rotating 使用 f.e。在图书馆。