今天遇到一个浮点数运算时间异常的问题,诡异……

[复制链接]
741|2
手机看帖
扫描二维码
随时随地手机跟帖
computer00|  楼主 | 2020-6-1 22:48 | 显示全部楼层 |阅读模式
代码见**末尾的附文。
在一台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;
}

使用特权

评论回复

相关帖子

zzz3265| | 2020-6-2 13:26 | 显示全部楼层
浮点数据有个叫非规格化数据  "denormal data"
按标准浮点格式, 幂的范围是-126 - 127,  能表示的最小值也就是大约: pow(2.0,-126.0) = 1.1754944e-038
绝对值小于这个值的就是 denormal data
非规格化数据计算从名称可以看到, 因为非规格化, 所以而外要处理很多东西, 导致性能10倍以上的损失, 甚至更高
其实从硬件上对于这些情况是有些开关的, 可以搜索 FTZ, DAZ (Flush-to-zero, denormal-are-zero)
但是C语言的标准对这些东西支持不是很好

使用特权

评论回复
kingTek| | 2020-6-2 14:31 | 显示全部楼层
应该是看看底层代码,分析一下吧,

我在以往遇到类似问题时采取硬件手段,可以将实际耗时大的部分用排除法定位,从而找出真正的原因来。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:MAXHUB高效会议平台

246

主题

14682

帖子

206

粉丝