Why aren't my benchmark results faster in Release compared to Debug?
07:32 05 Jan 2026

Setup

I have the following project: IvanStoychev.Useful.String.Extensions

It's just a bunch of string helper methods.

Such as:

public static bool ContainsAny(this string str, IEnumerable keywords, StringComparison comparison = StringComparison.Ordinal)   
{
    Validate.OriginalInstanceNotNull(str);
    Validate.NotNull(keywords);
    Validate.IEnumNotEmpty(keywords);
    Validate.EnumContainsValue(comparison);

    foreach (var word in keywords)
    {
        Validate.NotNullMember(word, nameof(keywords));
        if (str.Contains(word, comparison))
            return true;
    }

    return false;
}

The "Validate" class just performs checks and throws an exception if they don't hold up. Apart from that it's just a foreach using the original "Contains" method.

Aim

I wanted to benchmark my library in Debug vs Release configuration and in .Net 8 vs .Net 9, expecting the results to be better in Release and in .Net 9.

Benchmarking

I add net8-DBG to the name/version of my library and I compile it by using the command dotnet pack -c Release -o out, replacing "Release" with "Debug" and copying the output package file from the "out" directory to another local directory and I point my Benchmarker project to that directory as its only nuget source and install/update the package version in the Benchmarker project. I do this once for .Net 8 and then I change the version in the library project csproj file to .Net 9 and repeat (changing "net8" to "net9" in the name/version).

I have not specified the property in my csproj so I would expect the default behaviour of the "Release" configuration being more optimized than the "Debug" one. Yet the results are not such.

I'm using the following Benchmarker code:

class Program
{
    static void Main(string[] args)
    {
        //var summary = BenchmarkRunner.Run(ManualConfig.Create(DefaultConfig.Instance).WithOptions(ConfigOptions.DisableOptimizationsValidator)); --- I use this one for Debug versions
        var summary = BenchmarkRunner.Run();
    }
}

[MemoryDiagnoser]
public class Benchmarker
{
    const string constantStr = "<2394 char string>";

    [Benchmark]
    public void Remove()
    {
        constantStr.Remove("a");
    }

    [Benchmark]
    public void Replace()
    {
        constantStr.Replace("a", "e");
    }

    [Benchmark]
    public void Substring()
    {
        constantStr.Substring("user", "Library");
    }

    [Benchmark]
    public void ContainsAny()
    {
        constantStr.ContainsAny(["zwarghsgorp"]);
    }

    [Benchmark]
    public void Trim()
    {
        constantStr.Trim("DescriptionI");
    }
}

Results

8-DBG

Method Mean Error StdDev Median Gen0 Gen1 Allocated
Remove 6,494.7 ns 44.50 ns 39.45 ns 6,496.8 ns 0.5417 - 4536 B
Replace 206.5 ns 4.14 ns 8.65 ns 201.6 ns 0.5691 - 4760 B
Substring 1,252.9 ns 25.11 ns 58.69 ns 1,231.3 ns 0.7019 - 5888 B
ContainsAny 176.3 ns 0.76 ns 0.63 ns 176.1 ns 0.0095 - 80 B
Trim 377.8 ns 7.18 ns 7.05 ns 378.3 ns 0.5746 0.0100 4808 B

8-RLS

Method Mean Error StdDev Gen0 Gen1 Allocated
Remove 6,344.7 ns 20.10 ns 16.79 ns 0.5417 - 4536 B
Replace 214.4 ns 4.21 ns 4.14 ns 0.5691 - 4760 B
Substring 1,168.3 ns 17.14 ns 16.03 ns 0.7019 - 5888 B
ContainsAny 206.9 ns 1.72 ns 1.53 ns 0.0095 - 80 B
Trim 315.2 ns 5.82 ns 5.16 ns 0.5746 0.0100 4808 B

9-DBG

Method Mean Error StdDev Median Gen0 Gen1 Allocated
Remove 6,507.8 ns 59.81 ns 55.95 ns 6,507.8 ns 0.5417 - 4536 B
Replace 208.2 ns 4.21 ns 9.32 ns 202.1 ns 0.5691 - 4760 B
Substring 1,229.1 ns 5.49 ns 4.59 ns 1,228.1 ns 0.7019 - 5888 B
ContainsAny 172.6 ns 3.26 ns 3.04 ns 171.4 ns 0.0095 - 80 B
Trim 379.9 ns 7.29 ns 7.48 ns 380.7 ns 0.5746 0.0100 4808 B

9-RLS

Method Mean Error StdDev Median Gen0 Gen1 Allocated
Remove 6,655.3 ns 132.70 ns 136.27 ns 6,656.5 ns 0.5417 - 4536 B
Replace 211.2 ns 4.25 ns 9.15 ns 211.1 ns 0.5691 - 4760 B
Substring 1,155.1 ns 21.20 ns 41.35 ns 1,133.6 ns 0.7019 - 5888 B
ContainsAny 208.3 ns 0.72 ns 0.60 ns 208.4 ns 0.0095 - 80 B
Trim 312.8 ns 6.11 ns 6.00 ns 313.4 ns 0.5746 0.0100 4808 B

As you can see while some methods are faster other are slower under "Release". Furthermore seeing all the improvements that went in .Net 9 I expected it to be MUCH faster than 8 but, again, the results are very similar.

Question

Why is there no significant performance benefit under "Release" and .Net 9?

I thought perhaps it might be because I am not doing much else other than foreaching over built-in string extension methods, which might be as optimized as can be but am not sure.

And even if that is the case the "Substring" method ought to have space for improvement under "Release" and .Net 9.

c# optimization benchmarking