【问题标题】:Math.Pow vs multiply operator (performance)Math.Pow 与乘法运算符(性能)
【发布时间】:2010-10-30 12:12:47
【问题描述】:

任何人都知道乘法运算符是否比使用 Math.Pow 方法更快?喜欢:

n * n * n

Math.Pow ( n, 3 )

【问题讨论】:

  • 实现绩效的方法是(1)设定有意义的目标,(2)衡量你是否达到了目标,(3)如果没有达到目标,找出最慢的,(4 ) 优化 最慢 的事情,直到达到你的目标。如果您要问我们哪个更快,那么您还没有完成步骤(2)或(3),因此执行步骤(4)还为时过早。
  • +Eric Lippert 确实如此,但了解语言是否进行了一些神奇的优化也很有趣:P

标签: c# .net performance pow


【解决方案1】:

我刚刚重新安装了windows,所以没有安装visual studio,代码很丑

using System;
using System.Diagnostics;

public static class test{

public static void Main(string[] args){
    MyTest();
    PowTest();
}

static void PowTest(){
    var sw = Stopwatch.StartNew();
    double res = 0;
    for (int i = 0; i < 333333333; i++){
        res = Math.Pow(i,30); //pow(i,30)
    }
    Console.WriteLine("Math.Pow: " + sw.ElapsedMilliseconds + " ms:  " + res);
}

static void MyTest(){
    var sw = Stopwatch.StartNew();
    double res = 0;
    for (int i = 0; i < 333333333; i++){
        res = MyPow(i,30);
    }
    Console.WriteLine("MyPow: " + sw.ElapsedMilliseconds + " ms:  " + res);
}



static double MyPow(double num, int exp)
{
    double result = 1.0;
    while (exp > 0)
    {
        if (exp % 2 == 1)
            result *= num;
        exp >>= 1;
        num *= num;
    }

    return result;
}
}

结果:
csc /o test.cs

test.exe

MyPow: 6224 ms:  4.8569351667866E+255  
Math.Pow: 43350 ms:  4.8569351667866E+255 

在我的测试中,平方求幂(请参阅 https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int)比 Math.Pow 快得多(我的 CPU 是 2 Ghz 的 Pentium T3200)

编辑:.NET 版本是 3.5 SP1,操作系统是 Vista SP1,电源计划是高性能。

【讨论】:

  • 谢谢。你的方法似乎很好。是不是比 nnn 还要好?
  • 它需要两次乘法,就像 nnn 一样,但是有一些开销,所以它会稍微更糟...对于四(或更高)的指数可能比 nnn*n 更好,因为它仍然只需要两次乘法,而直接方法需要三次。
  • MyPow() 仅适用于正数。它应该以if (exp&lt; 0) return (1 / MyPow(num, Math.Abs(exp))); 开头
  • 您可以通过将模数更改为按位检查来实现约 1.5% 的性能提升,即 (exp % 2 == 1) -> ((exp & 1) != 0)
  • 我只是在我的笔记本电脑上运行了这个,但是作为一个 .NET Core 2.1 控制台应用程序,我得到了结果:MyPow:23545 毫秒,Math.Pow:21109 毫秒。所以很明显,自从这篇文章发布以来,MS 已经大大提高了性能。
【解决方案2】:

基本上,您应该基准测试才能看到。

有根据的猜测(不可靠):

如果某些编译器没有针对同一事物进行优化...

x * x * x 很可能比Math.Pow(x, 3) 快,因为Math.Pow 必须在一般情况下处理问题,处理分数幂和其他问题,而x * x * x 只需要几个乘法指令,所以它很可能会更快。

【讨论】:

  • 你为什么认为这很有可能?
  • 它需要两个整数乘法指​​令。如果 Math.Pow 没有针对同一事物进行优化,它可能会做更多的工作(请记住,它必须解决更普遍的问题,例如分数幂......)
  • “很可能 x * x * x 比 Math.Pow 快(完全猜测)。”这句话似乎有些不对劲。
  • 我认为 System.Math 是作为 InternalCall 实现的。不过我还没有检查过。
  • @divo:确实,这不是猜测。是以前的测试导致了这种情况。我想指出,这实际上取决于运行代码的特定基础架构,您应该始终依赖基准测试而不是猜测。
【解决方案3】:

10 多年图像处理和科学计算优化的经验法则:

算法级别的优化优于任何低级别的优化。尽管有“写显而易见,然后优化”的传统智慧,但这必须在一开始就完成。不是之后。

手工编码的数学运算(尤其是 SIMD SSE+ 类型)通常会优于完全错误检查的通用内置运算。

编译器事先知道需要做什么的任何操作都会被编译器优化。这些包括: 1. Array.Copy()等内存操作 2. 对给定数组长度的数组进行循环。至于 (..; i&lt;array.Length;..)

总是设定不切实际的目标(如果你愿意的话)。

【讨论】:

    【解决方案4】:

    我昨天刚好测试了这个,现在看到你的问题了。

    在我的机器上,运行 1 个测试线程的 Core 2 Duo,使用乘法高达 9 的速度更快。在 10 时,Math.Pow(b, e) 更快。

    但是,即使是 2 倍,结果也常常不完全相同。存在舍入错误。

    一些算法对舍入误差非常敏感。在我发现这一点之前,我不得不进行超过一百万次随机测试。

    【讨论】:

    • 谢谢,那么使用哪一个来减少舍入误差更好?
    • Power 公式最适合准确性。但是,这取决于输入。对于大量的数字,结果是相同的。我建议您测试它们的使用情况。
    【解决方案5】:

    这是非常微小的,您可能应该针对特定平台对其进行基准测试,我认为 Pentium Pro 的结果不一定与 ARM 或 Pentium II 的结果相同。

    总而言之,这很可能是完全不相关的。

    【讨论】:

      【解决方案6】:

      我检查了,Math.Pow() 被定义为两个双打。这意味着它不能进行重复乘法,而是必须使用更通用的方法。如果有Math.Pow(double, int),可能会更高效。

      话虽如此,性能差异几乎可以肯定是微不足道的,因此您应该使用更清晰的那个。像这样的微优化几乎总是毫无意义的,几乎可以在任何时候引入,并且应该留到开发过程的最后。此时,您可以检查软件是否太慢、热点在哪里,然后将您的微优化工作花在真正能产生影响的地方。

      【讨论】:

        【解决方案7】:

        让我们使用约定 x^n。假设 n 始终是一个整数。

        对于较小的 n 值,无​​聊的乘法会更快,因为 Math.Pow(可能取决于实现)使用奇特的算法来允许 n 为非整数和/或负数。

        对于较大的 n 值,Math.Pow 可能会更快,但如果您的库不是很聪明,它将使用相同的算法,如果您知道 n 始终是整数,那么这并不理想。为此,您可以编写 exponentiation by squaring 或其他一些奇特算法的实现。

        当然,现代计算机速度非常快,您可能应该坚持使用最简单、最容易阅读、最不可能出错的方法,直到您对程序进行基准测试并确定使用不同的算法可以显着提高速度。

        【讨论】:

          【解决方案8】:

          Math.Pow(x, y) 通常在内部计算为Math.Exp(Math.Log(x) * y)。每个幂方程都需要找到一个自然对数、一个乘法,然后将e 提高到一个幂。

          正如我在之前的回答中提到的那样,Math.Pow() 只有在 10 的幂时才会变得更快,但如果使用一系列乘法,准确性会受到影响。

          【讨论】:

            【解决方案9】:

            我不同意手工构建的函数总是更快。余弦函数比我能写的任何东西都更快、更准确。至于 pow()。我做了一个快速测试,看看 Math.pow() 在 JavaScript 中有多慢,因为 Mehrdad 告诫不要猜测

                for (i3 = 0; i3 < 50000; ++i3) { 
                  for(n=0; n < 9000;n++){ 
                    x=x*Math.cos(i3);
                  }
                }
            

            结果如下:

            Each function run 50000 times 
            
            time for 50000 Math.cos(i) calls = 8 ms 
            time for 50000 Math.pow(Math.cos(i),9000) calls = 21 ms 
            time for 50000 Math.pow(Math.cos(i),9000000) calls = 16 ms 
            time for 50000 homemade for loop calls 1065 ms
            

            如果您不同意,请在http://www.m0ose.com/javascripts/speedtests/powSpeedTest.html 尝试该程序

            【讨论】:

            • 这不是很丰富。在您的自定义版本中,您多次调用 Math.cos,而对于 Math.pow,它作为参数被调用一次。因此,对于真正的测试,将 x = x * math... 替换为 x = x * c;并在两者之间插入 c = Math.cos...
            猜你喜欢
            • 2017-10-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-04-12
            • 1970-01-01
            • 2011-03-08
            • 2011-12-12
            • 1970-01-01
            相关资源
            最近更新 更多