. net数学计算性能

本文关键字:计算性能 net | 更新日期: 2023-09-27 18:08:27

我问了一个关于将Excel的BetaInv函数移植到。net的问题:SQL Server中的BetaInv函数

现在我设法写函数在纯少依赖c#代码,我做得到相同的结果比在MS Excel后6或7位逗号,结果对我们很好,问题是,这样的代码是嵌入在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);
        }

. net数学计算性能

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

double通常是最好的类型。特别是因为Math中的函数是双精度的。不幸的是,我没有看到你的代码有明显的改进。

也许可以使用查找表来获得更好的初始估计,但是由于我不知道你所做的背后的数学,我不知道在这种特定情况下是否可能。

显然更大的epsilons将提高性能。因此,在满足精度要求的同时,选择尽可能大的尺寸。

如果使用相同的参数重复调用函数,则可以缓存结果。

看起来很奇怪的一件事是你强制c, d,…到FPMIN。我的直觉是,这可能会导致次优步长。

我所得到的只是在gammln中展开j循环,但它最多只会产生微小的差异。

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