Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: struct constructor performance

Why creating struct with constructor more slower than direct assignment? In code below I get 10 second with custom constructor and 6 second without! Pure cycle take a 5 second. Custom constructor five (!sic) times slower than direct access.

Is there any hack or something to speed up a custom constructor?

class Program
{
    public struct Point
    {
        public int x, y;

        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();

        sw.Start();
        for(int i =0; i < int.MaxValue; i++)
        {
            var a = new Point(i, i);
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);

        sw.Restart();
        for (int i = 0; i < int.MaxValue; i++)
        {
            var a = new Point();
            a.x = i;
            a.y = i;
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.ReadLine();
    }
}
like image 696
Grey Avatar asked Mar 05 '17 14:03

Grey


2 Answers

In code below i get 10 second with custom constructor

That is not code that takes 10 seconds, unless you have a very old machine. Tells us what you did wrong, you are running the unoptimized Debug build or run with the debugger attached. The kind of scenario where the optimizer that is built into the jitter is not allowed to do its job.

Put a loop around the code to run it 10 times, helps to get rid of startup overhead and measuring the standard deviation, always very high on very fast code like this. Switch to the Release build and run it with Ctrl+F5. Now you get the real perf of this code, ought to be south of ~1.5 seconds. That is just how long the loops take, none of the Point code remains, removed by the optimizer. Which it could do because the a variable is not used anywhere and the constructor has no observable side-effects.

Having the code you want to profile completely removed is a classic benchmark hazard. You can prevent that from happening by hoisting the a variable out of the loop and using:

  Console.WriteLine("{0}", sw.ElapsedMilliseconds, a);

Now the optimizer cannot eliminate the Point code anymore since the variable is used beyond the loop. You'll know you are benchmarking it right if it is still just as fast. It just doesn't matter how you initialize the struct. More about what the jitter optimizer does in this post.

like image 60
Hans Passant Avatar answered Oct 16 '22 21:10

Hans Passant


Your profiling code simplifies a lot of things, but it also makes the measurement quite inaccurate. I'd suggest using tools like BenchmarkDotNet to get more accurate results.

A quick test shows that direct assignments are a bit faster, but nothing like 5x you're claiming:

[BenchmarkDotNet.Attributes.Benchmark(Baseline = true)]
public static Point GetPoint()
{
    return new Point(x, y);
}

[BenchmarkDotNet.Attributes.Benchmark]
public static Point GetPoint2()
{
    Point point = new Point();
    point.x = x;
    point.y = y;
    return point;
}
    Method |       Mean |    StdDev | Scaled | Scaled-StdDev |
---------- |----------- |---------- |------- |-------------- |
  GetPoint | 10.2038 ns | 0.2593 ns |   1.00 |          0.00 |
 GetPoint2 |  9.3272 ns | 0.0767 ns |   0.91 |          0.02 |

That's 10% difference, not 5x.

like image 26
MarcinJuraszek Avatar answered Oct 16 '22 20:10

MarcinJuraszek