我正在尝试优化以下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寄存器而引起抵消?
答案 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?