我正在开发一个扩展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#。
答案 0 :(得分:4)
要记住两件事:
您可能don't need to optimise这段代码。你在不到一秒的时间内完成了对该功能的一百万次调用。这真的会给你的程序带来很大的问题吗?
Math.Pow
可能是相当优化的。猜测一下,它会调用一个用较低级语言编写的合适的数字库,这意味着你不应该期望数量级增加。
数字编程比你想象的要难。即使您认为自己知道如何计算的算法也不是这样计算的。例如,当您计算平均值时,您不应该只是将数字相加并除以您拥有的数字。 (现代数字库使用两遍例程来纠正浮点错误。)
那就是说,如果你确定你确实需要优化,那么考虑使用整数而不是浮点值,或者将其外包给另一个数字库。
答案 1 :(得分:2)
首先,整数运算 比浮点运算更快。如果不需要浮点值,请不要使用浮点数据类型。这通常适用于任何编程语言。
其次,正如您自己所说,Math.Pow
可以处理实际问题。它使用了比简单循环更复杂的算法。难怪它比简单循环慢。如果你摆脱了循环并且只进行了n次乘法,那么你也会减少设置循环的开销 - 从而使它更快。但是如果你不使用循环,你必须知道
预先指数的值 - 它不能在运行时提供。
我不确定为什么Math.Exp
和Math.Log
更快。但是如果你使用Math.Log
,就无法找到负值的力量。
基本上int
更快,避免循环避免额外开销。但是当你选择这些时,你会获得一些灵活性。但是当你需要的只是整数时,避免实时通常是一个好主意,但是在这种情况下编写一个已经存在的自定义函数似乎有点太多了。
你必须问自己的问题是这是否值得。 Math.Pow
实际上会减慢你的程序吗?无论如何,已经与您的语言捆绑在一起的Math.Pow
通常是最快或非常接近的。如果你真的想要制作一个真正通用的替代实现(即不仅限于整数,正值等),你最终可能会使用默认实现中使用的相同算法。
答案 2 :(得分:0)
当你谈论对一行代码进行一百万次迭代时,显然每一个小细节都会有所作为。
Math.Pow()函数调用将比手动4 * 4 ... * 4示例慢得多。
不要编写自己的类,因为它可以编写比标准Math类更优化的任何内容。