---
title: "Does readonly make your code faster?"
description: "In this blog post we will discover whether or not the `readonly` modifier can make your code faster. So without further ado let's get started."
updated: 2024-04-23
tags: ["C#", ".NET", "Performance"]
canonical: "https://steven-giesel.com/blogPost/50b84029-4f1e-4e6d-8fd1-0311b0605562/does-readonly-make-your-code-faster"
---

# Does readonly make your code faster?

In this blog post we will discover whether or not the `readonly` modifier can make your code faster. So without further ado let's get started.

**Update 24th of April:** I originally used the `DEBUG` build for IL/JIT code, which is obviously never a good idea when you talk about performance.I did correct the blog post. Thanks [@lassevk](https://github.com/lassevk) for noticing!

## What is the `readonly` modifier?
The `readonly` modifier is a keyword in C# that can be applied to fields. When a field is marked as `readonly` it means that the field can only be assigned a value during the declaration or in the constructor of the class. Once the field has been assigned a value it cannot be changed.

An example:
```csharp
public class Class 
{
    private readonly int readonlyField = 100;
    private int nonReadonlyField = 100;
    
    public int RO() {
        return readonlyField + readonlyField;
    }
    
    public int NonRO() {
        return nonReadonlyField + nonReadonlyField;
    }
}
```

## Does `readonly` make your code faster?
To answer that question we have to look at the IL code that is generated by the C# compiler. Let's take a look at the IL code for the `RO` method and the `NonRO` method.

```csharp
.class public auto ansi beforefieldinit Class
    extends [System.Runtime]System.Object
{
    // Fields
    .field private initonly int32 readonlyField
    .field private int32 nonReadonlyField

    // Methods
    .method public hidebysig 
        instance int32 RO () cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 14 (0xe)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldfld int32 Class::readonlyField
        IL_0006: ldarg.0
        IL_0007: ldfld int32 Class::readonlyField
        IL_000c: add
        IL_000d: ret
    } // end of method Class::RO

    .method public hidebysig 
        instance int32 NonRO () cil managed 
    {
        // Method begins at RVA 0x205f
        // Code size 14 (0xe)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldfld int32 Class::nonReadonlyField
        IL_0006: ldarg.0
        IL_0007: ldfld int32 Class::nonReadonlyField
        IL_000c: add
        IL_000d: ret
    } // end of method Class::NonRO

    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x206e
        // Code size 23 (0x17)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldc.i4.s 100
        IL_0003: stfld int32 Class::readonlyField
        IL_0008: ldarg.0
        IL_0009: ldc.i4.s 100
        IL_000b: stfld int32 Class::nonReadonlyField
        IL_0010: ldarg.0
        IL_0011: call instance void [System.Runtime]System.Object::.ctor()
        IL_0016: ret
    } // end of method Class::.ctor

} // end of class Class
```

The methods are identical, well except for the field they are accessing. The only difference is int the declaration of the fields. The `readonlyField` is declared as `initonly` and the `nonReadonlyField` is not. The `initonly` keyword is used to mark a field as readonly (basically `readonly` in IL terms).

Now, there could be potentially some performance benefits to using `readonly` fields. The JIT compiler could potentially optimize the code better if it knows that a field is readonly. But in this case, the JIT compiler does not optimize the code differently. The code generated for the `RO` method and the `NonRO` method is identical.

Here some JIT code:
```csharp
Class.RO()
    L0000: mov eax, [ecx+4]
    L0003: add eax, eax
    L0005: ret

Class.NonRO()
    L0000: mov eax, [ecx+8]
    L0003: add eax, eax
    L0005: ret
```

It is again completely identical. The JIT compiler does not optimize the code differently if a field is marked as `readonly` (in this case). I also tried this with more complex objects/classes instead of value types, but the result was the same. I couldn't find a way to make the JIT compiler optimize the code differently if a field is marked as `readonly`.

If I run a benchmark with something like the above, you get what you would expect in this case - the somewhat same runtime:
```csharp
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<ManyFieldsBenchmark>();

public class ManyFieldsBenchmark
{
    private class ReadOnlyFields
    {
        public readonly int Field1 = 1;
        public readonly int Field2 = 2;
        public readonly int Field3 = 100;
        public readonly int Field4 = 300;
        public readonly int Field5 = 300;
        public readonly int Field6 = 300;
    }

    private class NonReadOnlyFields
    {
        public int Field1 = 1;
        public int Field2 = 2;
        public int Field3 = 100;
        public int Field4 = 300;
        public int Field5 = 300;
        public int Field6 = 300;
    }

    private readonly ReadOnlyFields readOnlyInstance = new();
    private readonly NonReadOnlyFields nonReadOnlyInstance = new();

    [Benchmark]
    public int AccessReadOnlyFields()
    {
        var sum = 0;
        for (var i = 0; i < 10000; i++)
        {
            sum += readOnlyInstance.Field1
                   + readOnlyInstance.Field2
                   + readOnlyInstance.Field3
                   + readOnlyInstance.Field4
                   + readOnlyInstance.Field5
                   + readOnlyInstance.Field6;
        }
        
        return sum;
    }

    [Benchmark]
    public int AccessNonReadOnlyFields()
    {
        var sum = 0;
        for (var i = 0; i < 10000; i++)
        {
            sum += nonReadOnlyInstance.Field1 
                   + nonReadOnlyInstance.Field2
                   + nonReadOnlyInstance.Field3 
                   + nonReadOnlyInstance.Field4
                   + nonReadOnlyInstance.Field5
                   + nonReadOnlyInstance.Field6;
        }
        
        return sum;
    }
}
```

```no-class
| Method                  | Mean     | Error    | StdDev   |
|------------------------ |---------:|---------:|---------:|
| AccessReadOnlyFields    | 17.58 us | 0.100 us | 0.094 us |
| AccessNonReadOnlyFields | 17.52 us | 0.052 us | 0.046 us |
```

We are in the margins of error. So if there is any performance benefit to using `readonly` fields, it is negligible in almost all cases. But then again, the main reason of using `readonly` fields is to make your code more robust and maintainable, not to make it faster. If you suspect that it can bring benefits then 
a. Measure it (create a baseline), implement the change and measure it again (against your baseline).
b. Inform me, as I would be interested in seeing a case where it does make a difference.