【问题标题】:.NET math calculation performances.NET 数学计算性能
【发布时间】:2011-09-14 09:11:32
【问题描述】:

我问了一个关于将 Excel 的 BetaInv 函数移植到 .NET 的问题:BetaInv function in SQL Server

现在我设法在纯依赖较少的 C# 代码中编写了该函数,并且在逗号后最多 6 或 7 位的 MS Excel 中得到了相同的结果,结果对我们来说很好,问题是嵌入了这样的代码在 SQL CLR 函数中,并从存储过程中调用数千次,如果我使用该函数或不使用该函数,则整个过程的执行速度会慢 50%,从 30 秒到一分钟。

这里有一些代码,我不是在要求深入分析,但是有没有人在我进行此计算的方式中看到任何主要的性能问题?例如使用其他数据类型而不是双精度数或其他...?

private static double betacf(double a, double b, double x)
        {
            int m, m2;
            double aa, c, d, del, h, qab, qam, qap;

            qab = a + b;
            qap = a + 1.0;
            qam = a - 1.0;

            c = 1.0; // First step of Lentz’s method.

            d = 1.0 - qab * x / qap;

            if (System.Math.Abs(d) < FPMIN)
            {
                d = FPMIN;
            }

            d = 1.0 / d;
            h = d;

            for (m = 1; m <= MAXIT; ++m)
            {
                m2 = 2 * m;
                aa = m * (b - m) * x / ((qam + m2) * (a + m2));
                d = 1.0 + aa * d; //One step (the even one) of the recurrence.

                if (System.Math.Abs(d) < FPMIN)
                {
                    d = FPMIN;
                }

                c = 1.0 + aa / c;

                if (System.Math.Abs(c) < FPMIN)
                {
                    c = FPMIN;
                }

                d = 1.0 / d;
                h *= d * c;

                aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
                d = 1.0 + aa * d; // Next step of the recurrence (the odd one).

                if (System.Math.Abs(d) < FPMIN)
                {
                    d = FPMIN;
                }

                c = 1.0 + aa / c;

                if (System.Math.Abs(c) < FPMIN)
                {
                    c = FPMIN;
                }

                d = 1.0 / d;
                del = d * c;
                h *= del;

                if (System.Math.Abs(del - 1.0) < EPS)
                {
                    // Are we done?
                    break;
                }
            }

            if (m > MAXIT)
            {
                return 0;
            }
            else
            {
                return h;
            }
        }

        private static double gammln(double xx)
        {
            double x, y, tmp, ser;

            double[] cof = new double[] { 76.180091729471457, -86.505320329416776, 24.014098240830911, -1.231739572450155, 0.001208650973866179, -0.000005395239384953 };

            y = xx;
            x = xx;
            tmp = x + 5.5;
            tmp -= (x + 0.5) * System.Math.Log(tmp);

            ser = 1.0000000001900149;

            for (int j = 0; j <= 5; ++j)
            {
                y += 1;
                ser += cof[j] / y;
            }

            return -tmp + System.Math.Log(2.5066282746310007 * ser / x);
        }

【问题讨论】:

    标签: c# .net performance


    【解决方案1】:

    对我来说唯一突出并且通常会影响性能的是内存分配。我不知道gammln 被调用的频率,但您可能希望将double[] cof = new double[] {} 移动到静态一次性分配。

    【讨论】:

    • 酷,从 56 秒下降到 39 秒,静态数组也必须是只读的,否则 SQL Server 会抱怨程序集导入/创建。非常感谢!
    【解决方案2】:

    double 通常是最好的类型。特别是因为Math 中的函数采用双打。不幸的是,我认为您的代码没有明显的改进。

    也许可以使用查找表来获得更好的第一个估计值,但由于我不知道你在做什么背后的数学,我不知道在这种特定情况下这是否可能.

    显然较大的 epsilon 将提高性能。因此,在满足您的准确性要求的同时,选择尽可能大。

    如果使用相同的参数重复调用该函数,您可能能够缓存结果。

    看起来很奇怪的一件事是您将 c、d、... 的小值强制为 FPMIN 的方式。我的直觉是,这可能会导致步长不理想。

    【讨论】:

    • 谢谢,很遗憾我们无法更改 epsilon 并且输入参数是随机的并且总是不同的。我不确定是否可以更改 SQL CLR 函数以针对输入参数数组工作并将结果作为表返回,然后我会以某种方式加入 SQL 中的结果表,只是说这样我会调用SQL 函数一次使​​用所有数据而不是数千次使用标量参数...但是在编码替代方案时需要付出一些努力,我不确定优势...无论如何感谢您看一下 :)
    【解决方案3】:

    我所拥有的只是在gammln 中展开j 循环,但它最多只会产生一点点不同。

    一个更激进的想法是用纯 T-SQL 重写,因为它拥有你使用的一切:+ - * / abs log 都可用。

    【讨论】:

    • 您好,我也有同样的想法,然后我在文章中找到了这段文字:CLR 函数受益于比 Transact-SQL 用户定义函数更快的调用路径。此外,与 Transact-SQL 相比,托管代码在过程代码、计算和字符串操作方面具有决定性的性能优势。计算密集型且不执行数据访问的 CLR 函数最好用托管代码编写。然而,Transact-SQL 函数确实比 CLR 集成更有效地执行数据访问。 msdn.microsoft.com/en-us/library/ms131075.aspx ...
    猜你喜欢
    • 2020-08-08
    • 2011-02-14
    • 2015-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    相关资源
    最近更新 更多