浮点Div / Mul>比Add / Sub慢30倍?

我最近阅读了这篇文章:现代硬件上的浮点和整数计算,并且对我自己的处理器在这个准基准测试中的性能感到好奇,所以我将两个版本的代码放在一起,一个放在C#中,一个放在C ++中(Visual Studio 2010 Express),并通过优化对它们进行编译以查看哪些内容会掉落。 我的C#版本的输出是相当合理的:

int add/sub: 350ms
int div/mul: 3469ms
float add/sub: 1007ms
float div/mul: 67493ms
double add/sub: 1914ms
double div/mul: 2766ms

当我编译并运行C ++版本时,完全不同的是:

int add/sub: 210.653ms
int div/mul: 2946.58ms
float add/sub: 3022.58ms
float div/mul: 172931ms
double add/sub: 1007.63ms
double div/mul: 74171.9ms

我期望一些性能差异,但不是这么大! 我不明白为什么C ++中的除法/乘法比加法/减法慢得多,因为托管的C#版本更符合我的预期。 该函数的C ++版本的代码如下所示:

template< typename T> void GenericTest(const char *typestring)
{
    T v = 0;
    T v0 = (T)((rand() % 256) / 16) + 1;
    T v1 = (T)((rand() % 256) / 16) + 1;
    T v2 = (T)((rand() % 256) / 16) + 1;
    T v3 = (T)((rand() % 256) / 16) + 1;
    T v4 = (T)((rand() % 256) / 16) + 1;
    T v5 = (T)((rand() % 256) / 16) + 1;
    T v6 = (T)((rand() % 256) / 16) + 1;
    T v7 = (T)((rand() % 256) / 16) + 1;
    T v8 = (T)((rand() % 256) / 16) + 1;
    T v9 = (T)((rand() % 256) / 16) + 1;

    HTimer tmr = HTimer();
    tmr.Start();
    for (int i = 0 ; i < 100000000 ; ++i)
    {
        v += v0;
        v -= v1;
        v += v2;
        v -= v3;
        v += v4;
        v -= v5;
        v += v6;
        v -= v7;
        v += v8;
        v -= v9;
    }
    tmr.Stop();

      // I removed the bracketed values from the table above, they just make the compiler
      // assume I am using the value for something do it doesn't optimize it out.
    cout << typestring << " add/sub: " << tmr.Elapsed() * 1000 << "ms [" << (int)v << "]" << endl;

    tmr.Start();
    for (int i = 0 ; i < 100000000 ; ++i)
    {
        v /= v0;
        v *= v1;
        v /= v2;
        v *= v3;
        v /= v4;
        v *= v5;
        v /= v6;
        v *= v7;
        v /= v8;
        v *= v9;
    }
    tmr.Stop();

    cout << typestring << " div/mul: " << tmr.Elapsed() * 1000 << "ms [" << (int)v << "]" << endl;
}

C#测试的代码不是通用的,因此可以这样实现:

static double DoubleTest()
{
    Random rnd = new Random();
    Stopwatch sw = new Stopwatch();

    double v = 0;
    double v0 = (double)rnd.Next(1, int.MaxValue);
    double v1 = (double)rnd.Next(1, int.MaxValue);
    double v2 = (double)rnd.Next(1, int.MaxValue);
    double v3 = (double)rnd.Next(1, int.MaxValue);
    double v4 = (double)rnd.Next(1, int.MaxValue);
    double v5 = (double)rnd.Next(1, int.MaxValue);
    double v6 = (double)rnd.Next(1, int.MaxValue);
    double v7 = (double)rnd.Next(1, int.MaxValue);
    double v8 = (double)rnd.Next(1, int.MaxValue);
    double v9 = (double)rnd.Next(1, int.MaxValue);

    sw.Start();
    for (int i = 0; i < 100000000; i++)
    {
        v += v0;
        v -= v1;
        v += v2;
        v -= v3;
        v += v4;
        v -= v5;
        v += v6;
        v -= v7;
        v += v8;
        v -= v9;
    }
    sw.Stop();

    Console.WriteLine("double add/sub: {0}", sw.ElapsedMilliseconds);
    sw.Reset();

    sw.Start();
    for (int i = 0; i < 100000000; i++)
    {
        v /= v0;
        v *= v1;
        v /= v2;
        v *= v3;
        v /= v4;
        v *= v5;
        v /= v6;
        v *= v7;
        v /= v8;
        v *= v9;
    }
    sw.Stop();

    Console.WriteLine("double div/mul: {0}", sw.ElapsedMilliseconds);
    sw.Reset();

    return v;
}

这里的任何想法?


对于float div / mul测试,您可能会得到非规格化的值,这对于处理正常的浮点值来说要慢得多。 这对于int测试来说不是问题,并且在双重测试之后会很快出现。

您应该可以将其添加到C ++的开头,以便将非正则表达式清除为零:

_controlfp(_DN_FLUSH, _MCW_DN);

我不确定如何在C#中做到这一点(或者甚至可能)。

一些更多的信息在这里:浮点数学执行时间


由于C#知道这些值在循环过程中没有被修改,所以C#可能会将vx优化为除以1 / vx乘法,并且它可以在前一次计算反转。

你可以自己做这个优化,并在C ++中定时。


如果您对浮点速度和可能的优化感兴趣,请阅读本书:http://www.agner.org/optimize/optimizing_cpp.pdf

你也可以检查:http://msdn.microsoft.com/en-us/library/aa289157%28VS.71%29.aspx

您的结果可能取决于诸如JIT,编译标志(调试/发布,执行哪种FP优化或允许的指令集)等。

尝试将这些标志设置为最大优化并更改程序,以便它肯定不会产生溢出或NAN,因为它们会影响计算速度。 (甚至像“v + = v1; v + = v2; v - = v1; v - = v2;”就可以,因为在“严格”或“精确”浮点模式下不会减少)。 也尽量不要使用比FP寄存器更多的变量。

链接地址: http://www.djcxy.com/p/31599.html

上一篇: Floating Point Div/Mul > 30 times slower than Add/Sub?

下一篇: C# Func delegate in combination with lambda expression not correct