简单的数学运算在 double 上比在 float 数据类型上更快? [复制]

Posted

技术标签:

【中文标题】简单的数学运算在 double 上比在 float 数据类型上更快? [复制]【英文标题】:Simple math operations faster on double than on float datatype? [duplicate] 【发布时间】:2013-01-04 14:26:15 【问题描述】:

可能重复:Are doubles faster than floats in c#?

我编写了简单的基准测试来检查在我的应用程序中将double 数据类型更改为float 可以获得多少性能。这是我的代码:

    // my form: 
    // one textbox: textbox1 (MultiLine property set to true)
    // one button: button1 with event button1_Click

    private void button1_Click(object sender, EventArgs e)
    

        int num = 10000000;

        float[] floats1 = new float[num];
        float[] floats2 = new float[num];
        float[] floatsr = new float[num];  // array for results
        double[] doubles1 = new double[num];
        double[] doubles2 = new double[num];
        double[] doublesr = new double[num]; // array for results

        Stopwatch stw = new Stopwatch();

        log("Preparing data");

        Random rnd = new Random();

        stw.Start();

        for (int i = 0; i < num; i++)
        
            floats1[i] = NextFloat(rnd);
            floats2[i] = NextFloat(rnd);
            doubles1[i] = rnd.NextDouble();
            doubles2[i] = rnd.NextDouble();
        
        stw.Stop();
        log(stw.Elapsed.TotalMilliseconds.ToString()+"ms");
        stw.Reset();




        log("");


        stw.Start();
        for (int i = 0; i <# i++)
        
            floatsr[i] = floats1[i] * floats2[i];
        
        stw.Stop();
        log("Multiplying floats: " + stw.Elapsed.TotalMilliseconds.ToString() + "ms");
        stw.Reset();



        stw.Start();
        for (int i = 0; i < num; i++)
        
            doublesr[i] = doubles1[i] * doubles2[i];
        
        stw.Stop();
        log("Multiplying doubles: " + stw.Elapsed.TotalMilliseconds.ToString() + "ms");
        stw.Reset();


        stw.Start();
        for (int i = 0; i < num; i++)
        
            floatsr[i] = floats1[i] / floats2[i];
        
        stw.Stop();
        log("Dividing floats: " + stw.Elapsed.TotalMilliseconds.ToString() + "ms");
        stw.Reset();


        stw.Start();
        for (int i = 0; i < num; i++)
        
            doublesr[i] = doubles1[i] / doubles2[i];
        
        stw.Stop();
        log("Dividing doubles: " + stw.Elapsed.TotalMilliseconds.ToString() + "ms");
        stw.Reset();

    

    private void log(string text)
    
        textBox1.Text = textBox1.Text + text + Environment.NewLine;
    

    // I found that function somewhere on ***
    static float NextFloat(Random random)
    
        double mantissa = (random.NextDouble() * 2.0) - 1.0;
        double exponent = Math.Pow(2.0, random.Next(-126, 128));
        return (float)(mantissa * exponent);
    

我得到了这样的结果(发布,无调试,Intel Mobile Core Duo T2500 2.0GHz 2MB CPU):

Preparing data 5275,6862ms

Multiplying floats: 442,7865ms 
Multiplying doubles: 169,4028ms
Dividing floats: 550,7052ms 
Dividing doubles: 164,1607ms

我很惊讶,double 上的操作比float 上的操作快了近 3 倍。我在这里搜索“双浮动”,我发现了这个:

Is using double faster than float?

最佳答案集中在 CPU 架构上,但我不能同意。

我怀疑是其他原因导致浮点数性能低下,因为我的带有英特尔 SSE 的 CPU 应该能够一次乘或除 4 个浮点数(打包浮点指令),或者一次 2 个双精度数。所以浮动应该更快。

也许编译器(或 .net 中的 clr)正在以某种方式优化内存使用?

有没有办法优化它,让浮动更快?

请不要重复报告,我看到了其他问题,但他们并不满意。


更改生成浮点数的方法后我的结果现在看起来很好(Servy 建议):

Preparing data 1367,0678ms

Multiplying floats: 109,8742ms 
Multiplying doubles: 149,9555ms
Dividing floats: 167,0079ms 
Dividing doubles: 168,6821ms

【问题讨论】:

请记住,使用浮点数进行任何冗长的数值计算都有很快累积舍入误差的风险。 You're NextFloat 与小麦 NextDouble 相比,数字分布非常不同。我不知道这是否有任何关联。但请注意,NextDouble0.01.0 之间产生一个数字,它是1.0 / 2147483647.0 的整数倍。因此,数字的“结尾”并不是真正随机的,这在四舍五入计算的乘积或商时可能很重要。 @JeppeStigNielsen 这很重要;看我的回答。当我在修改之前和之后运行 OP 的代码时,将其更改为它们都生成 0 和 1 之间的数字会从根本上改变运行时。 另外,假设您的计算机可以自行乘以 64 位浮点数。要处理 32 位(floatSystem.Single 情况),它可能必须首先“扩大”从数组中读取的数字,然后相乘,最后四舍五入并“缩小”数字以适应结果数组的 32 位“槽”。而 64 位数字则不需要这样的转换。 【参考方案1】:

这与您生成随机数的方式有关。浮点数的乘除并不完全相同;这些数字的实际值很重要。在浮点数的情况下,您要在相当大的范围内填充一个值。如果你创建你的浮点数,使它们在 0 和 1 之间,就像双打一样,那么它会更像你所期望的那样。只需将NextFloat 更改为:

static float NextFloat(Random random)

    return (float) random.NextDouble();

我刚刚进行了一些测试,通过这种更改,浮点数的乘法速度提高了 33%。

当然,这只是使比较“公平”的最简单方法。为了更好地了解浮点数与双精度浮点数的真正比较,您需要在各自类型的全部范围内生成随机浮点数和双精度数,或者更好的是,两者都保存代表程序将使用的数据类型的值。

【讨论】:

这是正确的。如果您进行此更改,浮动性能将略好于双倍性能。 不错的收获!修复后的时间:乘法浮点数:107,5963 毫秒乘法双打:132,2323 毫秒除法浮点数:160,1531 毫秒除法双打:170,8343 毫秒。现在这对我来说很有意义。 也许更公平地说:static float NextFloat(Random random) return random.Next(32767) / 32767f; 原因是NextDouble 没有使用double 的完整精度。它只生成1.0 / 2147483647.0 的倍数,即大约十进制数字的精度,而double 的精度大约是十六进制数字。【参考方案2】:

在某些情况下,浮点数上的 GPU 操作仍然更快,因为它们具有 32 位浮点硬件。

您的 x86(或 x86_64)架构 CPU 在数学协处理器中不支持 32 位。甚至 64 位支持。 x87 浮点单元使用 80 位算法。

现在,现代 x86 CPU 确实具有 SIMD 指令(MMX、SSE、AVX),硬件支持 32 位和 64 位浮点运算,性能更高——如果您可以在 SIMD 单元中执行所有操作.在 SIMD 和 FPU 之间移动数据会降低性能。

从当前版本开始,.NET 不使用 MMX、SSE 或 AVX。您可以尝试 Mono,它提供了 JIT 编译为 SIMD 指令的内在方法。或者,您可以将本机代码用于对性能最敏感的部分,因为现代 C++ 编译器不仅允许使用 SIMD,而且可以将看似普通的代码自动向量化为 SIMD 指令。

【讨论】:

以上是关于简单的数学运算在 double 上比在 float 数据类型上更快? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

我的 OpenCL 代码在 GPU 上比在 CPU 上慢

HTML5 Canvas 在 Firefox 上比在 Chrome 上更快!为啥?

Java中的简单浮点数类型float和double不能够进行精确运算

为啥 sift.compute() 在 MSER 关键点上比在 SIFT 关键点上慢

Unity 应用在 IOS 上比在 Android 上使用更多的内存

SSE 程序在 AMD 上比在 Intel 上花费的时间要长得多