.NET 9 String.Contains("X") performance improvement




Date Added (UTC):

08 Apr 2024 @ 02:44

Date Updated (UTC):

23 Apr 2024 @ 16:21


.NET Version(s):

.NET 8 .NET 9

Tag(s):

#.Net9PerfImprovement #StringComparison #Strings


Added By:
Profile Image

Blog   
Ireland    
.NET Developer and tech lead from Ireland!

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs; 
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;

BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);

[Config(typeof(Config))]
[HideColumns(Column.Job, Column.RatioSD, Column.AllocRatio)]
[MemoryDiagnoser]
public class StringContains
{
    readonly string _inputString =
        "Permission is hereby granted, free of charge, to any person obtaining " +
        "a copy of the Unicode data files and any associated documentation";

    [Benchmark]
    public bool Contains_string() => _inputString.Contains("X");
    private class Config : ManualConfig
    {
        public Config()
        {
            AddJob(Job.Default.WithId(".NET 8").WithRuntime(CoreRuntime.Core80).AsBaseline());
            AddJob(Job.Default.WithId(".NET 9").WithRuntime(CoreRuntime.Core90));

            SummaryStyle =
                SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend);
        }
    }
}

// .NET 8
public bool Contains_string()
{
    return _inputString.Contains("X");
}

// .NET 8
.method public hidebysig 
    instance bool Contains_string () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 1f 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2076
    // Code size 17 (0x11)
    .maxstack 8

    // sequence point: (line 32, col 38) to (line 32, col 64) in _
    IL_0000: ldarg.0
    IL_0001: ldfld string StringContains::_inputString
    IL_0006: ldstr "X"
    IL_000b: callvirt instance bool [System.Runtime]System.String::Contains(string)
    IL_0010: ret
}

// .NET 8 (X64)
Contains_string()
    L0000: sub rsp, 0x28
    L0004: mov rcx, [rcx+8]
    L0008: mov r8, 0x1fa4d5c96f8
    L0012: mov r8, [r8]
    L0015: add r8, 0xc
    L0019: mov edx, [rcx+8]
    L001c: add rcx, 0xc
    L0020: mov r9d, 1
    L0026: call qword ptr [0x7ffba96a51a0]
    L002c: not eax
    L002e: shr eax, 0x1f
    L0031: add rsp, 0x28
    L0035: ret


Benchmark Description:


This performance optimization redirects calls into string.Contains("x") to string.Contains('x') for single char literals. Even though devs should ideally be using the right API I really like when the runtime (and/or compiler) can do these optimizations for us. [PR](https://github.com/dotnet/runtime/pull/97632)

### General Setup Overview The benchmark setup provided is designed to evaluate the performance of a specific operation in .NET applications, specifically the `String.Contains` method. This is done using the BenchmarkDotNet library, a powerful tool for benchmarking .NET code. The configuration and execution of the benchmarks are defined using attributes and a custom configuration class. - **.NET Versions:** The benchmarks are configured to run against two different versions of the .NET runtime: .NET 8 and .NET 9. This allows for comparison across different runtime versions to observe performance improvements or regressions. - **Configuration (Config class):** A custom configuration class named `Config` is defined, which extends `ManualConfig`. Within this class, two jobs are added, one for each .NET runtime version (.NET 8 as the baseline and .NET 9). The `SummaryStyle` is set to display trends in the ratio of performance between different runs, which helps in easily spotting performance changes. - **BenchmarkDotNet Attributes:** - `@Config(typeof(Config))` specifies the configuration to use for the benchmark class. - `@HideColumns` hides specific columns in the output report, focusing on the most relevant data. - `@MemoryDiagnoser` enables memory diagnostics, allowing the benchmark to report on memory allocations. ### Benchmark Method: `Contains_string` #### Purpose The `Contains_string` method is designed to benchmark the performance of the `String.Contains` method in .NET. This method checks if the input string contains a specific substring ("X" in this case). #### Performance Aspect - **String Search Performance:** It measures how efficiently the .NET runtime can search for a substring within a larger string. This is a common operation in many applications, making it a valuable aspect to benchmark. - **Memory Usage:** With the `MemoryDiagnoser` attribute, the benchmark also provides insights into the memory allocations involved in performing the `Contains` operation. This can help in understanding the memory efficiency of string operations in .NET. #### Expected Results and Insights - **Runtime Comparison:** By running this benchmark against different .NET versions, you can observe how the performance of the `String.Contains` method has evolved. Improvements in runtime or library implementations can lead to faster execution times in newer versions. - **Memory Allocation Insights:** The memory diagnostics will show if there are significant differences in memory allocations when performing string search operations across different .NET versions. Ideally, efficient memory usage is desirable alongside fast execution times. - **Performance Trends:** The configuration is set to highlight performance trends, so you should expect to see whether the operation becomes faster, slower, or remains consistent across the versions tested. This can inform decisions on upgrading projects to newer .NET versions based on performance considerations. In summary, this benchmark setup is tailored to provide detailed insights into the performance of string search operations across different .NET runtime versions, focusing on execution speed and memory efficiency. The insights gained can guide optimization efforts and inform decisions regarding .NET version upgrades in projects where string manipulation performance is critical.


Benchmark Comments: