Math.Pow的最佳实践

时间:2011-03-04 10:09:22

标签: c# .net pow

我正在开发一个扩展OpenCV,HALCON,....的图像处理库。该库必须与.NET Framework 3.5一起使用,由于我的.NET经验有限,我想问一些有关性能的问题。

我遇到了一些我无法正确解释的具体事情,并希望你问a)为什么和b)处理案件的最佳做法是什么。

我的第一个问题是关于Math.pow。我已经在StackOverflow上找到了一些答案,这很好地解释了它(a)但不知道如何处理(b)。我的基准程序看起来像这样

Stopwatch watch = new Stopwatch();  // from the Diagnostics class
watch.Start();
for (int i = 0; i < 1000000; i++)
    double result = Math.Pow(4,7)   // the function call
watch.Stop()

结果不是很好(在我的电脑上约300毫秒)(我已经运行了10次测试并计算了平均值)。

我的第一个想法是检查这是因为它是一个静态功能。所以我实现了自己的类

class MyMath
{
    public static double Pow (double x, double y)   //Using some expensive functions to calculate the power
    {
        return Math.Exp(Math.Log(x) * y);
    }

    public static double PowLoop (double x, int y)  // Using Loop
    {
        double res = x;
        for(int i = 1; i < y; i++)
            res *= x;
        return res;
    }

    public static double Pow7 (double x)            // Using inline calls
    {
        return x * x * x * x * x * x * x;
    }
}

我检查的第三件事是我是否会通过4 * 4 * 4 * 4 * 4 * 4 * 4直接替换Math.Pow(4,7)。

结果是(10次试运行中的平均值)

300 ms   Math.Pow(4,7)
356 ms   MyMath.Pow(4,7)    //gives wrong rounded results
264 ms   MyMath.PowLoop(4,7)
 92 ms   MyMath.Pow7(4)
 16 ms   4*4*4*4*4*4*4

现在我的情况基本上是这样的:不要使用Math for Pow。我唯一的问题就是......我现在真的需要实现自己的数学课吗?对于幂函数来说,实现一个自己的类似乎无效。 (顺便说一句,PowLoop和Pow7在Release版本中的速度甚至更快了〜25%,而Math.Pow却没有。)

所以我最后的问题是

a)我错了,如果我根本不使用Math.Pow(但可能是分数)(这让我感到难过)。

b)如果您有优化代码,您是否真的直接编写所有这些数学运算?

c)可能已经存在用于数学运算的更快(开源^^)库

d)我的问题的根源基本上是:我假设.NET Framework本身已经为这样的基本操作提供了非常优化的代码/编译结果 - 无论是Math-Class还是处理数组,我都有些意外编写自己的代码可以获得多少好处。是否有一些其他的,一般的“字段”或其他东西要在C#中查看,我不能直接信任C#。

3 个答案:

答案 0 :(得分:4)

要记住两件事:

  1. 您可能don't need to optimise这段代码。你在不到一秒的时间内完成了对该功能的一百万次调用。这真的会给你的程序带来很大的问题吗?

  2. 无论如何,
  3. Math.Pow可能是相当优化的。猜测一下,它会调用一个用较低级语言编写的合适的数字库,这意味着你不应该期望数量级增加。

  4. 数字编程比你想象的要难。即使您认为自己知道如何计算的算法也不是这样计算的。例如,当您计算平均值时,您不应该只是将数字相加并除以您拥有的数字。 (现代数字库使用两遍例程来纠正浮点错误。)

  5. 那就是说,如果你确定你确实需要优化,那么考虑使用整数而不是浮点值,或者将其外包给另一个数字库。

答案 1 :(得分:2)

首先,整数运算 比浮点运算更快。如果不需要浮点值,请不要使用浮点数据类型。这通常适用于任何编程语言。

其次,正如您自己所说,Math.Pow可以处理实际问题。它使用了比简单循环更复杂的算法。难怪它比简单循环慢。如果你摆脱了循环并且只进行了n次乘法,那么你也会减少设置循环的开销 - 从而使它更快。但是如果你不使用循环,你必须知道 预先指数的值 - 它不能在运行时提供。

我不确定为什么Math.ExpMath.Log更快。但是如果你使用Math.Log,就无法找到负值的力量。

基本上int更快,避免循环避免额外开销。但是当你选择这些时,你会获得一些灵活性。但是当你需要的只是整数时,避免实时通常是一个好主意,但是在这种情况下编写一个已经存在的自定义函数似乎有点太多了。

你必须问自己的问题是这是否值得。 Math.Pow实际上会减慢你的程序吗?无论如何,已经与您的语言捆绑在一起的Math.Pow通常是最快或非常接近的。如果你真的想要制作一个真正通用的替代实现(即不仅限于整数,正值等),你最终可能会使用默认实现中使用的相同算法。

答案 2 :(得分:0)

当你谈论对一行代码进行一百万次迭代时,显然每一个小细节都会有所作为。

Math.Pow()函数调用将比手动4 * 4 ... * 4示例慢得多。

不要编写自己的类,因为它可以编写比标准Math类更优化的任何内容。