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

时间:2019-05-15 07:28:35

标签: c++ simd intrinsics avx2

我正在尝试优化以下fmap :: (a -> b) -> Maybe a -> Maybe b ,其中sum{vec4[indexarray[i]] * scalar[i]}vec4,而float[4]是浮点数。使用128位寄存器,可以归结为

scalar

如果我想在256位寄存器上执行FMA,则必须做类似的事情

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

随机混洗,并在末尾加总上下通道。

从理论上讲,我从单个FMA中获得5倍的延迟(假设采用Haswell架构),但是从__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); 中获得2倍的延迟。

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

1 个答案:

答案 0 :(得分:3)

  

但由于_mm256_set_m128而失去2x3的延迟

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

对于通过sum进行的循环携带的依赖链来说,延迟才是真正重要的,它是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?