提问人:thequestioner 提问时间:10/31/2023 最后编辑:thequestioner 更新时间:11/2/2023 访问量:126
YMM寄存器之间的逻辑转换
Logical shift between YMM registers
问:
我是否可以将一个 2048 位数字加载到 8 个 AVX ymm 寄存器中,并在所有这些寄存器之间左右移动位?
我一次只需要移动 1 位。
我试图在 AVX 上找到准确的信息,但很多时候 xmm/ymm/zmm 和进位之间的交互似乎不清楚。
答:
4赞
harold
10/31/2023
#1
我试图在 AVX 上找到准确的信息,但很多时候 xmm/ymm/zmm 和进位之间的交互似乎不清楚。
这是简单的部分:没有互动。SSE/AVX 算术不涉及标志。有一些特定的指令可以比较/测试向量 () 或向量 ( 等) 中的标量,然后设置标志,但它们在这里并不那么有用。ptest
comiss
一种方法是从数字的顶部而不是底部开始,加载两个略微偏移(大部分重叠,因此其中一个向量与另一个元素相比被一个元素偏移)向量,并使用其中一个“连接和移位”指令(例如)进行左移,从前一个元素移位(通常它不是来自前一个元素, 它来自另一个向量,但这就是为什么我们在单元素偏移处加载第二个向量)而不是零。在 AVX2 中,您可以使用左移、右移和 来模拟这种情况。vpshld
vpor
评论
0赞
thequestioner
10/31/2023
我想知道如果我在 ymm 中将 1 加到 0xff 并滚动到 0,会发生什么。进位是否记录在任何地方?
1赞
harold
10/31/2023
@thequestioner不行,你可以自己计算。此外,例如,如果您使用每个 dword 中的 31 位,则相应的进位会自动出现在每个单独总和的顶部,但这还不足以使多 dword 加法变得容易。无论如何,这里有一些技巧: numberworld.org/y-cruncher/internals/addition.html
1赞
Soonts
11/2/2023
#2
这是可能的,但不是直截了当的。
这是 C++ 中的 AVX2 实现,它在每个寄存器 5 条指令中执行此操作。
#include <immintrin.h>
// Shift AVX vector left by 1 bit
// The flag should contain either 0 or 1 in the lowest int32 lane, higher 96 bits are unused
inline __m256i shiftLeft1( const __m256i src, __m128i& carryFlag )
{
// Shift 64 bit lanes right by 63 bits, i.e. isolate the high bit into low location
__m256i right = _mm256_srli_epi64( src, 63 );
// Cyclic permute across the complete vector
right = _mm256_permute4x64_epi64( right, _MM_SHUFFLE( 2, 1, 0, 3 ) );
// Deal with the carry flags
const __m128i nextFlag = _mm256_castsi256_si128( right );
right = _mm256_blend_epi32( right, _mm256_castsi128_si256( carryFlag ), 1 );
carryFlag = nextFlag;
// Shift 64 bit lanes left by 1 bit
__m256i left = _mm256_slli_epi64( src, 1 );
// Assemble the result
return _mm256_or_si256( left, right );
}
// Shift AVX vector right by 1 bit
// The flag should contain either 0 or 0x80000000 in the highest int32 lane, lower 224 bits are unused
inline __m256i shiftRight1( const __m256i src, __m256i& carryFlag )
{
// Shift 64 bit lanes left by 63 bits, i.e. isolate low bits into high location
__m256i left = _mm256_slli_epi64( src, 63 );
// Cyclic permute across the complete vector
left = _mm256_permute4x64_epi64( left, _MM_SHUFFLE( 0, 3, 2, 1 ) );
// Deal with the carry flags
const __m256i nextFlag = left;
left = _mm256_blend_epi32( left, carryFlag, 0b10000000 );
carryFlag = nextFlag;
// Shift 64 bit lanes right by 1 bit
__m256i right = _mm256_srli_epi64( src, 1 );
// Assemble the result
return _mm256_or_si256( left, right );
}
这 5 条指令中的大多数都非常快,延迟为 1 个周期,但在大多数处理器上需要 3-6 个周期。幸运的是,该指令不依赖于进位标志,它只依赖于输入向量。现代无序处理器应该能够很好地运行该代码。vpermq
vpermq
4 个向量中 1024 位数字的使用示例:
// 1024 bits of data in 4 AVX registers
struct Blob1k
{
__m256i v0, v1, v2, v3;
};
void shiftLeft1( Blob1k& blob )
{
__m128i cf = _mm_setzero_si128();
blob.v0 = shiftLeft1( blob.v0, cf );
blob.v1 = shiftLeft1( blob.v1, cf );
blob.v2 = shiftLeft1( blob.v2, cf );
blob.v3 = shiftLeft1( blob.v3, cf );
}
void shiftRight1( Blob1k& blob )
{
__m256i cf = _mm256_setzero_si256();
blob.v3 = shiftRight1( blob.v3, cf );
blob.v2 = shiftRight1( blob.v2, cf );
blob.v1 = shiftRight1( blob.v1, cf );
blob.v0 = shiftRight1( blob.v0, cf );
}
评论
valignd
q
palignr