为什么我会看到使用本机代码增加约20%的速度?

Rob*_* H. 24 c# winforms

知道为什么这段代码:

extern "C" __declspec(dllexport) void Transform(double x[], double y[], int iterations, bool forward)
{
    long n, i, i1, j, k, i2, l, l1, l2;
    double c1, c2, tx, ty, t1, t2, u1, u2, z;

    /* Calculate the number of points */
    n = (long)pow((double)2, (double)iterations);

    /* Do the bit reversal */
    i2 = n >> 1;
    j = 0;
    for (i = 0; i < n - 1; ++i)
    {
        if (i < j)
        {
            tx = x[i];
            ty = y[i];
            x[i] = x[j];
            y[i] = y[j];
            x[j] = tx;
            y[j] = ty;
        }
        k = i2;
        while (k <= j)
        {
            j -= k;
            k >>= 1;
        }
        j += k;
    }

    /* Compute the FFT */
    c1 = -1.0; 
    c2 = 0.0;
    l2 = 1;
    for (l = 0; l < iterations; ++l)
    {
        l1 = l2;
        l2 <<= 1;
        u1 = 1; 
        u2 = 0;
        for (j = 0; j < l1; j++) 
        {
            for (i = j; i < n; i += l2) 
            {
                i1 = i + l1;
                t1 = u1 * x[i1] - u2 * y[i1];
                t2 = u1 * y[i1] + u2 * x[i1];
                x[i1] = x[i] - t1; 
                y[i1] = y[i] - t2;
                x[i] += t1;
                y[i] += t2;
            }
            z = u1 * c1 - u2 * c2;
            u2 = u1 * c2 + u2 * c1;
            u1 = z;
        }
        c2 = sqrt((1.0 - c1) / 2.0);
        if (forward) 
            c2 = -c2;
        c1 = sqrt((1.0 + c1) / 2.0);
    }

    /* Scaling for forward transform */
    if (forward)
    {
        for (i = 0; i < n; ++i)
        {
            x[i] /= n;
            y[i] /= n;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

运行速度比这段代码快20%?

public static void Transform(DataSet data, Direction direction)
{
    double[] x = data.Real;
    double[] y = data.Imag;
    data.Direction = direction;
    data.ExtremeImag = 0.0;
    data.ExtremeReal = 0.0;
    data.IndexExtremeImag = 0;
    data.IndexExtremeReal = 0;

    long n, i, i1, j, k, i2, l, l1, l2;
    double c1, c2, tx, ty, t1, t2, u1, u2, z;

    /* Calculate the number of points */
    n = (long)Math.Pow(2, data.Iterations);

    /* Do the bit reversal */
    i2 = n >> 1;
    j = 0;
    for (i = 0; i < n - 1; ++i)
    {
        if (i < j)
        {
            tx = x[i];
            ty = y[i];
            x[i] = x[j];
            y[i] = y[j];
            x[j] = tx;
            y[j] = ty;
        }
        k = i2;
        while (k <= j)
        {
            j -= k;
            k >>= 1;
        }
        j += k;
    }

    /* Compute the FFT */
    c1 = -1.0; 
    c2 = 0.0;
    l2 = 1;
    for (l = 0; l < data.Iterations; ++l)
    {
        l1 = l2;
        l2 <<= 1;
        u1 = 1; 
        u2 = 0;
        for (j = 0; j < l1; j++) 
        {
            for (i = j; i < n; i += l2) 
            {
                i1 = i + l1;
                t1 = u1 * x[i1] - u2 * y[i1];
                t2 = u1 * y[i1] + u2 * x[i1];
                x[i1] = x[i] - t1; 
                y[i1] = y[i] - t2;
                x[i] += t1;
                y[i] += t2;
            }
            z = u1 * c1 - u2 * c2;
            u2 = u1 * c2 + u2 * c1;
            u1 = z;
        }
        c2 = Math.Sqrt((1.0 - c1) / 2.0);
        if (direction == Direction.Forward) 
            c2 = -c2;
        c1 = Math.Sqrt((1.0 + c1) / 2.0);
    }

    /* Scaling for forward transform */
    if (direction == Direction.Forward)
    {
        for (i = 0; i < n; ++i)
        {
            x[i] /= n;
            y[i] /= n;
            if (Math.Abs(x[i]) > data.ExtremeReal)
            {
                data.ExtremeReal = x[i];
                data.IndexExtremeReal = (int)i;
            }
            if (Math.Abs(y[i]) > data.ExtremeImag)
            {
                data.ExtremeImag = y[i];
                data.IndexExtremeImag = (int)i;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

FFT http://www.rghware.com/fft.png

通过在我的应用程序中选择"Native DLL FFT",我创建了在图中间看到的CPU下降:

http://www.rghware.com/InstrumentTuner.zip(源代码)

我认为这将在大多数PC上运行.您需要安装DirectX.我在使用某些硬件的捕获设置时遇到了一些问题.捕获设置应该是可配置的,但应用程序的开发一直被这个有趣的发现所牵制.

无论如何,为什么我看到使用本机代码增加了20%的速度?面对我以前的一些假设,这似乎有所作为.

UPDATE

将函数转换为不安全的方法并修复long/int问题.新的不安全方法实际上比本机方法运行得更快(非常酷).

简介http://www.rghware.com/profile.png

很明显,数组绑定检查是这种FFT方法减慢20%的原因.由于它的性质,这种方法中的for循环无法优化.

谢谢大家的帮助.

Ree*_*sey 23

只看这段代码,我怀疑从我的经验来看,C++ - > C#的速度相当显着.

你要在C#例程的天真端口中遇到的一个主要问题是C#将在这里添加检查每个数组检查的边界.由于您永远不会以一种优化的方式循环遍历数组(详细信息请参阅此问题),因此几乎每个数组访问都将接收边界检查.

此外,这个端口非常接近来自C的1-> 1映射.如果你通过一个好的.NET分析器运行它,你可能会发现一些可以优化的好点,使其恢复到接近C++的速度.一两次调整(这几乎总是我在移植这样的例程时的经验).

但是,如果你想让它以几乎相同的速度运行,你可能需要将其转换为不安全的代码并使用指针操作而不是直接设置数组.这将消除所有边界检查问题,并恢复您的速度.


编辑:我看到一个更大的区别,这可能是你的C#不安全代码运行速度较慢的原因.

查看此页面有关C#与C++的比较,特别是:

"long类型:在C#中,long类型是64位,而在C++中,它是32位."

您应该将C#版本转换为使用int,而不是长.在C#中,long是64位类型.这实际上可能会对指针操作产生深远的影响,因为我相信你无意中在每次指针调用时都添加了long-> int转换(带溢出检查).

此外,当您处于此状态时,您可能希望尝试将整个函数包装在未经检查的块中.C++没有进行C#中的溢出检查.