8

我正在开发一个扩展 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

现在我现在的情况基本上是这样的:Don't use Math for Pow。我唯一的问题是……我现在真的必须实现自己的数学课吗?仅仅为 power 函数实现一个自己的类似乎有点无效。(顺便说一句。PowLoop 和 Pow7 在 Release 构建中的速度甚至快了约 25%,而 Math.Pow 则没有)。

所以我最后的问题是

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

b) 如果你有代码需要优化,你真的是直接写所有这些数学运算吗?

c)可能已经有一个更快的(开源^^)数学运算库

d) 我的问题的来源基本上是:我假设.NET Framework 本身已经为这些基本操作提供了非常优化的代码/编译结果 - 无论是数学类还是处理数组,我有点惊讶有多少好处我会通过编写自己的代码来获益。在我不能直接信任 C# 的 C# 中是否有一些其他的通用“字段”或其他东西需要注意。

4

3 回答 3

5

Two things to bear in mind:

  1. You probably don't need to optimise this bit of code. You've just done a million calls to the function in less than a second. Is this really going to cause big problems in your program?

  2. Math.Pow is probably fairly optimal anyway. At a guess, it will be calling a proper numerics library written in a lower level language, which means you shouldn't expect orders of magnitude increases.

  3. Numerical programming is harder than you think. Even the algorithms that you think you know how to calculate, aren't calculated that way. For example, when you calculate the mean, you shouldn't just add up the numbers and divide by how many numbers you have. (Modern numerics libraries use a two pass routine to correct for floating point errors.)

That said, if you decide that you definitely do need to optimise, then consider using integers rather than floating point values, or outsourcing this to another numerics library.

于 2011-03-04T10:40:14.063 回答
2

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

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

我不太确定为什么Math.Exp而且Math.Log速度更快。但是如果你使用Math.Log,你就找不到负值的力量。

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

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

于 2011-03-04T10:32:54.173 回答
0

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

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

不要编写自己的课程,因为它怀疑您是否能够编写比标准数学课程更优化的东西。

于 2011-03-04T10:22:05.790 回答