IndexOf("x") v IndexOf('x') in .NET 8




Date Added (UTC):

08 Apr 2024 @ 00:55

Date Updated (UTC):

08 Apr 2024 @ 01:12


.NET Version(s):

.NET 8

Tag(s):

#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;

[MemoryDiagnoser]
[Config(typeof(Config))]
[HideColumns("Error", "StdDev", "Median", "RatioSD")]
public class IndexOfStringvIndexOfChar
{
    private readonly string _haystack = "All we have to decide is what to do with the time that is given us.";

    [Benchmark(Baseline = true)]
    public int IndexOfString() => _haystack.IndexOf("v");

    [Benchmark]
    public int IndexOfChar() => _haystack.IndexOf('v');

    private class Config : ManualConfig
    {
        public Config()
        {
            AddJob(Job.Default.WithId(".NET 8").WithRuntime(CoreRuntime.Core80));

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

// .NET 8
public int IndexOfString()
{
    return _haystack.IndexOf("v");
}
// .NET 8
public int IndexOfChar()
{
    return _haystack.IndexOf('v');
}

// .NET 8
.method public hidebysig 
    instance int32 IndexOfString () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 1a 00 00 00 01 5f 01 00 54 02 08 42 61 73
        65 6c 69 6e 65 01
    )
    // Method begins at RVA 0x2050
    // Code size 17 (0x11)
    .maxstack 8

    // sequence point: (line 27, col 35) to (line 27, col 57) in _
    IL_0000: ldarg.0
    IL_0001: ldfld string IndexOfStringvIndexOfChar::_haystack
    IL_0006: ldstr "v"
    IL_000b: callvirt instance int32 [System.Runtime]System.String::IndexOf(string)
    IL_0010: ret
}
// .NET 8
.method public hidebysig 
    instance int32 IndexOfChar () cil managed 
{
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 1d 00 00 00 01 5f 00 00
    )
    // Method begins at RVA 0x2062
    // Code size 14 (0xe)
    .maxstack 8

    // sequence point: (line 30, col 33) to (line 30, col 55) in _
    IL_0000: ldarg.0
    IL_0001: ldfld string IndexOfStringvIndexOfChar::_haystack
    IL_0006: ldc.i4.s 118
    IL_0008: callvirt instance int32 [System.Runtime]System.String::IndexOf(char)
    IL_000d: ret
}

// .NET 8 (X64)
IndexOfString()
    L0000: push rsi
    L0001: push rbx
    L0002: sub rsp, 0x38
    L0006: mov rbx, [rcx+8]
    L000a: mov esi, [rbx+8]
    L000d: call qword ptr [0x7ff8a869e1c0]
    L0013: mov rcx, rax
    L0016: mov rax, [rax]
    L0019: mov rax, [rax+0x48]
    L001d: call qword ptr [rax+0x30]
    L0020: mov rcx, rax
    L0023: mov [rsp+0x20], esi
    L0027: xor r8d, r8d
    L002a: mov [rsp+0x28], r8d
    L002f: mov r8, 0x1ba280654e8
    L0039: mov r8, [r8]
    L003c: mov rdx, rbx
    L003f: xor r9d, r9d
    L0042: cmp [rcx], ecx
    L0044: call qword ptr [0x7ff8a88b4750]
    L004a: nop
    L004b: add rsp, 0x38
    L004f: pop rbx
    L0050: pop rsi
    L0051: ret
// .NET 8 (X64)
IndexOfChar()
    L0000: sub rsp, 0x28
    L0004: mov rdx, [rcx+8]
    L0008: lea rcx, [rdx+0xc]
    L000c: mov r8d, [rdx+8]
    L0010: mov edx, 0x76
    L0015: call qword ptr [0x7ff8a870de00]
    L001b: nop
    L001c: add rsp, 0x28
    L0020: ret


Benchmark Description:


Use the char overload when only looking for singe characters, it's much faster.

### General Setup Overview The benchmark setup provided is designed to measure and compare the performance of two methods for finding the index of a character or string within a larger string in C#. It uses the BenchmarkDotNet library, a powerful tool for benchmarking .NET code, to conduct these measurements. The setup includes several key components: - **.NET Version**: The benchmark specifies the use of .NET 8 (CoreRuntime.Core80), indicating that it's targeting a future version of .NET (as of my last update in April 2023, .NET 8 has not been released). This choice of runtime ensures that the benchmark results are relevant for applications planning to use the latest .NET technologies. - **BenchmarkDotNet Configuration**: The benchmark class is annotated with attributes that configure how BenchmarkDotNet runs the tests and reports the results. The `MemoryDiagnoser` attribute is used to include memory usage metrics in the output. The `Config` attribute specifies a custom configuration class that sets up the runtime environment and modifies the summary report style. - **Custom Configuration**: The `Config` class within the benchmark specifies the use of .NET 8 and adjusts the summary report to focus on percentage ratios while hiding columns like Error, StdDev, Median, and RatioSD. This simplification aims to highlight the performance differences between the two methods being tested. ### Benchmark Methods Rationale #### 1. `IndexOfString()` - **Purpose**: This method measures the performance of finding the first occurrence of a string ("v") within a larger string using the `IndexOf` method. - **Performance Aspect**: It tests how efficiently the .NET runtime can locate a substring within a string. This operation is common in text processing, parsing, and data manipulation tasks, making its performance critical for applications that deal with large amounts of text. - **Expected Insights**: The benchmark will provide insights into the time and memory overhead associated with substring search operations. Since this method searches for a string, it might be slower and potentially use more memory than searching for a single character due to the need to match a sequence of characters. #### 2. `IndexOfChar()` - **Purpose**: This method benchmarks the performance of finding the first occurrence of a character ('v') within a larger string using the `IndexOf` method. - **Performance Aspect**: It focuses on measuring the efficiency of character search within a string. This is a fundamental operation in many text processing scenarios, and its performance can significantly impact the overall speed of applications that frequently search for characters within strings. - **Expected Insights**: Results from this benchmark will show how quickly and with how much memory overhead the .NET runtime can locate a single character within a string. This method is expected to be faster and possibly more memory-efficient than searching for a substring, as it involves a simpler comparison operation. ### Summary Running these benchmarks will provide valuable insights into the performance characteristics of substring versus character search operations in .NET. Specifically, developers can understand the trade-offs in speed and memory usage between these two approaches, helping them make informed decisions when optimizing their applications. The focus on .NET 8 ensures that the results are forward-looking and applicable to developers working with the latest .NET technologies.


Benchmark Comments: