Structs versus Classes in C#

As a C# Performance Architect, your job is to create solution architectures that provide the best possible performance for your business or organization. And to do your job well, you’ll need a solid understanding of basic C# code optimizations.

In this post, we’ll look at structs and classes in C#. Understanding the performance difference between these two will help you pick the correct code for every occasion.

Take a look at the following code.

public class PointClass
{
    public int X { get; set; }
    public int Y { get; set; }

    public PointClass(int x, int y)
    {
        X = x;
        Y = y;
    }
}

It’s a simple class that contains two public integer properties and a constructor to initialize them. You’ll typically find classes like this in graph libraries, to store data points in a chart.

But wait! There’s another way of doing this. Check out this code:

public struct PointStruct
{
    public int X { get; set; }
    public int Y { get; set; }

    public PointStruct(int x, int y)
    {
        X = x;
        Y = y;
    }
}

It’s the exact same code, I just changed class to struct.

So what do you think is better? A class or a struct?

In both cases, I have a data structure that contains two integers for storing an X- and Y coordinate pair. In terms of memory use and code performance, you’d expect similar results, right?

Well, let’s find out. I’ve coded a quick benchmark to compare the two:

Did you see the results? The class benchmark took 111 milliseconds. But the struct benchmark took only 6 milliseconds, so structs are 18.5 times faster than classes!

As an extra twist, I added a finalizer to the PointClass class. That benchmark took 246 milliseconds, so the finalizer made my code run another 2.2 times slower.

That’s a huge difference.

Here’s what’s going on. When I store a list of PointClass instances on the heap, the memory layout looks like this:

The list reference is in a local variable, so it’s stored on the stack. It points to a List<PointClass> instance on the heap.

But here’s the twist: PointClass is a reference type, so it’s stored elsewhere on the heap. The list only maintains an array of object references that point to PointClass instances stored elsewhere on the heap.

When you access a specific item in the list, the .NET runtime needs to first calculate where that list item is stored, then retrieve that particular object reference, and then ‘follow’ the reference to get at the PointClass instance.

When the list goes out of scope, the .NET garbage collector has to dispose up the list and every single PointClass instance to reclaim the memory. When I added a finalizer, I slowed that process down even further. The .NET Framework runs finalizers on a single thread, so that thread had to process each list item in turn before it could reclaim the memory.

Now compare that to the memory layout of a list of structs:

Structs are value types, which means they are stored inline inside their containing data type. So now all PointStruct instances are stored inside the list itself. There is only a single object on the heap.

When you access a specific item in the list, the .NET runtime calculates where that list item is stored, and then retrieve the struct directly because it’s stored right there, inside the list array.

And when the list goes out of scope, the .NET garbage collector now only needs to dispose a single object.

All these savings add up. My benchmarks repeatedly filled up huge lists with structs and classes, and it made the class-benchmark run more than 18 times slower than the struct-benchmark.

So when should you use a struct, and when should you use a class?

Here’s what you need to do:

  • When you’re storing more than 30-40 bytes of data, use a class.
  • When you’re storing reference types, use a class.
  • When your list isn’t very large, use a class.
  • When you list is long-lived, use a class.
  • In all other cases, use structs instead.

 

 

Would you like to know more? I’ve created a series of blog posts on C# performance optimization. Each post is based on content from my courses and from actual techniques I’ve used in the field.

Take a look: