A question came up in an online discussion about the relative performance of using a bare primitive type vs wrapping that type into a struct. Wrapping the type is a common technique used to avoid runtime errors, enlisting the compiler to enforce the correct semantics at compile time instead. But, sometimes people worry about the performance cost of doing this.

My experience is that type safety usually results in code that scales better. For example, it might enable better runtime optimization, or it might reduce code duplication, or it might permit better algorithms.

In this case, we’re explicitly going to look at the micro-optimization of wrapping a primitive type into a struct. Is there actually a performance problem to address?

I didn’t think there was.

But, facts always trump opinions (or guesses), so I decided to write a benchmark to find out.

I created a simple project using BenchMarkDotNet and made sure to include benchmarking of allocations as well as execution time. If you’re interested, I published the source as a gist.

For the primitive type case, the method Foo takes a string and returns its hash:

private int Foo(string s)
{
    return s.GetHashCode();
}

I originally used .Length the but the optimizer is just a little too clever and it managed to optimize away the method completely.

For comparison, I created a simple wrapper struct, and an overload of Foo:

private int Foo(WrapString s)
{
    return s.Value.GetHashCode();
}

public readonly struct WrapString
{
    public readonly string Value;

    public WrapString(string value)
    {
        Value = value;
    }
}

Then, because I was curious, I defined a generic struct for comparison:

private int Foo(Wrap<string> s)
{
    return s.Value.GetHashCode();
}

public readonly struct Wrap<T>
{
    public readonly T Value;

    public Wrap(T value)
    {
        Value = value;
    }
}

Here are the results of running the Benchmark:

| Method             | Mean     | Error     | StdDev    | Median   | Allocated |
|--------------------|----------|-----------|-----------|----------|-----------|
| Primitive          | 6.092 ns | 0.2577 ns | 0.5437 ns | 5.875 ns | 0 B       |
| UsingStruct        | 5.916 ns | 0.1123 ns | 0.1050 ns | 5.904 ns | 0 B       |
| UsingGenericStruct | 5.846 ns | 0.0706 ns | 0.0660 ns | 5.829 ns | 0 B       |

I did all of this on my laptop, and while I was “hands off” while the benchmark was running, this isn’t a machine that’s been configured to eliminate as much background activity as possible. The only tuning I did was to configure power management for best performance.

It’s initially interesting to see that the struct versions appear to run just a hair faster, but when you look at the error and standard deviation values, it becomes pretty clear that there’s no meaningful difference in performance.

Crucially, they all run with zero memory allocation, so there’s absolutely no impact on the heap.

Comments

blog comments powered by Disqus
Next Post
Why we need better validation  07 Jul 2018
Prior Post
Solving my Assembly Load Issues  23 Jun 2018
Related Posts
Vocabulary Set  16 Mar 2019
Vocabulary Word  09 Mar 2019
WordTutor Revisted  02 Mar 2019
Converting a List to a Queue  23 Feb 2019
Explicit Interfaces and Delegate Properties  16 Feb 2019
Extension Methods  19 Jan 2019
Generating Hash Codes  12 Jan 2019
The Problem with Equality  05 Jan 2019
Queue Equality  29 Dec 2018
Smarter Queue Enumeration  22 Dec 2018
More csharp posts »
Archives
June 2018
2018