【问题标题】:Differences in division and multiplication vs bit shifting除法和乘法与位移的差异
【发布时间】:2014-08-05 15:12:20
【问题描述】:

免责声明

我确实想知道何时或是否在我的代码中使用移位运算符,我对为什么乘法比向左移动位,而除法不是。


当我只是在四处闲逛时,我遇到了this 关于除法和位移的效率和速度的问题。它基本上表明,虽然在执行 2 的幂次移位时可能会节省几秒钟,但这并不是人们需要担心的差异。

对此我很感兴趣,我决定检查一下 C# 中的位移实际上有多快,并意识到一些奇怪的事情:

正如我所料,位移而不是除法更快,但“正常”乘法方法比位移更快。

我的问题很简单:为什么两个数字的乘法比位移快,尽管位移是处理器的基本操作?


这是我的测试用例的结果:

           Division: | Multiplication:
Bit shift:   315ms   |   315ms
   normal:   406ms   |   261ms

时间是 100 个案例的平均值,每个案例包含对 10000000 个从 1 到 int.MaxValue 的随机正数的每个数字 10 次操作。操作范围从除/乘以 2 到 1024(2 的幂)以及从 1 位到 10 位的位移。


编辑

@usr:我使用的是 .NET 4.5.1 版

我更新了我的结果,因为我意识到我只计算了我所说的数字的十分之一...... facepalm

我的主要:

static Main(string[] args)
{
    Fill(); // fills the array with random numbers
    Profile("division shift:", 100, BitShiftDiv);
    Profile("division:", 100, Div);
    Profile("multiplication shift:", 100, BitShiftMul);
    Profile("multiplication:", 100, Mul);
    Console.ReadKey();
}

这是我的分析方法:

static void Profile(string description, int iterations, Action func)
{
    GC.Collect()
    GC.WaitForPendingFinalizers();
    GC.Collect();

    func();

    Stopwatch stopWatch = Stopwatch.StartNew();
    for (int i = 0; i < iterations; i++)
    {
        func();
    }
    stopWatch.Stop();

    Console.WriteLine(description);
    Console.WriteLine("total: {0}ms", stopWatch.Elapsed.TotalMilliseconds);
    Console.WriteLine("  avg: {0}ms", stopWatch.Elapsed.TotalMilliseconds / (double)iterations);
}

包含操作的 Actions 的结构如下:

static void <Name>()
{
    for (int s = 1; s <= 10; s++)    /* for shifts */
    for (int s = 2; s <= 1024; s++)  /* for others */ 
    {
        for (int i = 0; i < nums.Length; i++)
        {
            var useless = nums[i] <shift> s;    /* shifting  */
            var useless = nums[i] <operator> s; /* otherwise */
        }
    }
}

nums是一个包含10000000ints的公共数组,由Fill()方法填充。

【问题讨论】:

  • 因为优化器不傻。
  • 您可能测量错误;微基准测试很难。
  • 您可能仍然测量错误。当心 JITter。 yoda.arachsys.com/csharp/benchmark.html
  • 移位比乘法更快的想法在 1970 年代硬件上的一些优化不佳的 C 编译器中是正确的。让优化器完成它的工作。
  • 移位在 C# 中有额外的开销,在 this answer 中进行了解释。未在启用优化器的情况下运行是标准的基准测试错误。使用 Agner Fog 的说明计时手册来获得洞察力。周期时间是一个估计值:将内存值移动任意数量大约需要 4 个周期,乘法需要 1,除法需要 11 到 18 个周期。 for(;;) 循环开销模糊了这些巨大差异。

标签: c# performance division bit-shift


【解决方案1】:

总结一下cmets中已经提到的答案:

  • 乘法和位移更快,因为它也是 CPU 的 本机操作。它需要一个周期,而位移大约需要四个周期,这就是它更快的原因。除法需要 11 到 18 个周期。

  • 使用 C# 时,由于在我的代码和 CPU 之间进行了许多优化,因此我无法足够接近 CPU 以获得诊断上的结论性结果。

  • 此外,微基准测试很困难,可能会产生错误的结果,也可能由于上述原因而发生。

如果我忘记了什么,请评论告诉我!

【讨论】:

    猜你喜欢
    • 2011-09-22
    • 1970-01-01
    • 2012-01-05
    • 1970-01-01
    • 2011-02-16
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 2010-11-13
    相关资源
    最近更新 更多