c-用YMM向量寄存器求和vec4 [idx [i]] *标量[i]

我正在尝试优化以下总和{vec4 [indexarray [i]] *标量[i]},其中vec4是float [4],标量是float.使用128位寄存器,可以归结为

sum = _mm_fmadd_ps(
            _mm_loadu_ps(vec4[indexarray[i]]),
            _mm_set_ps1(scalar[i]),
            sum);

如果要在256位寄存器上执行FMA,则必须执行类似的操作

__m256 coef = _mm256_set_m128(
                    _mm_set_ps1(scalar[2 * i + 0]),
                    _mm_set_ps1(scalar[2 * i + 1]));
__m256 vec = _mm256_set_m128(
                    _mm_loadu_ps(vec4[indexarray[2 * i + 0]]),
                    _mm_loadu_ps(vec4[indexarray[2 * i + 1]]));
sum = _mm256_fmadd_ps(vec, coef, sum);

以及随机播放,并在末尾加总上下通道.

从理论上讲,我从单个FMA获得5的延迟(假设Haswell架构),但是从_mm256_set_m128失去2×3的延迟.

有没有一种方法可以使用ymm寄存器来使速度更快,或者单个FMA的所有增益是否会因组合xmm寄存器而引起抵消?

最佳答案

but lose 2×3 in latency from the _mm256_set_m128

不,延迟不是关键路径.这是为FMA准备输入的一部分.对于每个独立的i值进行更多改组的问题是吞吐量.

对于通过总和进行的循环承载的依赖关系链,延迟才是真正重要的,它既是FMA的输入也是输出.

仅依赖于i和内存内容的输入可以通过无序执行跨多个迭代并行处理.

但是,您可能仍然会领先一步,构建256位向量.但是,您编写源代码(_mm256_set_m128不是真正的指令),它可能会成为前端或每1个时钟每秒混洗吞吐量的瓶颈.您希望它先编译为128位负载,然后再编译为vinsertf128 ymm,ymm,[mem],1以插入向量的高半部分. vinsertf128确实需要洗牌.

如果您遇到128位寄存器的延迟瓶颈,那么最好只使用多个累加器,这样(在Haswell上)一次最多可以运行10个FMA:5c延迟* 0.5c吞吐量.最后添加它们. Why does mulss take only 3 cycles on Haswell, different from Agner’s instruction tables?

转载注明原文:c-用YMM向量寄存器求和vec4 [idx [i]] *标量[i] - 代码日志