Array bounds check efficiency in .net 4 and above

后端 未结 4 981
离开以前
离开以前 2020-12-05 06:19

I\'m interested in how efficient low-level algorithms can be in .net. I would like to enable us to choose to write more of our code in C# rather than C++ in the future, but

4条回答
  •  庸人自扰
    2020-12-05 06:51

    One way to be sure that bounds checking is not performed is to use pointers, which you can do in C# in unsafe mode (this requires you to set a flag in the project properties):

    private static unsafe double SumProductPointer(double[] X, double[] Y)
    {
        double sum = 0;
        int length = X.Length;
        if (length != Y.Length)
            throw new ArgumentException("X and Y must be same size");
        fixed (double* xp = X, yp = Y)
        {
            for (int i = 0; i < length; i++)
                sum += xp[i] * yp[i];
        }
        return sum;
    }
    

    I tried measuring your original method, your method with the X.Length change and my code using pointers, compiled both as x86 and x64 under .Net 4.5. Specifically, I tried computing the method for vectors of length 10 000 and ran the method 10 000 times.

    The results are pretty much in line with Michael Liu's answer: there is no measurable difference between the three methods, which means that bounds checking either isn't done or that its effect on performance is insignificant. There was measurable difference between x86 and x64 though: x64 was about 34 % slower.

    Full code I used:

    static void Main()
    {
        var random = new Random(42);
        double[] x = Enumerable.Range(0, 10000).Select(_ => random.NextDouble()).ToArray();
        double[] y = Enumerable.Range(0, 10000).Select(_ => random.NextDouble()).ToArray();
    
        // make sure JIT doesn't affect the results
        SumProduct(x, y);
        SumProductLength(x, y);
        SumProductPointer(x, y);
    
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (int i = 0; i < 10000; i++)
        {
            SumProduct(x, y);
        }
        Console.WriteLine(stopwatch.ElapsedMilliseconds);
        stopwatch.Restart();
        for (int i = 0; i < 10000; i++)
        {
            SumProductLength(x, y);
        }
        Console.WriteLine(stopwatch.ElapsedMilliseconds);
        stopwatch.Restart();
        for (int i = 0; i < 10000; i++)
        {
            SumProductPointer(x, y);
        }
        Console.WriteLine(stopwatch.ElapsedMilliseconds);
    }
    
    private static double SumProduct(double[] X, double[] Y)
    {
        double sum = 0;
        int length = X.Length;
        if (length != Y.Length)
            throw new ArgumentException("X and Y must be same size");
        for (int i = 0; i < length; i++)
            sum += X[i] * Y[i];
        return sum;
    }
    
    private static double SumProductLength(double[] X, double[] Y)
    {
        double sum = 0;
        if (X.Length != Y.Length)
            throw new ArgumentException("X and Y must be same size");
        for (int i = 0; i < X.Length; i++)
            sum += X[i] * Y[i];
        return sum;
    }
    
    private static unsafe double SumProductPointer(double[] X, double[] Y)
    {
        double sum = 0;
        int length = X.Length;
        if (length != Y.Length)
            throw new ArgumentException("X and Y must be same size");
        fixed (double* xp = X, yp = Y)
        {
            for (int i = 0; i < length; i++)
                sum += xp[i] * yp[i];
        }
        return sum;
    }
    

提交回复
热议问题