是否有显示/写入大寄存器的约定,如英特尔 AVX 指令集中可用的那些?
例如,如果最低有效字节为 1,最高有效字节为 20,xmm
寄存器中的其他地方为 0,则字节显示是以下首选(小端):
[1, 0, 0, 0, ..., 0, 20]
或者这是首选:
[20, 0, 0, 0, ..., 0, 1]
同样,当显示由较大数据项组成的此类寄存器时,是否应用相同的规则?例如,要将寄存器显示为 DWORD,我假设每个 DWORD 仍以通常(大端)方式编写,但 DWORD 的顺序是什么:
[0x1, 0x0, ..., 0x14]
对比
[0x14, 0x0, ..., 0x1]
讨论
我认为两个最有希望的答案就是“LSE1 first”(即上面示例中的第一个输出)或“MSE first”(第二个输出)。两者都不取决于平台的字节序,因为实际上一旦在寄存器中数据通常是独立于字节序的(就像对 GP 寄存器或
long
或 int
或 C 中的任何内容的操作都与字节序无关)。字节序出现在寄存器 内存接口(interface)中,在这里我询问寄存器中已有的数据。可能存在其他答案,例如取决于字节顺序的输出(Paul R 的答案可能是一个,但我不知道)。
伦敦证券交易所第一
LSE-first 的一个优势似乎特别适用于逐字节输出:通常字节从 0 到 N 编号,LSB 为 02,所以 LSE-first 输出以递增的索引输出它,就像你输出一个大小为 N 的字节数组。
这在小端架构上也很好,因为输出然后与存储到内存的相同向量的内存表示相匹配。
MSE优先
这里的主要优点似乎是较小元素的输出与较大元素的输出顺序相同(仅具有不同的分组)。例如,对于 MSB 表示法
[0x4, 0x3, 0x2, 0x1]
中的 4 字节向量,字节元素、字和双字元素的输出将是:[0x4, 0x3, 0x2, 0x1]
[ 0x0403, 0x0201 ]
[0x04030201]
本质上,即使从字节输出中,您也可以“读取”字或双字输出,反之亦然,因为字节已经按通常的 MSB 优先顺序进行数字显示。另一方面,LSE-first 的相应输出是:
[0x1, 0x2, 0x3, 0x4]
[ 0x0201 , 0x0403 ]
[0x04030201]
请注意,每一层都相对于其上方的行进行交换,因此读取更大或更小的值要困难得多。您需要更多地依赖于输出最适合您的问题的元素。
这种格式还有一个优点,即在 BE 架构上,输出随后与存储到 memory3 的相同向量的内存表示相匹配。
英特尔在其手册中首先使用 MSE。
1 个最不重要的元素
2 这样的编号不仅仅用于文档目的——它们在架构上是可见的,例如,在随机掩码中。
3 当然,与 LSE-first 在 LE 平台上的相应优势相比,这种优势是微不足道的,因为 BE 在商品 SIMD 硬件中几乎已经死了。
最佳答案
保持一致是最重要的;如果我正在处理已经具有 LSE 优先注释或变量名称的现有代码,我会匹配它。
鉴于选择, 我更喜欢在 评论中使用 MSE-first 表示法,尤其是在设计带有 shuffle 或特别是打包/解包到不同元素大小的东西时。
英特尔不仅在手册中的图表中使用 MSE-first,而且在内部函数/指令的命名中使用 MSE-first,如 pslldq
(字节移位)和 psrlw
(位移): 左位移位/字节移向 MSB 。 LSE 优先思考并不能让您免于精神上的逆转,这意味着您在考虑轮类而不是加载/存储时必须这样做。由于 x86 是小端的,因此您有时无论如何都必须考虑这一点。
在 MSE 优先考虑向量时,请记住内存顺序是从右到左。当您需要考虑从内存块中重叠未对齐的加载时, 您可以按从右到左的顺序绘制内存内容 ,因此您可以查看它的向量长度窗口。
在文本编辑器中,在某些内容的左侧添加新文本并将现有文本移到右侧是没有问题的,因此向注释添加更多元素不是问题。
MSE-first 表示法的两个主要缺点是:
h g f e | d c b a
用于 32 位元素的 AVX 向量),所以我有时只是从右边开始键入 a
、左箭头、 b
、空格、ctrl-左箭头、 c
、空格, ... 或类似的东西。 _mm_set_epi*
使用 MSE 优先顺序。 (使用 _mm_setr_epi*
匹配 LSE-first 评论)。 MSE-first 很好的一个例子是在尝试设计 256b
vpalignr
的车道交叉版本时:请参阅我对该问题的回答How to concatenate two vector efficiently using AVX2? 。这包括 MSE 优先符号中的设计说明。
作为另一个例子,考虑在整个向量上实现可变计数字节移位。您可以制作一个
pshufb
控制向量表,但这会大量浪费缓存占用空间。从内存加载滑动窗口要好得多:/* Example of using MSE notation for memory as well as vectors
// 4-element vectors to keep the design notes compact
// I started by just writing down a couple rows of this, then noticing which way they lined up
<< 3: 00 FF FF FF
<< 1: 02 01 00 FF
0: 03 02 01 00
>> 2: FF FF 03 02
>> 3: FF FF FF 03
>> 4: FF FF FF FF
FF FF FF FF 03 02 01 00 FF FF FF FF
highest address lowest address
*/
#include <immintrin.h>
#include <stdint.h>
// positive counts are right shifts, negative counts are left
// a left-only or right-only implementation would only have one side of the table,
// and only need 32B alignment for the constant in memory to prevent cache-line splits.
__m128i vshift(__m128i v, intptr_t bytes_right)
{ // intptr_t means the caller has to sign-extend it to the width of a pointer, saving a movsx in the non-inline version
// C11 uses _Alignas, C++11 uses alignas
_Alignas(64) static const int32_t shuffles[] = {
-1, -1, -1, -1,
0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
-1, -1, -1, -1
}; // compact but messy with a mix of ordering :/
const char *identity_shuffle = 16 + (const char*)shuffles; // points to the middle 16B
// count &= 0xf; tricky to efficiently limit the count while still allowing >>16 to zero the vector, and to allow negative.
__m128i control = _mm_load_si128((const __m128i*) (identity_shuffle + bytes_right));
return _mm_shuffle_epi8(v, control);
}
对于 MSE-first 来说,这是最坏的情况,因为右移需要从更左边的窗口。在 LSE-first 表示法中,它可能看起来更自然。尽管如此,除非我有一些倒退:P,我认为这表明即使对于您认为棘手的事情,您也可以成功地使用 MSE-first 表示法。它并没有让人感到费神或过于复杂。我刚开始写下随机播放控制向量,然后将它们排列起来。如果我使用
uint8_t shuffles[] = { 0xff, 0xff, ..., 0, 1, 2, ..., 0xff };
,我可以在转换为 C 数组时使它稍微简单一些。我没有测试过这个,只有 that it compiles to one instruction :
vpshufb xmm0, xmm0, xmmword ptr [rdi + vshift.shuffles+16]
ret
当您可以使用位移而不是随机指令来减少端口 5 的压力时,MSE 让您更容易注意到。
psllq xmm, 16
/_mm_slli_epi64(v,16)
将单词元素左移一(在 qword 边界处归零)。或者当您需要移位字节元素,但唯一可用的移位是 16 位或更宽时。每元素最窄的变量移位是 32 位元素 ( vpsllvd
)。当使用更大或更小的粒度混洗或混合时,MSE 可以很容易地获得正确的混洗常数,例如
pshufd
当您可以将单词元素对保持在一起时,或者 pshufb
在整个向量中随机排列单词(因为 pshuflw/hw
是有限的)。_MM_SHUFFLE(d,c,b,a)
也按 MSE 顺序排列。将它写为单个整数的任何其他方式也是如此,例如 C++14 0b11'10'01'00
或 0xE4
(身份洗牌)。使用 LSE-first 表示法将使您的 shuffle 常量相对于您的评论看起来“倒退”。 (除了 pshufb
常量,你可以用 _mm_setr
编写)关于x86 - 显示向量寄存器的约定,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41351087/