StartsWith with StringComparison.OrdinalIgnoreCase versus ToUpper on .NET 8

Date Added (UTC):

08 Apr 2024 @ 01:08

Date Updated (UTC):

08 Apr 2024 @ 01:08

.NET Version(s):

.NET 8


#StringComparison #Strings

Added By:
Profile Image

.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 System;

[HideColumns("Error", "StdDev", "Median", "RatioSD")]
public class StartsWithOrdinalIgnoreCase
    private readonly string _input = "https://dot.net";

    [Benchmark(Baseline = true)]
    public bool IsHttps_ToUpper() => _input.ToUpperInvariant().StartsWith("HTTPS://");

    public bool IsHttps_StringComparison() => _input.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase);

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

            SummaryStyle =

// .NET 8
public bool IsHttps_ToUpper()
    return _input.ToUpperInvariant().StartsWith("HTTPS://");
// .NET 8
public bool IsHttps_StringComparison()
    return _input.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase);

// .NET 8
.method public hidebysig 
    instance bool IsHttps_ToUpper () cil managed 
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 1b 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 22 (0x16)
    .maxstack 8

    // sequence point: (line 28, col 38) to (line 28, col 86) in _
    IL_0000: ldarg.0
    IL_0001: ldfld string StartsWithOrdinalIgnoreCase::_input
    IL_0006: callvirt instance string [System.Runtime]System.String::ToUpperInvariant()
    IL_000b: ldstr "HTTPS://"
    IL_0010: callvirt instance bool [System.Runtime]System.String::StartsWith(string)
    IL_0015: ret
// .NET 8
.method public hidebysig 
    instance bool IsHttps_StringComparison () cil managed 
    .custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
        01 00 1e 00 00 00 01 5f 00 00
    // Method begins at RVA 0x2067
    // Code size 18 (0x12)
    .maxstack 8

    // sequence point: (line 31, col 47) to (line 31, col 112) in _
    IL_0000: ldarg.0
    IL_0001: ldfld string StartsWithOrdinalIgnoreCase::_input
    IL_0006: ldstr "HTTPS://"
    IL_000b: ldc.i4.5
    IL_000c: callvirt instance bool [System.Runtime]System.String::StartsWith(string, valuetype [System.Runtime]System.StringComparison)
    IL_0011: ret

// .NET 8 (X64)
    L0000: sub rsp, 0x28
    L0004: mov rdx, [rcx+8]
    L0008: cmp [rdx], dl
    L000a: mov rcx, 0x1ba28000590
    L0014: mov rcx, [rcx]
    L0017: call qword ptr [0x7ff8a875f0a8]
    L001d: cmp [rax], al
    L001f: mov rdx, 0x1ba2805fcc8
    L0029: mov rdx, [rdx]
    L002c: mov rcx, rax
    L002f: xor r8d, r8d
    L0032: add rsp, 0x28
    L0036: jmp qword ptr [0x7ff8a85962f8]
// .NET 8 (X64)
    L0000: vzeroupper
    L0003: mov rax, [rcx+8]
    L0007: cmp dword ptr [rax+8], 8
    L000b: jl short L002f
    L000d: vmovups xmm0, [rax+0xc]
    L0012: vpor xmm0, xmm0, [StartsWithOrdinalIgnoreCase.IsHttps_StringComparison()]
    L001a: vpxor xmm0, xmm0, [StartsWithOrdinalIgnoreCase.IsHttps_StringComparison()]
    L0022: vptest xmm0, xmm0
    L0027: sete al
    L002a: movzx eax, al
    L002d: jmp short L0031
    L002f: xor eax, eax
    L0031: ret

Benchmark Description:

The provided benchmark code is designed to measure and compare the performance of two different methods for checking if a given string starts with a specific prefix in a case-insensitive manner. This benchmark is set up using BenchmarkDotNet, a powerful .NET library for benchmarking code performance. The benchmark is configured to run on .NET 8, as indicated by the configuration setup within the `Config` class. This specific version of .NET is chosen to ensure the benchmarks are run on a consistent and modern runtime environment, which helps in obtaining relevant and up-to-date performance insights. ### General Setup - **.NET Version**: The benchmark specifies the use of .NET 8 (`CoreRuntime.Core80`), ensuring that the tests are run on a specific and modern version of the .NET runtime. - **BenchmarkDotNet Configuration**: The configuration class `Config` extends `ManualConfig` from BenchmarkDotNet, allowing customization of the benchmarking environment. It specifies the use of `.NET 8` runtime and customizes the summary style to display ratio percentages. - **Memory Diagnoser**: The `[MemoryDiagnoser]` attribute is used to diagnose memory usage, providing insights into the allocations made by each benchmark method. - **Column Hiding**: The `[HideColumns]` attribute is used to simplify the output by hiding specific columns like Error, StdDev (Standard Deviation), Median, and RatioSD (Ratio Standard Deviation). ### Benchmark Methods #### 1. `IsHttps_ToUpper()` - **Purpose**: This method checks if the `_input` string starts with "HTTPS://" by first converting the entire `_input` string to uppercase using `ToUpperInvariant()` and then using `StartsWith("HTTPS://")` to perform the comparison. - **Performance Aspect**: This benchmark is designed to measure the performance implications of converting an entire string to uppercase as a means to perform a case-insensitive comparison. It tests both the computational overhead of the `ToUpperInvariant()` method and the subsequent `StartsWith` operation. - **Expected Insights**: Running this benchmark will help understand the cost (in terms of execution time and memory allocations) associated with string manipulation through case conversion before performing a comparison. It is generally expected to be less efficient due to the additional string allocation and processing involved in the `ToUpperInvariant()` call. #### 2. `IsHttps_StringComparison()` - **Purpose**: This method directly checks if the `_input` string starts with "HTTPS://" in a case-insensitive manner by using the `StartsWith` method with `StringComparison.OrdinalIgnoreCase` as a parameter. - **Performance Aspect**: The benchmark aims to measure the efficiency of performing a case-insensitive comparison without modifying the original string. It specifically tests the performance of the `StartsWith` method when using the `StringComparison.OrdinalIgnoreCase` comparison option. - **Expected Insights**: This method is expected to be more efficient than the `IsHttps_ToUpper()` method because it avoids the overhead of converting the `_input` string to uppercase. The direct use of `StringComparison.OrdinalIgnoreCase` allows for a more optimized comparison that doesn't require additional string allocations or transformations. The results should typically show lower execution times and fewer memory allocations compared to the `IsHttps_ToUpper()` method. ### Summary By comparing these two methods, the benchmark aims to highlight the performance benefits of using more direct and optimized string comparison techniques (`IsHttps_StringComparison()`) over more manual and potentially less efficient approaches (`IsHttps_ToUpper()`). The insights gained can guide developers in choosing more performant string comparison strategies in their .NET applications, especially in scenarios where such comparisons are frequent or critical for performance.

Benchmark Comments: