INTEL SIMD:为什么就地乘法这么慢?

INTEL SIMD: why is inplace multiplication so slow?

我已经编写了一些向量方法,这些方法可以就地进行简单的数学运算或复制,并且对就地变体具有相同的惩罚。

最简单的可以归结为如下:

void scale(float* dst, const float* src, int count, float factor)
{
    __m128 factorV = _mm_set1_ps(factorV);

    for(int i = 0; i < count; i+= 4)
    {
        __m128 in = _mm_load_ps(src);
        in = _mm_mul_ps(in, factorV);
        _mm_store_ps(dst, in);

        dst += 4;
        src += 4;
    }
}

测试代码:

for(int i = 0; i < 1000000; i++)
{
    scale(alignedMemPtrDst, alignedMemPtrSrc, 256, randomFloatAbsRange1);
}

在测试的时候,也就是在同一个buffer上反复运行这个函数,发现如果dst和src相同,速度是一样的。如果它们不同,它的速度大约快 70 倍。主要循环在写作时燃烧(即_mm_store_ps)

有趣的是,相同的行为并不适用于加法,即 += 工作得很好,只有 *= 是一个问题..

--

评论里已经回答了。人工测试时异常

您的 factor 是否产生了低于正常的结果?非零但小于 FLT_MIN?如果在此之外有一个循环在同一个块上重复就地循环,数字可能会变得足够小,需要缓慢的 FP 辅助

这是 OP 的问题)。

重复就地乘法使数字越来越小,系数低于 1.0。复制和缩放到不同的缓冲区每次都使用相同的输入。

产生 +-InfNaN 结果不需要额外的时间,但至少在 Intel CPU 上它会逐渐下溢到次正常。这就是 -ffast-math 设置 DAZ/FTZ 的原因之一 - 下溢时清零。


我想我已经读到 AMD 没有对次正规的 FP 辅助微编码处理,但 Intel 有。

英特尔 CPU 上有一个针对 fp_assist.any 的性能计数器,当次正常结果需要额外的微代码 uops 来处理特殊情况时,它会计数。 (我认为它与前端和 OoO exec 一样具有侵入性。不过它肯定很慢。)


(显示 ICC 如何在 main 开始时使用默认的快速数学设置设置 FTZ/DAZ。)