【问题标题】:Strange performance behaviour for 64 bit modulo operation64 位模运算的奇怪性能行为
【发布时间】:2010-01-21 11:21:48
【问题描述】:

最后三个方法调用大约需要。时间是前四个的两倍。

唯一的区别是它们的参数不再适合整数。但这有关系吗?该参数被声明为 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”。它还显示了该文件的完整源 cmets,因此我发现该算法特例是 32 位除数,而不是 .net 应用程序中的除数。

我还发现c程序的性能真的只受除数的影响,而不受除数的影响。另一项测试表明,.net 程序中余数函数的性能取决于被除数和除数。

顺便说一句:即使是 long long 值的简单加法也是通过连续的 add 和 adc 指令计算的。所以即使我的处理器称自己为 64 位,它也确实不是 :(

EDIT3:

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

【问题讨论】:

  • 关于您上次的观察,如果您使用的是 Windows XP 的常规消费者安装,那么您几乎可以肯定是在 32 位模式下运行您喜欢的 64 位处理器。
  • 您的处理器确实是 64 位的,它是您的操作系统和编译器在 32 位模式下运行。

标签: c++ performance 64-bit numeric


【解决方案1】:

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

了解生成的循环体与 C 程序中的循环体有何不同会很有趣。

所有这些箍跳过的原因是因为抖动会改变它在抖动“调试”程序集时生成的代码在抖动已经附加了调试器的程序时;在这些情况下,它会在调试器中更容易理解的代码。看看 jitter 认为什么是为这种情况生成的“最佳”代码会更有趣,因此您必须在 jitter 运行后延迟附加调试器。

【讨论】:

  • 感谢您的建议。我发现了很多有趣的东西,看看我的新编辑:)
【解决方案2】:

您是否尝试过在您的机器上使用本机代码执行相同的操作?

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

【讨论】:

  • 我现在用本机代码试了一下,这里没有出现问题。当然,完成这样的优化是有道理的,但为什么有时会,有时不会。但我不相信它是 JIT,因为有问题的值是作为参数传递的,我不认为该方法在同一过程中被多次 jit。
猜你喜欢
  • 2019-04-01
  • 1970-01-01
  • 2012-08-28
  • 1970-01-01
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
相关资源
最近更新 更多