我写了一些向量方法,这些方法可以进行简单的原位数学运算或复制,并且对原位变量也有相同的惩罚。
最简单的方法可以归结为以下内容:
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);
}
在测试时,即在SAME缓冲区上重复操作此功能时,我发现如果dst和src相同,则速度相同。如果它们不同,则速度要快70倍左右。主要周期在写作(例如_mm_store_ps)上燃烧
有趣的是,相同的行为并不适用于加法运算,即+ =效果很好,只有* =是一个问题。
-
已在评论中回答。在人工测试中是不正常的。
答案 0 :(得分:6)
您的factor
是否产生不正常的结果?非零但小于FLT_MIN
?如果此循环之外存在循环,则该循环会重复就地遍历同一块,数字可能会变得很小,需要缓慢的FP辅助。
(Turns out, yes是OP的问题)。
重复就地乘法会使数字越来越小,并且系数小于1.0。每次复制并缩放到不同的缓冲区都使用相同的输入。
产生+-Inf
或NaN
的结果并不需要花费额外的时间,但是至少在Intel CPU上会逐渐下溢至次正常水平。这是-ffast-math
设置DAZ / FTZ的原因之一-下溢时刷新为零。
我想我已经读过AMD并没有使用FP辅助对次常态进行微编码处理,但是Intel确实如此。
fp_assist.any
在Intel CPU上有一个性能计数器,用于计算次标准结果何时需要额外的微代码来处理特殊情况。 (我认为这与前端和OoO高管一样具有侵入性。不过,它肯定很慢。)
Why denormalized floats are so much slower than other floats, from hardware architecture viewpoint?
Why is icc generating weird assembly for a simple main?(显示ICC如何在main
的开头设置FTZ / DAZ,并使用默认的快速数学设置。)