64位模运算的奇怪性能行为

时间:2010-01-21 11:21:49

标签: c++ performance 64-bit numeric

这些方法调用的最后三个需要大约。比前四个时间翻倍。

唯一的区别是他们的参数不再适合整数。但这有关系吗?该参数声明为long,因此无论如何都应该使用long进行计算。模运算是否对数字使用另一种算法> maxint?

我正在使用amd athlon64 3200 +,winxp sp3和vs2008。

       Stopwatch sw = new Stopwatch();
       TestLong(sw, int.MaxValue - 3l);
       TestLong(sw, int.MaxValue - 2l);
       TestLong(sw, int.MaxValue - 1l);
       TestLong(sw, int.MaxValue);
       TestLong(sw, int.MaxValue + 1l);
       TestLong(sw, int.MaxValue + 2l);
       TestLong(sw, int.MaxValue + 3l);
       Console.ReadLine();

    static void TestLong(Stopwatch sw, long num)
    {
        long n = 0;
        sw.Reset();
        sw.Start();
        for (long i = 3; i < 20000000; i++)
        {
            n += num % i;
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed);            
    }

修改 我现在尝试用C进行相同操作,问题是 not 在这里发生,所有模运算都需要同时,在发布和调试模式下启用和不启用优化:

#include "stdafx.h"
#include "time.h"
#include "limits.h"

static void TestLong(long long num)
{
    long long n = 0;

    clock_t t = clock();
    for (long long i = 3; i < 20000000LL*100; i++)
    {
        n += num % i;
    }

    printf("%d - %lld\n", clock()-t, n);  
}

int main()
{
    printf("%i %i %i %i\n\n", sizeof (int), sizeof(long), sizeof(long long), sizeof(void*));

    TestLong(3);
    TestLong(10);
    TestLong(131);
    TestLong(INT_MAX - 1L);
    TestLong(UINT_MAX +1LL);
    TestLong(INT_MAX + 1LL);
    TestLong(LLONG_MAX-1LL);

    getchar();
    return 0;
}

EDIT2:

感谢您提出的好建议。我发现.net和c(在调试和发布模式下)都不会使用原子cpu指令来计算余数,但是它们调用了一个函数。

在c程序中,我可以得到它的名字“_allrem”。它还显示了该文件的完整源代码注释,因此我发现该算法特殊情况下的信息是32位除数而不是.net应用程序中的除数。

我还发现c程序的性能实际上只受除数的值而不是除数的影响。另一个测试表明.net程序中余数函数的性能取决于被除数和除数。

BTW:即使是简单的长long值加法也是通过连续的add和adc指令计算出来的。所以,即使我的处理器自称为64位,也不是:(

EDIT3:

我现在在使用visual studio 2010编译的Windows 7 x64版本上运行了c应用程序。有趣的是,性能行为保持不变,尽管现在(我检查了汇编源代码)使用了真正的64位指令。

2 个答案:

答案 0 :(得分:4)

多么奇怪的观察。您可以采取以下措施进一步调查:在程序开头添加“暂停”,如Console.ReadLine,但在第一次调用方法之后。然后以“发布”模式构建程序。然后启动程序而不是在调试器中。然后,在暂停时,附加调试器。通过它进行调试,然后查看为相关方法编写的代码。找到循环体应该很容易。

知道生成的循环体与C程序中的循环体有何不同是很有趣的。

所有这些箍跳过的原因是,当jitting一个已经连接了调试器的程序时,抖动会改变它在运行“debug”程序集时生成的代码;在这些情况下,它会调试在调试器中更容易理解的代码。看看抖动是什么意思是为这种情况生成的“最佳”代码会更有趣,所以你必须在抖动运行之后加入调试器。

答案 1 :(得分:3)

您是否尝试在盒子上的本机代码中执行相同的操作?

如果本机64位余数操作特殊情况下两个参数都在32位范围内,基本上将其委托给32位操作,我不会感到惊讶。 (或者可能是JIT做到了这一点......)优化这种情况确实有很大的意义,不是吗?