我需要做以下两个操作:
float x[4];
float y[16];
// 1-to-4 broadcast
for ( int i = 0; i < 16; ++i )
y[i] = x[i / 4];
// 4-to-1 reduce-add
for ( int i = 0; i < 16; ++i )
x[i / 4] += y[i];
什么是高效的 AVX-512 实施?
对于reduce-add,只需进行in-lane shuffles并添加(vmovshdup
/ vaddps
/ vpermilps imm8
/ vaddps
),就像在x86上做水平浮点向量求和的最快方式一样,以获得每个128位通道中的水平和,然后vpermps
对所需元素进行洗牌至底部。或者vcompressps
使用常量掩码来做同样的事情,可选地使用内存目标。
一旦打包成单个向量,您就有了一个普通的 SIMD 128 位相加。
如果您的数组实际上大于 16,则vpermps
您可以vpermt2ps
从两个源向量中的每一个中获取每个第 4 个元素,而不是让您准备好将+=
部分放入x[]
256 位向量中。(或者再次与另一个 shuffle 组合成 512 位向量,但这可能会成为 SKX 上的 shuffle 吞吐量的瓶颈)。
在 SKX 上,vpermt2ps
只有一个 uop,吞吐量为 1c / 延迟为 3c,因此它的强大效率非常高。在 KNL 上,它的吞吐量为 2c,比 差vpermps
,但也许仍然值得。(KNL 没有 AVX512VL,但如果需要添加x[]
256 位向量,您(或编译器)可以使用 AVX1 vaddps ymm
。)
有关说明表,请参见https://agner.org/optimize/。
对于负载:
这是在循环内完成的,还是重复的?(即你可以在寄存器中保留一个随机控制向量吗?如果是这样,你可以
VBROADCASTF32X4
使用(单个 uop 用于加载端口)进行 128->512 广播。vpermilps zmm,zmm,zmm
在每个 128 位通道内进行通道内随机播放以广播不同的元素。(必须与广播负载分开,因为内存源vpermilps
可以有一个m512
或m32bcst
源。(指令通常有它们的内存广播粒度=它们的元素大小,不幸的是在某些情况下它根本没有用。而且vpermilps
将控制向量作为内存操作数,而不是源数据。)这比因为 shuffle 有 1 个周期延迟而不是 3 个(在 Skylake-avx512 上)略好。vpermps zmm,zmm,zmm
即使在循环之外,加载一个随机控制向量可能仍然是你最好的选择。