如何使用 AVX512 转置 8x8 int64 矩阵

How to transpose a 8x8 int64 matrix with AVX512

提问人:Serge Rogatch 提问时间:9/30/2023 更新时间:9/30/2023 访问量:132

问:

考虑 8 个包含矩阵行的 AVX512 寄存器,以便每个 64 位通道都是 8x8 矩阵的一个单元。如何在 C/C++ 中转置这样的矩阵?

到目前为止,我尝试过:8 个内在函数。它非常慢,并且大量使用缓存,因此其他 CPU 内核无法获得足够的 L3 带宽。_mm512_i32scatter_epi64()

C++ 矩阵 转置 SIMD AVX512

评论

0赞 Serge Rogatch 9/30/2023
@harold你的意思是内在的还是别的什么?_mm512_permutex2var_epi64()
0赞 harold 9/30/2023
一堆是的。这应该可以通过 3 轮“象限交换”来实现,即交换 8x8 矩阵的 4x4 非对角线象限,然后在每个 4x4 块内交换 2x2 象限,然后交换每个 2x2 块的对角线外条目
1赞 harold 9/30/2023
我认为总共需要 24 次洗牌,这并不好。.也许有更好的方法?
1赞 jpa 9/30/2023
相关新闻: stackoverflow.com/questions/29519222/... 和 stackoverflow.com/questions/25622745/...
0赞 Peter Cordes 10/1/2023
大量使用缓存,使其他 CPU 内核无法获得足够的 L3 带宽。-什么?一个 8x8 i64 矩阵适合 512 字节;这是 8 行缓存。如何访问它们并不重要,无论是 1 个存储未命中还是 8 个存储未命中,单个内核每个缓存行只有一个未完成到 L3 的离核 RFO(读取所有权)。除非您谈论的是同一物理内核上的另一个逻辑内核缺乏加载/存储 uop 和缓存吞吐量?(但是 scatter 比 gathers 慢,而且 store 的吞吐量通常比 load 差,所以我仍然不会使用 scatter。

答:

1赞 jpa 9/30/2023 #1

如果这些值最初位于 RAM 中,请改为尝试。同时从缓存中读取比写入缓存时停滞更少。_mm512_i32gather_epi64

如果目标是内存,则将零虚拟写入缓存行对齐的目标区域可以确保指令不会因等待缓存而停滞。使用散点指令时,CPU 不知道整个目标区域将被覆盖,因此它必须等待加载先前的值。

如果这些值已经在寄存器中,并且目标是将它们保留在寄存器中,_mm512_permutex2var_pd似乎是最有用的指令。它允许从两个来源中按索引选择单个元素。但是,由于值分布在如此多的寄存器上,因此将它们洗牌到位将需要执行大量操作。

我将标记矩阵行 A-H 和列 0-7。最初,每个寄存器将包含一行,我将标记该行,例如。目标是让每个寄存器包含一列,标记为例如。A0-7A-H0

A0 .. A7
..    ..
H0 .. H7

我发现的最佳序列仍然是 24 次洗牌:

  1. 以 4 个块对寄存器对进行 8 次洗牌:

    • 合并和A0-7B0-7A-B0-3
    • 合并和A0-7B0-7A-B4-7
    • ...
    • 合并和G0-7H0-7G-H4-7
  2. 以 2 个块进行 8 次洗牌:

    • 合并和A-B0-3C-D0-3A-D0-1
    • 合并和A-B0-3C-D0-3A-D2-3
    • ...
    • 合并和E-F4-7G-H4-7E-H6-7
  3. 以 1 个块进行 8 次洗牌:

    • 合并和A-D0-1E-H0-1A-H0
    • 合并和A-D0-1E-H0-1A-H1
    • ...
    • 合并和A-D6-7E-H6-7A-H7

我认为这是仅在寄存器中操作时可能出现的最小洗牌次数。每个 AVX512 指令最多将两个寄存器作为输入,此序列充分利用输入和输出从 8 个源寄存器收集数据。

评论

0赞 Peter Cordes 9/30/2023
事实上,收集指令比分散指令快得多(尤其是在英特尔;Zen 4 的聚集速度相当慢,散射也更差)。uops.info - Ice Lake 每 5 个周期有 1 个吞吐量(8 个 32 位索引,8 个 64 位数据)。Alder Lake / Sapphire Rapids 将其提高到 3 个周期。但是更多的 uops 和 7 个周期的吞吐量。(订单对商店很重要,而不是对货物很重要。vpgatherdq zmmvpgatherdq zmm
0赞 Peter Cordes 9/30/2023
但这些可能仍然比普通加载(可能在 256 位半中开始洗牌)和寄存器洗牌更糟糕,因为 AVX-512 具有非常好的洗牌功能,就像您提到的,从 2 个源向量(整数。英特尔的指南仅列出 )。1/时钟吞吐量,用于 Intel 和 Zen 4,宽度。vinsert...vpermt2q_mm512_permutex2var_epi64_mm512_mask_permutex2var_epi64__m512i
0赞 Peter Cordes 9/30/2023
Zen 4 对 256 位向量具有 2/时钟吞吐量,如果您要存储回内存,使用较小的向量可能意味着更少的总洗牌。如果计算 256 位随机播放操作的成本,则总随机次数更少,其中一次 512 位随机播放计为 2 次。因此,16x 256 位以 2 个块和 1 个块的形式随机播放,跳过 4 个步骤的块。vpermt2q
0赞 Peter Cordes 9/30/2023
可能至少一个块大小的随机播放不需要完整的,而是可以通过内通道(在 C++ 中往返)或立即交错 128 位通道来实现。不过,这些仍然是仅在英特尔上的端口 5。 可以在 Intel Ice Lake 及更高版本上运行 2/clock,但是当任何 ZMM uops 处于运行状态时,端口 1 上的矢量 ALU 都会关闭,包括端口 1 上的额外随机播放单元,它可以处理部分但不是全部随机播放。因此,我们可以节省向量常量和 reg-copy 指令。vpermt2qvshufpd_mm512_cast...__m512d__m512ivshuff64x2vshufpd ymm