为什么将0.1f更改为0会将性能降低10倍?

为什么这段代码

常量浮点x[16]={1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,
1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6};
常量浮点z[16]={1.123,1.234,1.345,156.467,1.578,1.689,1.790,1.812,
1.923, 2.034, 2.145, 2.256, 2.367, 2.478, 2.589, 2.690};
浮动y[16];
对于(int i=0;i<16;i++)
{
y[i]=x[i];
}
对于(int j=0;j<9000000;j++)
{
对于(int i=0;i<16;i++)
{
y[i]*=x[i];
y[i]/=z[i];
y[i]=y[i]+0.1f;/&lt–
y[i]=y[i]-0.1f;/&lt–
}
}

运行速度比以下位快10倍以上(除非另有说明,否则相同)

常量浮点x[16]={1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,
1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6};
常量浮点z[16]={1.123,1.234,1.345,156.467,1.578,1.689,1.790,1.812,
1.923, 2.034, 2.145, 2.256, 2.367, 2.478, 2.589, 2.690};
浮动y[16];
对于(int i=0;i<16;i++)
{
y[i]=x[i];
}
对于(int j=0;j<9000000;j++)
{
对于(int i=0;i<16;i++)
{
y[i]*=x[i];
y[i]/=z[i];
y[i]=y[i]+0;//&lt–
y[i]=y[i]-0;//&lt–
}
}

使用Visual Studio 2010 SP1编译时。
优化级别为-02,启用了sse2
我还没有用其他编译器测试过

欢迎来到非规范化浮点的世界它们会对性能造成严重破坏

非正规(或次正规)数是一种从浮点表示中获取非常接近零的额外值的技巧。非规范化浮点运算的速度比规范化浮点运算慢几十到几百倍。这是因为许多处理器不能直接处理它们,必须使用微码捕获并解决它们

如果在10000次迭代后打印出数字,您将看到它们已收敛到不同的值,具体取决于使用的是0还是0.1

以下是在x64上编译的测试代码:

int main(){
双启动=omp_get_wtime();
常量浮点x[16]={1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0,2.1,2.2,2.3,2.4,2.5,2.6};
常量浮点z[16]={1.123,1.234,1.345156.467,1.578,1.689,1.790,1.812,1.923,2.034,2.145,2.256,2.367,2.478,2.589,2.690};
浮动y[16];
对于(int i=0;i<16;i++)
{
y[i]=x[i];
}
对于(int j=0;j<9000000;j++)
{
对于(int i=0;i<16;i++)
{
y[i]*=x[i];
y[i]/=z[i];
#ifdef浮动
y[i]=y[i]+0.1f;
y[i]=y[i]-0.1f;
#否则
y[i]=y[i]+0;
y[i]=y[i]-0;
#恩迪夫
如果(j>10000)
可以;
}
如果(j>10000)
库特<<endl;
}
双端=omp_get_wtime();
cout<<end-start<<endl;
系统(“暂停”);
返回0;
}

输出:

定义浮动
1.78814e-007 1.3411e-007 1.04308e-007 0 7.45058e-008 6.70552e-008 6.70552e-008 5.58794e-007 3.05474e-007 2.16067e-007 1.71363e-007 1.49012e-007 1.2666e-007 1.11759e-007 1.04308e-007 1.04308e-007
1.78814e-007 1.3411e-007 1.04308e-007 0 7.45058e-008 6.70552e-008 6.70552e-008 5.58794e-007 3.05474e-007 2.16067e-007 1.71363e-007 1.49012e-007 1.2666e-007 1.11759e-007 1.04308e-007 1.04308e-007
//#定义浮动
6.305844E-044 3.92364e-044 3.08286e-044 0 1.82169e-044 1.54143e-044 2.10195e-044 2.46842e-029 7.56701e-044 4 4.06377e-044 3.92364e-044 3.2229E-044 3.08286e-044 2.66247e-044 2.248E-044
6.30584e-044 3.92364e-044 3.08286e-044 0 1.82169e-044 1.54143e-044 2.10195e-044 2.45208e-029 7.56701e-044 4 4.06377e-044 3.92364e-044 3.2229E-044 3.08286e-044 2.66247e-044 2.247E-044 2.248E-044

请注意,在第二次运行中,数字非常接近于零

非规范化数字通常很少见,因此大多数处理器不会尝试有效地处理它们


为了证明这一切都与非规范化数字有关,如果我们通过将此添加到代码开头将非规范化数字刷新为零

\u MM\u SET\u FLUSH\u ZERO\u模式(\u MM\u FLUSH\u ZERO\u ON);

然后0的版本不再慢10倍,实际上会更快。(这要求在编译代码时启用SSE。)

这意味着我们不使用这些奇怪的低精度几乎为零值,而是取而代之的四舍五入到零

定时:核心i7 [email protected] GHz:

//不要将非规范化刷新为零。
0.1f:0.564067
0   : 26.7669
//将非规范化刷新为零。
0.1f:0.587117
0   : 0.341406

最后,这实际上与它是整数还是浮点无关。00.1f被转换/存储到两个循环之外的寄存器中。因此,这对性能没有影响

发表评论