Skip to content

Structs equality using Equals method – performance

Introduction

The post title might seem to be a little enigmatic. How does the Equals method have something to do with the program performance? Well, It turns out there is one caveat that can impact the program execution… A LOT. While I was reading the official Microsoft docs I found an interesting part regarding Value Types and the Equals method.

Guidelines for Value Types

The section title is the same as in the Microsoft docs (you can find the section at the very end of the docs page). The guidelines says the following: “If you are defining a value type that includes one or more fields whose values are reference types, you should override Equals(Object). The Equals(Object) implementation provided by ValueType performs a byte-by-byte comparison for value types whose fields are all value types, but it uses reflection to perform a field-by-field comparison of value types whose fields include reference types.” . What does that really mean? Read on…

Let’s consider the following scenario. We implement some kind of particle system. As we know there are many particles in the universe so it is not surprising to process particles e.g. in billions. One of the execution path could be as simple as presented below:

As mentioned, such a scenario could execute the Equals method billion times.

The particle is represented as a structure that looks like this:

public struct ParticleWithoutEquals
{
    private readonly int _x;
    private readonly InternalData _internalData;

    public ParticleWithoutEquals(int x, InternalData internalData)
    {
        _x = x;
        _internalData = internalData;
    }
}

It has two fields: value type (int) and reference type (InternalData). If at least one field is reference type the default Equals implementation uses reflection. All I want you to remember now is that reflection is sluggish. To avoid the reflection being used we can override the Equals method as mentioned in the docs.

public struct ParticleWithEquals
{
    private readonly int _x;
    private readonly InternalData _internalData;

    public ParticleWithEquals(int x, InternalData internalData)
    {
        _x = x;
        _internalData = internalData;
    }

    public override bool Equals(object obj) =>
        obj is ParticleWithEquals particleWithEquals &&
        Equals(particleWithEquals);

    public bool Equals(ParticleWithEquals particleWithEquals) =>
        particleWithEquals._x.Equals(_x) &&
        particleWithEquals._internalData.Equals(_internalData);
}

Performance

I’ve prepared simple example. You can get it from my github, project: compare-objects, test project: compare-objects-tests . The program measures the execution time of the Equals method for ParticleWithoutEquals and for ParticleWithoutEquals structures. The program invokes the Equals method 1_000_000_000 times. The execution time difference (in release mode) is pretty impressive:

Well, 3.8 seconds vs 5:35.2 minutes. I leave to you the ratio calculation.

Summary

When it comes to use structures it is worth to remember that the default Equals method uses reflection if there is at least one field of reference type. In the provided example the Equals method result are the same in both cases, with and without override (see the tests: compare-objects-tests project), but the execution time vary greatly.

Have a nice day, bye!

Published in.NET

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *