如何提高C#数字代码的速度

如何提高C#数字代码的速度,c#,complex-numbers,numerics,C#,Complex Numbers,Numerics,我有一个C#应用程序,它调用Fortran DLL进行数值计算。已经确定DLL调用至少比从Fortran控制台应用程序调用相同计算慢10倍,因此我已经开始将代码移植到C#。移植过程是一行一行地复制代码和修改语法。因此C#和Fortran看起来非常相似。Fortran中的公共块成为类中的字段。在移植了一些核心例程之后,我开始测试,发现双精度C#代码比双精度Fortran慢30倍(比单精度Fortran慢50倍)。我循环测试代码100x,以尽量减少C#JIT编译器的开销 该代码使用复杂的算术和复杂的

我有一个C#应用程序,它调用Fortran DLL进行数值计算。已经确定DLL调用至少比从Fortran控制台应用程序调用相同计算慢10倍,因此我已经开始将代码移植到C#。移植过程是一行一行地复制代码和修改语法。因此C#和Fortran看起来非常相似。Fortran中的公共块成为类中的字段。在移植了一些核心例程之后,我开始测试,发现双精度C#代码比双精度Fortran慢30倍(比单精度Fortran慢50倍)。我循环测试代码100x,以尽量减少C#JIT编译器的开销

该代码使用复杂的算术和复杂的函数,如SQRT和LOG10。我提供了一个C#struct来处理这个数学问题。我怀疑这就是问题所在,但VS2010 Pro中似乎没有分析器,所以我不确定

Fortran编译是Intel的最新版本。我没有对C#或Fortran代码进行任何特殊优化。我比较了使用发布版本的时间。我只有一个双核CPU,所以并行化可能不会有多大帮助

我可以就如何加速这段代码提出一些建议

这是我的方法之一。C#看起来就像我没有写的Fortran

    public void Forwx(double rshldr, double rbed, double[][] resdep, double[]toolparm)
    {
        var zex = new Complex[70];
        var zey = new Complex[70];

        var px2 = new Complex[70];
        var px4 = new Complex[70];

        var rlt = new double[2];
        var trsmt = new double[2];
        var fr = new double[2];
        var dnc = -0.02;
        var factr = 26332.65;

        var rh2 = Math.Max(0.1, rbed);
        var rh1 = Math.Max(0.1, rshldr);
        const double e1 = 1.0;
        const double e2 = 1.0;
        const double er = 0.818730753077982;
        const double re = 1.0 / er;
        var ii = Complex.I;
        const double pi = Math.PI;
        const double eps0 = 8.854e-12;
        const double amu0 = 4.0e-7 * pi;

        for (var ktool = 3; ktool <= 6; ktool++)
        {
            if (ktool == 3)            // Integrated 2MHz
            {
                dnc = -0.02;
                rlt[0] = 0.2794;
                rlt[1] = -0.2794;
                trsmt[0] = 0.904875;
                trsmt[1] = -0.904875;
                fr[0] = 2000.0;
                factr = 26332.65;
            }

            if (ktool == 4)         // Integrated 400kHz
            {
                dnc = -0.02;
                rlt[0] = 0.2794;
                rlt[1] = -0.2794;
                trsmt[0] = 0.904875;
                trsmt[1] = -0.904875;
                fr[0] = 400.0;
                factr = 26811.866;
            }

            if (ktool == 5)           // Option 5 20kHz
            {
                dnc = -0.1;
                rlt[0] = 0.0;
                rlt[1] = 0.0;
                trsmt[0] = 5.75;
                trsmt[1] = 5.75;
                fr[0] = 20.0;
                factr = 26811.866 * 2.516 * toolparm[1] / 0.28e8;
            }

            if (ktool == 6)         // Option 6 50kHz
            {
                dnc = -0.1;
                rlt[0] = 0.0;
                rlt[1] = 0.0;
                trsmt[0] = 5.75;
                trsmt[1] = 5.75;
                fr[0] = 50.0;
                factr = 26811.866 * 6.291 * toolparm[2] / 0.7e8;
            }

            var r1 = trsmt[0] - rlt[0];
            var r2 = trsmt[0] - rlt[1];
            var omega = 2000.0 * pi * fr[0];
            var k12 = omega*amu0*(omega*e1*eps0 + ii/rh1);
            var k22 = omega*amu0*(omega*e2*eps0 + ii/rh2);
            var krat = (k22 - k12)/k12;

            for (var iz = 0; iz < 601; iz++)
            {
                var recx1 = new Complex(0.0, 0.0);
                var rx1 = new Complex(0.0, 0.0);
                var recy1 = new Complex(0.0, 0.0);
                var ry1 = new Complex(0.0, 0.0);
                var lam = new Complex(3.01517934056e-04 / (Math.Pow(er, 5) * r1));
                Complex c1;
                Complex c2;
                for (var i = 0; i < 70; i++)
                {
                    if (iz == 0)
                    {
                        lam = lam * re;
                        var lam2 = lam * lam;
                        var p11 = lam2 - k12;
                        var p1 = Complex.Sqrt(p11);
                        var p22 = lam2 - k22;
                        var p2 = Complex.Sqrt(p22);
                        zex[i] = Complex.Exp(dnc * p2);
                        zey[i] = Complex.Exp(dnc * p1);
                        c1 = p2 * k12;
                        c2 = p1 * k22;
                        var t3 = lam / p2;
                        var t2 = t3 * (c1 - c2) / (c1 + c2);
                        var q2 = lam * krat * (t2 + t3) / (p1 + p2);
                        px2[i] = (lam2 * q2 + lam * p2 * t2);
                        px4[i] = px2[i];
                    }
                    else
                    {
                        px2[i] = px2[i] * zex[i];
                        px4[i] = px4[i] * zey[i];
                    }
                    recx1 = recx1 + a1[i] * px2[i];
                    recy1 = recy1 + a1[i] * px4[i];
                    rx1 = rx1 + px2[i] * as1i[i];
                    ry1 = ry1 + px4[i] * as1i[i];
                }
                if (ktool <= 4)
                {
                    c1 = recx1*r1;
                    c2 = rx1*r2;
                    c2 = c2 - Math.Pow(r1/r2,3)*c1;
                    resdep[12 - ktool][iz + 600] = c2.Re*factr;
                    c1 = recy1*r1;
                    c2 = ry1*r2;
                    c2 = c2 - Math.Pow(r1 / r2,3) * c1;
                    resdep[12 - ktool][600 - iz] = c2.Re*factr;
                }
                else
                {
                    c1 = recx1*r1;
                    //c2 = rx1*r2;
                    //c2 = c2 - Math.Pow(r1 / r2,3) * c1;
                    resdep[ktool + 5][iz + 600] = c1.Re * factr;
                    c1 = recy1*r1;
                    //c2 = ry1*r2;
                    //c2 = c2 - Math.Pow(r1 / r2,3) * c1;
                    resdep[ktool + 5][600 - iz] = c1.Re * factr;
                }
            }
        }
    }
这是复杂结构的一部分

    public static Complex Sqrt(double x)
    {
        return x >= 0 ? new Complex(Math.Sqrt(x)) : new Complex(0, Math.Sqrt(-x));
    }

    public static Complex Exp(Complex z)
    {
        return new Complex(Math.Exp(z.Re) * Math.Cos(z.Im), Math.Exp(z.Re) * Math.Sin(z.Im));
    }

    public static Complex Log(Complex z)
    {
        return new Complex(Math.Log(Abs(z)), Arg(z));
    }
public struct Complex
{
    private readonly double _re;
    private readonly double _im;

    #region Properties

    public double Re
    {
        get { return _re; }
        //set { re = value; }
    }

    public double Im
    {
        get { return _im; }
        //set { im = value; }
    }

    public static Complex I
    {
        get { return new Complex(0.0, 1.0); }
    }

    public static Complex Zero
    {
        get { return new Complex(0.0, 0.0); }
    }

    public static Complex One
    {
        get { return new Complex(1.0, 0.0); }
    }

    #endregion


    #region constructors

    public Complex(double x)
    {
        _re = x;
        _im = 0.0;
    }

    public Complex(Complex z)
    {
        _re = z.Re;
        _im = z.Im;
    }

    public Complex(double x, double y)  //constructor
    {
        _re = x;
        _im = y;
    }

    #endregion
}

我看到的最佳途径是使用C++/CLI并利用GPU进行繁重的计算


但在此之前,请确保性能问题与DLL有关,而不是与数据编组等有关。

我看到的最佳途径是使用C++/CLI并利用GPU进行繁重的计算


但在执行此操作之前,请确保性能问题与DLL有关,而不是与数据编组等有关。

您应该尝试摆脱复杂的结构,而是使用一个。它位于
System.Numerics
名称空间中。您可能需要在代码上执行查找和替换,以将行
Complex.I
替换为
Complex.ImaginaryOne
,但这应该是一个相当简单的转换

这有两个好处:

1) 内置逻辑将比您可以编写的任何东西都更优化(或者至少不会更糟)。

2) 它更易于维护,因为它使用.NET标准,所以任何人都可以查看文档,以及任何可以在代码中使用的增强内容。

您应该尝试摆脱复杂的结构,而改用它。它位于
System.Numerics
名称空间中。您可能需要在代码上执行查找和替换,以将行
Complex.I
替换为
Complex.ImaginaryOne
,但这应该是一个相当简单的转换

这有两个好处:

1) 内置逻辑将比您可以编写的任何东西都更优化(或者至少不会更糟)。

2) 它更易于维护,因为它使用.NET标准,所以任何人都可以查看文档,以及任何可以对代码进行扩展的内容。

我用复数计算来计时我的算法,并将其替换为双运算,速度提高了4倍。当然答案是错误的,但是我没有一个没有复杂操作符调用开销的代码基线。如果我能弄明白如何内联复杂的数学运算,这应该是代码可以得到的最快速度。4x的因子仍然使代码比等效的Fortran慢得多

所以最后的答案是,这是不可能做到的。对于时间很重要的严重数字运算,C#不能提供答案。我相信必须坚持用本机FORTRAN或C++。
我感谢大家提出的提高C#numerics运算速度的建议。

我用复数运算代替了双倍运算,对算法进行了计时,速度提高了4倍。当然答案是错误的,但是我没有一个没有复杂操作符调用开销的代码基线。如果我能弄明白如何内联复杂的数学运算,这应该是代码可以得到的最快速度。4x的因子仍然使代码比等效的Fortran慢得多

所以最后的答案是,这是不可能做到的。对于时间很重要的严重数字运算,C#不能提供答案。我相信必须坚持用本机FORTRAN或C++。
我感谢大家为提高C#numerics的速度提供的建议。

你能发布一些示例吗?有很多免费的和商业的.NET分析器-为什么不试试呢?还有-这种缓慢被认为是一个问题吗?或者你是为了优化而优化的?@BrianRasmussen:除了使用探查器来确定需要很长时间的具体线条。没有更多的细节-我认为没有人能帮上忙。请从举例说明计算的性质开始。也许你可以使用矢量化?也完全可能是内存访问模式不好,或者创建了太多的结构或其他东西。还有,你所说的“已经提供了一个C#struct来处理这个数学问题”是什么意思?你能发布一些示例吗?有很多免费的和商业的.NET分析器-为什么不试试呢?还有-这种缓慢被认为是一个问题吗?或者你是为了优化而优化的?@BrianRasmussen:除了使用探查器来确定需要很长时间的具体线条。没有更多的细节-我认为没有人能帮上忙。请从举例说明计算的性质开始。也许你可以使用矢量化?也完全可能是内存访问模式不好,或者创建了太多的结构或其他东西。另外,你所说的“提供了一个C#struct来处理这个数学运算”是什么意思?我不认为Fortran DLL的速度慢是由于数据编组造成的,因为我使用嵌入