代码见**末尾的附文。
在一台Intel CPU的Linux服务器上运行结果如下(编译命令:gcc -O3 -o test main.c)
A: ret=0.00000000000000000000000000000000000000000007006492, cost=695ms
B: ret=0.10000066459178924560546875000000000000000000000000, cost=41ms
在一台Intel CPU的Windows电脑上运行结果如下(编译命令:gcc -O3 -o test main.c)
A: ret=0.00000000000000000000000000000000000000000007006492, cost=2241ms
B: ret=0.10000046342611313000000000000000000000000000000000, cost=58ms
同一台电脑用VS 2017编译运行结果如下(命令行工程,默认优化等级)
A: ret=0.00000000000000000000000000000000000000000007006492, cost=718ms
B: ret=0.10000066459178924560546875000000000000000000000000, cost=89ms
同样这个二进制文件,放在AMD CPU windows电脑上运行,时间是接近的。
同样的C代码,用ARM GCC编译,ARM处理器上运行时间是接近的。
代码是一样的,只是一个浮点数的运算值不一样而已,导致运算耗时差了几十倍……
经过初步分析和讨论,比较大可能是Intel处理器在处理往0方向下溢时异常引起的,不知道
这个问题有没有谁之前有遇到过?有没有办法可以把CPU设置成像ARM和AMD一样的方式?
不然这个问题只能通过代码去规避了。这个问题隐藏在一大坨代码里,让我排查了一天,
希望后面的同学不要再次踩这个坑。我的精度是足够的,也有办法绕过这个问题,只是我
觉得这个问题比较有意思,想看看谁知道这里面的细节,跟我解释清楚一下,不然对我来说
它就是一个悬案。主要是AMD和ARM CPU都是一致的,Intel CPU搞成这样很不应该。不知道
是我没用对,还是它的问题。以前做浮点运算只会留意不要除0(或者除很小的数),或者
乘法时不要乘到无穷大,或者乘到很小会有精度损失,但从来没有想过乘以一个很小的数,
会导致运行效率急剧下降,也从来没有听谁说过有这样的事情。
附测试代码如下,希望知道这个问题原因的同学帮忙解惑一下:
//Created by computer00
//Date: 2020.06.01
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#ifdef _WIN32
#include <time.h>
#else
#include <sys/time.h>
#endif
#define USE_FUNC 1
int getNowTimeAsMs(void) {
#ifdef _WIN32
return clock();
#else
struct timeval tms;
gettimeofday(&tms, NULL);
return (int)(tms.tv_sec * 1000L + tms.tv_usec / 1000L);
#endif
}
float func(float scale, int cnt) {
float ret = 1;
int i;
for (i = 0; i < cnt; i ++) {
ret = ret * 0.99f + scale * 0.001;
}
return ret;
}
int main(int argc, char **argv) {
int cnt;
int i;
int start;
float ret;
cnt = 10000000;
start = getNowTimeAsMs();
ret = func(0, cnt);
printf("A: ret=%.50f, cost=%dms\n", ret, getNowTimeAsMs() - start);
start = getNowTimeAsMs();
ret = func(1, cnt);
printf("B: ret=%.50f, cost=%dms\n", ret, getNowTimeAsMs() - start);
return 0;
}
|