Assembly XMM或YMM寄存器中的反向字节顺序?

Assembly XMM或YMM寄存器中的反向字节顺序?,assembly,x86,x86-64,sse,avx,Assembly,X86,X86 64,Sse,Avx,假设我想反转一个非常大的字节数组的字节顺序。我可以使用主寄存器以缓慢的方式完成这项工作,但我希望使用XMM或YMM寄存器来加快速度 是否有办法反转XMM或YMM寄存器中的字节顺序?是的,使用SSSE3\u mm\u shuffle\u epi8或AVX2\u mm256\u shuffle\u epi8在16字节AVX2“通道”内对字节进行无序排列。根据随机播放控制向量,可以交换字节对、反转4字节单位或反转8字节单位。或者反转所有16个字节 但是vpshufb不是车道交叉,所以在AVX512VB

假设我想反转一个非常大的字节数组的字节顺序。我可以使用主寄存器以缓慢的方式完成这项工作,但我希望使用XMM或YMM寄存器来加快速度


是否有办法反转XMM或YMM寄存器中的字节顺序?

是的,使用SSSE3
\u mm\u shuffle\u epi8
或AVX2
\u mm256\u shuffle\u epi8
在16字节AVX2“通道”内对字节进行无序排列。根据随机播放控制向量,可以交换字节对、反转4字节单位或反转8字节单位。或者反转所有16个字节

但是
vpshufb
不是车道交叉,所以在AVX512VBMI
vpermb
之前,不能用一条指令反转32字节
vpshufb ymm
在ymm向量的两个128位通道中执行2次16字节的随机移位

因此,如果要对整个数组进行字节反转,而不是对数组中单个元素的endianness/字节顺序进行反转,则有3个选项:

  • 坚持使用128位向量(简单且可移植,在当前CPU上可能不会较慢)。只需16字节对齐即可获得最佳性能
  • 加载
    vmovdqu
    /
    vinsert128
    ,然后
    vpshufb
    然后存储32字节。(或者进行32字节加载和拆分16字节存储,但这可能没有那么好)。将缓存阻止的字节aarray reverse包含到tmp缓冲区中,以8kiB块的形式提供
    fwrite
  • 使用
    vpermq
    vpshufb
    之前或之后进行通道交换(在AMD上不是很好,在当前Intel上每时钟1次洗牌吞吐量存在瓶颈)。但在冰湖上可能非常好(2个洗牌端口)

vpshufb
在Intel上是一条uop指令,在AMD上是2条,一次处理32字节的数据

对于非常大的输入,在向量化循环之前达到32或64字节的对齐边界可能是值得的,因此没有任何加载/存储跨越缓存线边界。(对于较小的输入,次要的好处不值得额外的开场白/尾声代码和分支。)


但可能更好的方法是在使用16kiB块之前只交换它,这样当下一步读取它时,它在L1d缓存中仍然是热的。这称为缓存阻塞。或者可能使用128kiB块来阻止二级缓存大小

当您从文件中读取数据时,可以进行块交换。e、 g.在内核将数据从pagecache复制到用户空间缓冲区后,以64k或128k的数据块执行
read()
系统调用,并在缓存中仍处于热状态时交换结果。或者使用
mmap
对文件进行内存映射,并从中运行复制和交换循环。(或者对于私有映射,使用就地交换;但这将触发写时复制,因此没有多大好处。Linux上的文件支持mmap不能使用匿名hugepages)

另一种选择是,如果您只读取了几次数据,那么只需动态交换数据;如果以后的使用仍然是内存受限的,或者没有瓶颈就有空间进行shuffle uop,那么它可能不会让它们在运行时慢下来

一个涉及所有数据且仅进行字节交换的过程,其计算强度非常低;当数据在寄存器中时,或者至少当数据在缓存中处于热状态时,您希望对其进行更多操作。但是,如果只交换一次字节,然后多次读取数据,或者以随机访问模式,或者从另一种无法在运行中高效交换的语言(如Python或JavaScript)读取数据,那么一定要执行交换过程

或者,如果您要在其上进行多个没有内存限制的传递,则交换传递很有帮助,并且额外的洗牌会降低以后每次传递的速度。在这种情况下,您确实希望缓存阻止交换,以便稍后的过程的输入在缓存中是热的


标量选项bswap,每个时钟周期最多限制为8个字节,并且每8个字节需要一个单独的加载和存储指令。(
movbe
通过字节交换从内存加载保存了一条指令,但在主流CPU上并没有微融合到一个加载+交换uop中。不过在Silvermont上它是一个uop。)此外,Intel
bswap r64
是2个uop,所以它不是很好

这可能会使现代CPU上的单线程内存带宽因某些循环展开而饱和,但SIMD处理相同数据的总UOP数较少,因此可以让无序执行“看”得更远,并更快地开始处理即将到来的页面的TLB未命中。HW数据预取和TLB预取确实有很大帮助,但对于
memcpy
,使用更广泛的加载/存储通常至少稍微好一点

vpshufb
足够便宜,基本上仍能像
memcpy
一样运行。如果重写到位,效果会更好。)


当然,如果你有任何缓存命中率,即使只是三级缓存,SIMD也会大放异彩。

我无法与传奇人物彼得·科德斯竞争。。。我想展示C语言的实现

下面是使用C内部函数反转字节顺序的示例(可用于对整个数组进行字节反转)

有3个代码示例

  • 使用SSE2指令集
  • 使用SSSE3指令集
  • 使用AVX2指令集
    //使用uint8值0到15初始化XMM寄存器(用于测试):
    __m128i a_F_E_D_C_a_9_8_7_6_5_4_3_2_1_0=_mm_集_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
    //SSE2:
    //优点:无需构建洗牌蒙版(对于非常短的循环非常有效)。
    //////////////////////////////////////////////////////////////////////////
    //uint32的相反顺序:
    __m128i a_3_2_1_0_7_6_4_B_a_9_8_E_D_C=_mm_shuffle_epi32(a_F_E_D_C_B_9_7_6_5_4_3_2_1_0,_mm_shuffle(0,1,2,3));
    //uint16的交换对:
    __m128i a_1_0_3_2_5_4_6_9_8_B_a_D_C_E=_mm_shuflehi_epi16(_mm_shuflelo_epi16)(a_3_2_1_0 7_6_5_4_B_a_9_8_E_D C),_mm_shufleo(2,0,1,0,1),mm