avx512 以任意步幅跨步聚集
avx512 strided gather with arbitrary stride
我知道在 AVX512 中,您可以执行步幅为 1、2、4、8 的步幅收集。但是,如果我的任意步幅介于 10-1000 之间怎么办?步幅在编译时已知。我明白指令不会成为瓶颈,内存可能会成为瓶颈。 _mm512_set_ps 是最有效的方法吗?
strided gathers with strides of 1,2,4,8
不,没有对此的特殊支持;也许您正在考虑 ARM/ARM64 NEON vld4
4 路去交错?
在 x86 中,您可以使用 1、2、4 或 8 作为 比例因子 作为 vpgatherdd
/ vpgatherdps
的索引向量,但是如果你只想要每个第二个元素,最好手动洗牌(例如 _mm512_permutex2var_ps
从 2 个输入向量中获取交替的浮点数),用一个宽负载获得许多有用的元素,而不是每个元素访问一次缓存。
但在你的情况下,最小步幅为 10,最多 2 个元素将来自相同的 16 x 32 位 512 位向量,并且更宽的步幅甚至每个向量一个都没有。
因此您可以在循环中使用 vpgatherdps
和 _mm512_add_epi32(idx, _mm512_set1_epi32(16 * stride))
。 或者更好的是,只使用固定的索引向量并递增基指针。您可以使用 _mm512_mullo_epi32(_mm512_setr_epi32(0,1,2,3,...,15), _mm512_set1_epi32(stride))
生成该索引向量。由于浮点数为 4 字节宽,因此在收集时使用 4
的比例因子。
即使您需要处理巨大的数组,递增指针而不是向量元素也可以避免对 64 位索引的任何需求,并最大限度地减少向量 uops 的数量。 (在当前 CPU 上使用 512 位向量时很有价值。)
IIRC,Intel 的优化手册中有一节介绍了跨步加载以及手动收集与使用收集指令之间的权衡。向量越宽,收集指令就变得相对越好(2/时钟负载吞吐量,但对于大多数洗牌只有 1/时钟洗牌吞吐量),因此特别是对于 512 位向量,使用向量洗牌可能是一个胜利。
我知道在 AVX512 中,您可以执行步幅为 1、2、4、8 的步幅收集。但是,如果我的任意步幅介于 10-1000 之间怎么办?步幅在编译时已知。我明白指令不会成为瓶颈,内存可能会成为瓶颈。 _mm512_set_ps 是最有效的方法吗?
strided gathers with strides of 1,2,4,8
不,没有对此的特殊支持;也许您正在考虑 ARM/ARM64 NEON vld4
4 路去交错?
在 x86 中,您可以使用 1、2、4 或 8 作为 比例因子 作为 vpgatherdd
/ vpgatherdps
的索引向量,但是如果你只想要每个第二个元素,最好手动洗牌(例如 _mm512_permutex2var_ps
从 2 个输入向量中获取交替的浮点数),用一个宽负载获得许多有用的元素,而不是每个元素访问一次缓存。
但在你的情况下,最小步幅为 10,最多 2 个元素将来自相同的 16 x 32 位 512 位向量,并且更宽的步幅甚至每个向量一个都没有。
因此您可以在循环中使用 vpgatherdps
和 _mm512_add_epi32(idx, _mm512_set1_epi32(16 * stride))
。 或者更好的是,只使用固定的索引向量并递增基指针。您可以使用 _mm512_mullo_epi32(_mm512_setr_epi32(0,1,2,3,...,15), _mm512_set1_epi32(stride))
生成该索引向量。由于浮点数为 4 字节宽,因此在收集时使用 4
的比例因子。
即使您需要处理巨大的数组,递增指针而不是向量元素也可以避免对 64 位索引的任何需求,并最大限度地减少向量 uops 的数量。 (在当前 CPU 上使用 512 位向量时很有价值。)
IIRC,Intel 的优化手册中有一节介绍了跨步加载以及手动收集与使用收集指令之间的权衡。向量越宽,收集指令就变得相对越好(2/时钟负载吞吐量,但对于大多数洗牌只有 1/时钟洗牌吞吐量),因此特别是对于 512 位向量,使用向量洗牌可能是一个胜利。