Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are structs slower than classes?

Tags:

c#

struct

I'm creating a C# console-app. I have some critical paths and thought that creating structs would be faster than creating classes since I would not need garbage collection for structs. In my test however I found the opposite.

In the test below, I create 1000 structs and 1000 classes.

class Program
{
    static void Main(string[] args)
    {
        int iterations = 1000;
        Stopwatch sw = new Stopwatch();

        sw.Start();
        List<Struct22> structures = new List<Struct22>();
        for (int i = 0; i < iterations; ++i)
        {
            structures.Add(new Struct22());
        }
        sw.Stop();
        Console.WriteLine($"Struct creation consumed {sw.ElapsedTicks} ticks");

        Stopwatch sw2 = new Stopwatch();
        sw2.Start();
        List<Class33> classes = new List<Class33>();
        for (int i = 0; i < iterations; ++i)
        {
            classes.Add(new Class33());
        }
        sw2.Stop();
        Console.WriteLine($"Class creation consumed {sw2.ElapsedTicks} ticks");


        Console.ReadLine();
    }
}

My classe / struct are simple:

class Class33
{
    public int Property { get; set; }
    public int Field;
    public void Method() { }
}

struct Struct22
{
    public int Property { get; set; }
    public int Field;
    public void Method() { }
}

Results (drum roll please...)

Struct creating consuming 3038 ticks
Class creating consuming 404 ticks

So the question is: Why would it take close to 10x the amount of time for a Class than it does for a Struct ?

EDIT. I made the Program "Do something" by just assigning integers to the properties.

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

        sw.Start();
        List<Struct22> structures = new List<Struct22>();
        for (int i = 0; i < iterations; ++i)
        {
            Struct22 s = new Struct22()
            {
                Property = 2,
                Field = 3
            };
            structures.Add(s);
        }
        sw.Stop();
        Console.WriteLine($"Struct creating consuming {sw.ElapsedTicks} ticks");

        Stopwatch sw2 = new Stopwatch();
        sw2.Start();
        List<Class33> classes = new List<Class33>();
        for (int i = 0; i < iterations; ++i)
        {
            Class33 c = new Class33()
            {
                Property = 2,
                Field = 3
            };
            classes.Add(c);
        }
        sw2.Stop();
        Console.WriteLine($"Class creating consuming {sw2.ElapsedTicks} ticks");


        Console.ReadLine();
    }

and the result is astounding to me. Classes are still at least 2x but the simple fact of assigning integers had a 20x impact!

Struct creating consuming 903456 ticks
Class creating consuming 4345929 ticks

EDIT: I removed references to Methods so there are no reference types in my Class or Struct:

class Class33
{
    public int Property { get; set; }
    public int Field;
}

struct Struct22
{
    public int Property { get; set; }
    public int Field;
}
like image 701
Ed Landau Avatar asked Oct 29 '22 16:10

Ed Landau


1 Answers

The performance difference can probably (or at least in part) be explained by a simple example.

For structures.Add(new Struct22()); this is what really happens:

  • A Struct22 is created and intialized.
  • The Add method is called, but it receives a copy because the item is a value type.

So calling Add in this case has overhead, incurred by making a new Struct22 and copying all fields and properties into it from the original.


To demonstrate, not focusing on speed but on the fact that copying takes place:

private static void StructDemo()
{
    List<Struct22> list = new List<Struct22>();

    Struct22 s1 = new Struct22() { Property = 2, Field = 3 };  // #1
    list.Add(s1);                            // This creates copy #2
    Struct22 s3 = list[0];                   // This creates copy #3

    // Change properties:
    s1.Property = 777;
    // list[0].Property = 888;    <-- Compile error, NOT possible
    s3.Property = 999;

    Console.WriteLine("s1.Property = " + s1.Property);
    Console.WriteLine("list[0].Property = " + list[0].Property);
    Console.WriteLine("s3.Property = " + s3.Property);
}

This will be the output, proving that both Add() and the use of list[0] caused copies to be made:

s1.Property = 777
list[0].Property = 2
s3.Property = 999

Let this be a reminder that the behaviour of structs can be substantially different compared to objects, and that performance should be just one aspect when deciding what to use.

like image 151
Peter B Avatar answered Nov 15 '22 04:11

Peter B