2011-03-04 73 views
7

我正在研究扩展OpenCV,HALCON,...的n图像处理库。该库必须使用.NET Framework 3.5,因为我对.NET的使用经验有限,所以我想问一些关于性能的问题。Math.Pow的最佳做法

我遇到了一些我无法向自己正确解释的具体事情,并希望您提问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次,calcuated平均值)。

我的第一个想法是检查这是因为它是一个静态函数。所以,我实现了我自己的直接阶级

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; 
    } 
} 

我检查的第三件事是,如果我将取代Math.Pow(4,7)到4 * 4 * 4 * 4 * 4 * 4 * 4。

的结果(平均出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-Class吗?为功能函数实现一个自己的类似乎不太有效。 (顺便说一下,PowLoop和Pow7在Release版本中的速度更快了25%,而Math.Pow则没有)。

所以我最后的问题是

一)我,如果我不能在所有(但可能分数)(这让我莫名其妙地伤心难过)使用Math.Pow我错了。 b)如果你有代码优化,你是否真的直接写这些数学运算?

c)是有可能已经快(开源^^)库数学运算

d)我的问题的来源基本上是:我认为在.NET Framework本身就已经提供了非常优化编码/编译这些基本操作的结果 - 无论是数学类还是处理数组,我都有点惊讶,通过编写自己的代码可以获得多少好处。还有其他的一些“领域”或别的东西在C#中看不到,我不能直接相信C#。

+6

我认为4 * 4 * 4 * 4 * 4 * 4 * 4会在编译时评估,因此它的速度非常快。 – Nick 2011-03-04 10:13:54

+0

我认为对于大多数人来说,100ms左右并不是什么大不了的事情。 C#通常不是大多数人的这种应用程序的首选。 – Ian 2011-03-04 10:15:26

+1

你用较大的数字测试过吗?我认为Math.Pow针对较大的指数进行了优化,并且执行如下操作:x^7 == x^{3 + 3 + 1} == {x^3 + x^3 x},这意味着它正在运行顺序是O(log(n)),而你的解决方案是O(n),并且对于大型指数可能会慢得多。 – markijbema 2011-03-04 10:19:01

回答

0

当你正在谈论做一行代码的百万次迭代时,显然每一个细节都会有所作为。

Math.Pow()是一个函数调用,它比您的手动4 * 4 ... * 4示例要慢很多。

不要将自己的课程编写为可疑课程,您可以编写比标准数学课程更优化的任何课程。

1

首先,整数运算是太多比浮点更快。如果您不需要浮点值,请不要使用浮点数据类型。对于任何编程语言来说都是如此。其次,正如你自己所说的,Math.Pow可以处理雷亚尔。它使用了比简单循环更复杂的算法。难怪它比循环更慢。如果你摆脱了循环,只需执行n次乘法运算,你也可以减少建立循环的开销 - 从而使其更快。但是如果你不使用循环,你必须事先知道指数的值 - 它不能在运行时提供。

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

基本上int更快,避免循环,避免额外的开销。但是当你为那些人而做时,你正在交易一些灵活性。但是,当你需要的只是整数时避免实数通常是一个好主意,但在这种情况下,当已经存在的自定义函数编码时似乎有点太过分了。

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

+1

虽然我同意你的回答的一般观点,但我想说一句话:当然库函数函数是优化的,但并不总是可以针对所有情况进行优化。考虑例如排序,其中有很多有效的算法选项,并且它确实取决于您的特定数据哪一个最好。 – markijbema 2011-03-04 11:57:04

+0

@markijbema:这就是为什么我在回答中使用了“通用”这个词。如果您可以对数据做出额外的假设(例如它们总是整数或在给定的范围内),当然您可以使用该数据进行优化(例如,计数排序而不是快速排序)。这是我的答案的重点,对于通用功能来说,很难与图书馆相匹配。 – MAK 2011-03-04 12:00:20

4

两件事情要牢记:

  1. 你可能don't need to optimise这段代码。你在不到一秒钟内完成了一百万次函数调用。这真的会在你的程序中造成很大的问题吗?

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

  3. 数值编程比您想象的要困难。即使你认为你知道如何计算的算法,也不是这样计算的。例如,当你计算平均值时,你不应该只加上数字并除以你有多少数字。 (现代NUMERICS库使用两遍程序纠正了为浮点错误。)

这就是说,如果你决定,你绝对需要优化,然后再考虑使用整数,而不是浮点值,或外包这到另一个数字图书馆。