Skip to content

Instantly share code, notes, and snippets.

@udlose
Created March 13, 2026 16:06
Show Gist options
  • Select an option

  • Save udlose/426efbfb1fc36d620e589b804e89c88a to your computer and use it in GitHub Desktop.

Select an option

Save udlose/426efbfb1fc36d620e589b804e89c88a to your computer and use it in GitHub Desktop.
SpanIndexOfBenchmark
// run with:
// dotnet run -c Release --framework net10.0 -- --filter *
// * Summary *
//
// BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8037/25H2/2025Update/HudsonValley2)
// 12th Gen Intel Core i7-12800H 2.40GHz, 1 CPU, 20 logical and 14 physical cores
//
// .NET SDK 10.0.200
// [Host] : .NET 10.0.4 (10.0.4, 10.0.426.12010), X64 RyuJIT x86-64-v3
// ShortRun : .NET 10.0.4 (10.0.4, 10.0.426.12010), X64 RyuJIT x86-64-v3
//Job=ShortRun IterationCount=3 LaunchCount=1
//WarmupCount=1
//| Method | Runtime | Source | ComparisonType | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
//|----------------------------------------------------- |------------------- |--------------------- |------------------ |-----------:|------------:|-----------:|-------------:|--------:|-------:|----------:|------------:|
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET 10.0 | ABCDE(...)^&*() [75] | Ordinal | 28.290 ns | 10.6876 ns | 0.5858 ns | baseline | | 0.0140 | 176 B | |
//| 'string.IndexOf (no alloc)' | .NET 10.0 | ABCDE(...)^&*() [75] | Ordinal | 7.742 ns | 5.4336 ns | 0.2978 ns | 3.66x faster | 0.14x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET 10.0 | ABCDE(...)^&*() [75] | Ordinal | 6.521 ns | 1.4463 ns | 0.0793 ns | 4.34x faster | 0.09x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET Framework 4.8 | ABCDE(...)^&*() [75] | Ordinal | 254.465 ns | 15.4581 ns | 0.8473 ns | baseline | | 0.0343 | 217 B | |
//| 'string.IndexOf (no alloc)' | .NET Framework 4.8 | ABCDE(...)^&*() [75] | Ordinal | 50.547 ns | 6.8980 ns | 0.3781 ns | 5.03x faster | 0.04x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET Framework 4.8 | ABCDE(...)^&*() [75] | Ordinal | 38.318 ns | 7.4586 ns | 0.4088 ns | 6.64x faster | 0.06x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET 10.0 | ABCDE(...)^&*() [75] | OrdinalIgnoreCase | 30.575 ns | 13.2449 ns | 0.7260 ns | baseline | | 0.0140 | 176 B | |
//| 'string.IndexOf (no alloc)' | .NET 10.0 | ABCDE(...)^&*() [75] | OrdinalIgnoreCase | 8.099 ns | 6.2832 ns | 0.3444 ns | 3.78x faster | 0.16x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET 10.0 | ABCDE(...)^&*() [75] | OrdinalIgnoreCase | 6.427 ns | 6.9473 ns | 0.3808 ns | 4.77x faster | 0.26x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET Framework 4.8 | ABCDE(...)^&*() [75] | OrdinalIgnoreCase | 269.232 ns | 121.7263 ns | 6.6722 ns | baseline | | 0.0343 | 217 B | |
//| 'string.IndexOf (no alloc)' | .NET Framework 4.8 | ABCDE(...)^&*() [75] | OrdinalIgnoreCase | 239.134 ns | 24.5035 ns | 1.3431 ns | 1.13x faster | 0.02x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET Framework 4.8 | ABCDE(...)^&*() [75] | OrdinalIgnoreCase | 260.472 ns | 43.7581 ns | 2.3985 ns | 1.03x faster | 0.02x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET 10.0 | Lore(...)ris. [191] | Ordinal | 44.765 ns | 8.6767 ns | 0.4756 ns | baseline | | 0.0325 | 408 B | |
//| 'string.IndexOf (no alloc)' | .NET 10.0 | Lore(...)ris. [191] | Ordinal | 13.633 ns | 6.2573 ns | 0.3430 ns | 3.28x faster | 0.08x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET 10.0 | Lore(...)ris. [191] | Ordinal | 11.595 ns | 2.6312 ns | 0.1442 ns | 3.86x faster | 0.05x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET Framework 4.8 | Lore(...)ris. [191] | Ordinal | 511.982 ns | 66.4035 ns | 3.6398 ns | baseline | | 0.0706 | 449 B | |
//| 'string.IndexOf (no alloc)' | .NET Framework 4.8 | Lore(...)ris. [191] | Ordinal | 109.135 ns | 21.9243 ns | 1.2017 ns | 4.69x faster | 0.05x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET Framework 4.8 | Lore(...)ris. [191] | Ordinal | 94.081 ns | 99.0786 ns | 5.4308 ns | 5.45x faster | 0.28x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET 10.0 | Lore(...)ris. [191] | OrdinalIgnoreCase | 47.911 ns | 31.2295 ns | 1.7118 ns | baseline | | 0.0325 | 408 B | |
//| 'string.IndexOf (no alloc)' | .NET 10.0 | Lore(...)ris. [191] | OrdinalIgnoreCase | 13.196 ns | 1.5312 ns | 0.0839 ns | 3.63x faster | 0.11x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET 10.0 | Lore(...)ris. [191] | OrdinalIgnoreCase | 12.717 ns | 7.0340 ns | 0.3856 ns | 3.77x faster | 0.15x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET Framework 4.8 | Lore(...)ris. [191] | OrdinalIgnoreCase | 506.374 ns | 388.7720 ns | 21.3099 ns | baseline | | 0.0706 | 449 B | |
//| 'string.IndexOf (no alloc)' | .NET Framework 4.8 | Lore(...)ris. [191] | OrdinalIgnoreCase | 513.464 ns | 53.5487 ns | 2.9352 ns | 1.02x slower | 0.04x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET Framework 4.8 | Lore(...)ris. [191] | OrdinalIgnoreCase | 497.864 ns | 36.3594 ns | 1.9930 ns | 1.02x faster | 0.04x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET 10.0 | The q(...)y dog [43] | Ordinal | 24.595 ns | 13.9633 ns | 0.7654 ns | baseline | | 0.0089 | 112 B | |
//| 'string.IndexOf (no alloc)' | .NET 10.0 | The q(...)y dog [43] | Ordinal | 7.513 ns | 0.4483 ns | 0.0246 ns | 3.27x faster | 0.09x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET 10.0 | The q(...)y dog [43] | Ordinal | 5.240 ns | 0.7950 ns | 0.0436 ns | 4.69x faster | 0.13x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET Framework 4.8 | The q(...)y dog [43] | Ordinal | 204.573 ns | 64.9947 ns | 3.5626 ns | baseline | | 0.0241 | 152 B | |
//| 'string.IndexOf (no alloc)' | .NET Framework 4.8 | The q(...)y dog [43] | Ordinal | 43.608 ns | 8.8645 ns | 0.4859 ns | 4.69x faster | 0.08x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET Framework 4.8 | The q(...)y dog [43] | Ordinal | 29.372 ns | 2.4230 ns | 0.1328 ns | 6.97x faster | 0.11x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET 10.0 | The q(...)y dog [43] | OrdinalIgnoreCase | 22.419 ns | 5.4077 ns | 0.2964 ns | baseline | | 0.0089 | 112 B | |
//| 'string.IndexOf (no alloc)' | .NET 10.0 | The q(...)y dog [43] | OrdinalIgnoreCase | 7.537 ns | 2.8075 ns | 0.1539 ns | 2.98x faster | 0.06x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET 10.0 | The q(...)y dog [43] | OrdinalIgnoreCase | 6.158 ns | 1.6943 ns | 0.0929 ns | 3.64x faster | 0.06x | - | - | NA |
//| | | | | | | | | | | | |
//| 'ToLower + Contains (with ToLower) - 2x allocations' | .NET Framework 4.8 | The q(...)y dog [43] | OrdinalIgnoreCase | 206.237 ns | 26.9969 ns | 1.4798 ns | baseline | | 0.0241 | 152 B | |
//| 'string.IndexOf (no alloc)' | .NET Framework 4.8 | The q(...)y dog [43] | OrdinalIgnoreCase | 128.457 ns | 12.2357 ns | 0.6707 ns | 1.61x faster | 0.01x | - | - | NA |
//| 'Span.IndexOf (no alloc, managed path)' | .NET Framework 4.8 | The q(...)y dog [43] | OrdinalIgnoreCase | 145.212 ns | 10.7029 ns | 0.5867 ns | 1.42x faster | 0.01x | - | - | NA |
//
// ---------------------------------------------------------------------------
// LINQPad-compatible configuration
// ---------------------------------------------------------------------------
// BDN's default CsProj toolchain spawns a child process and re-compiles the
// benchmark project from source. It locates the .csproj by matching the
// output exe name — which breaks under LINQPad because LINQPad compiles
// queries into a temp assembly named "LINQPadQuery" with no .csproj present.
//
// InProcessEmitToolchain avoids this entirely: it runs benchmarks in the
// same process that LINQPad already started, using Reflection.Emit to
// generate the benchmark harness at runtime. No child process, no .csproj
// lookup, no net48 re-compilation.
//
// Trade-off: because everything runs in one process you cannot benchmark
// startup costs or cross-runtime differences (net48 vs net10) in a single
// run. Run once per LINQPad target runtime to compare.
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using System;
internal sealed class BenchmarkConfig : ManualConfig
{
public BenchmarkConfig()
{
foreach (var logger in DefaultConfig.Instance.GetLoggers()) AddLogger(logger);
foreach (var exporter in DefaultConfig.Instance.GetExporters()) AddExporter(exporter);
foreach (var validator in DefaultConfig.Instance.GetValidators()) AddValidator(validator);
foreach (var analyser in DefaultConfig.Instance.GetAnalysers()) AddAnalyser(analyser);
foreach (var diagnoser in DefaultConfig.Instance.GetDiagnosers()) AddDiagnoser(diagnoser);
foreach (var provider in DefaultConfig.Instance.GetColumnProviders()) AddColumnProvider(provider);
SummaryStyle = SummaryStyle.Default
.WithRatioStyle(RatioStyle.Trend);
// Both runtimes defined here with iteration counts pinned explicitly.
// No [SimpleJob] attributes on the class — this is the single source
// of truth for job definitions, which prevents duplicate job rows.
AddJob(Job.ShortRun
.WithRuntime(ClrRuntime.Net48)
.WithWarmupCount(1)
.WithIterationCount(3));
AddJob(Job.ShortRun
.WithRuntime(CoreRuntime.Core10_0)
.WithWarmupCount(1)
.WithIterationCount(3));
}
}
// ---------------------------------------------------------------------------
// Benchmark class
// ---------------------------------------------------------------------------
/// <summary>
/// Compares three Contains implementations across runtimes:
///
/// 1. StringIndexOf — string.IndexOf(string, StringComparison)
/// On net48: may route through Windows NLS / P/Invoke
/// for OrdinalIgnoreCase and culture-aware comparisons.
/// On net10.0: reimplemented in managed code; gap narrows.
///
/// 2. SpanIndexOf — source.AsSpan().IndexOf(toCheck.AsSpan(), comparison)
/// On net48 (System.Memory NuGet): fully managed path,
/// no P/Invoke, JIT-inlineable, eligible for SIMD
/// vectorization on supported hardware.
/// On net10.0: same managed path as string.IndexOf;
/// numbers should be essentially identical.
///
/// 3. ToLowerContains — the naïve approach included as a worst-case baseline
/// to anchor the "why we don't do this" discussion.
/// Allocates two new strings on every call regardless
/// of runtime.
///
/// The Consumer field is written to after each benchmark body so the JIT
/// cannot dead-code-eliminate the return value (a common source of
/// artificially fast micro-benchmark numbers).
/// </summary>
[Config(typeof(BenchmarkConfig))]
[MemoryDiagnoser]
public class ContainsBenchmarks
{
// ---------------------------------------------------------------------------
// Test data — varied lengths exercise both the short-string fast path and
// the longer SIMD-eligible path in MemoryExtensions.
// ---------------------------------------------------------------------------
[Params(
"The quick brown fox jumps over the lazy dog",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 !@#$%^&*()",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.")]
public string Source { get; set; } = string.Empty;
[Params(StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)]
public StringComparison ComparisonType { get; set; }
// Search term is always present so we measure a successful search,
// which exercises the full comparison path rather than an early exit.
private const string ToCheck = "lazy";
// BenchmarkDotNet's Consumer is a black-hole sink: calling Consume()
// writes the value into a volatile field, which is enough to prevent
// the JIT from treating the result as dead code and eliminating the call.
private readonly Consumer _consumer = new Consumer();
// ---------------------------------------------------------------------------
// Benchmarks
// ---------------------------------------------------------------------------
/// <summary>
/// Baseline: the naïve ToLower approach.
///
/// Allocates two strings per call (source.ToLower() + toCheck.ToLower()).
/// Shown here so the Ratio column in the results makes the cost of
/// allocations viscerally visible — especially on net48 where GC pressure
/// from boxed values and string allocations is already a concern.
/// </summary>
[Benchmark(Baseline = true, Description = "ToLower + Contains (with ToLower) - 2x allocations")]
public void ToLowerContains()
{
// Allocates: source.ToLower() → new string
// toCheck.ToLower() → new string
// Every. Single. Call.
bool result = Source.ToLower().Contains(ToCheck.ToLower());
_consumer.Consume(result);
}
/// <summary>
/// string.IndexOf(string, StringComparison).
///
/// Zero allocation — no new strings created.
/// On net48, OrdinalIgnoreCase may still route through a Windows NLS
/// helper (CompareInfo.IndexOf) which involves additional dispatch
/// overhead compared to the fully-managed Span path.
/// On net10.0 this was reimplemented to use the same managed searcher
/// as MemoryExtensions, so the advantage over SpanIndexOf largely
/// disappears.
/// </summary>
[Benchmark(Description = "string.IndexOf (no alloc)")]
public void StringIndexOf()
{
bool result = Source.IndexOf(ToCheck, ComparisonType) != -1;
_consumer.Consume(result);
}
/// <summary>
/// source.AsSpan().IndexOf(toCheck.AsSpan(), StringComparison).
///
/// Zero allocation — AsSpan() returns a view over the existing string's
/// memory; no copy, no heap object.
///
/// On net48 (System.Memory NuGet): MemoryExtensions.IndexOf is a
/// fully-managed implementation that:
/// a) avoids the P/Invoke / NLS dispatch that string.IndexOf may use,
/// b) is directly inlineable by the JIT because it has no
/// MarshalByRefObject or COM interop callsites in the call graph,
/// c) on hardware with SSE2/AVX2, the inner loop is SIMD-vectorized,
/// processing 16–32 chars per CPU instruction instead of 1.
///
/// On net10.0: both string.IndexOf and Span.IndexOf share the same
/// underlying managed searcher, so numbers will be essentially identical.
/// The real story is the net48 column — that's where the gap is visible.
/// </summary>
[Benchmark(Description = "Span.IndexOf (no alloc, managed path)")]
public void SpanIndexOf()
{
// AsSpan() — O(1), zero allocation, just a pointer + length struct
bool result = Source.AsSpan().IndexOf(ToCheck.AsSpan(), ComparisonType) != -1;
_consumer.Consume(result);
}
internal static class Program
{
private static void Main(string[] args)
{
BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
.Run(args, new BenchmarkConfig());
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!--
Multi-target so BenchmarkDotNet can run the same benchmark class
under both runtimes in a single solution. The net48 job exercises
the P/Invoke / NLS code path that string.IndexOf uses on .NET
Framework; the net10.0 job uses the fully-managed, SIMD-vectorized
MemoryExtensions path that both string.IndexOf AND Span.IndexOf
share on modern runtimes — giving us the "before / after" picture
for the migration conversation.
-->
<TargetFrameworks>net48;net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<OutputType>Exe</OutputType>
<AssemblyName>SpanIndexOfBenchmark</AssemblyName>
<RootNamespace>SpanIndexOfBenchmark</RootNamespace>
</PropertyGroup>
<ItemGroup>
<!--
BenchmarkDotNet — the industry-standard micro-benchmarking library.
Version 0.14.x supports both net48 and net10.0 out of the box.
-->
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<!--
System.Memory — required for net48 only.
This NuGet package back-ports ReadOnlySpan<char>, MemoryExtensions,
and the managed Span.IndexOf implementation to .NET Framework 4.x.
On net10.0 the types ship in-box and this reference is ignored.
-->
<PackageReference Include="System.Memory" Version="4.6.3" Condition="'$(TargetFramework)' == 'net48'" />
</ItemGroup>
</Project>

BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8037/25H2/2025Update/HudsonValley2)
12th Gen Intel Core i7-12800H 2.40GHz, 1 CPU, 20 logical and 14 physical cores
.NET SDK 10.0.200
  [Host]   : .NET 10.0.4 (10.0.4, 10.0.426.12010), X64 RyuJIT x86-64-v3
  ShortRun : .NET 10.0.4 (10.0.4, 10.0.426.12010), X64 RyuJIT x86-64-v3

Job=ShortRun  IterationCount=3  LaunchCount=1  
WarmupCount=1  

Method Runtime Source ComparisonType Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
'ToLower + Contains (with ToLower) - 2x allocations' .NET 10.0 ABCDE(...)^&*() [75] Ordinal 25.082 ns 3.9004 ns 0.2138 ns baseline **** 0.0140 176 B ****
'string.IndexOf (no alloc)' .NET 10.0 ABCDE(...)^&*() [75] Ordinal 7.481 ns 6.4782 ns 0.3551 ns 3.36x faster 0.14x - - NA
'Span.IndexOf (no alloc, managed path)' .NET 10.0 ABCDE(...)^&*() [75] Ordinal 6.493 ns 1.1292 ns 0.0619 ns 3.86x faster 0.04x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET Framework 4.8 ABCDE(...)^&*() [75] Ordinal 254.743 ns 42.4848 ns 2.3287 ns baseline 0.0343 217 B
'string.IndexOf (no alloc)' .NET Framework 4.8 ABCDE(...)^&*() [75] Ordinal 50.518 ns 5.6921 ns 0.3120 ns 5.04x faster 0.05x - - NA
'Span.IndexOf (no alloc, managed path)' .NET Framework 4.8 ABCDE(...)^&*() [75] Ordinal 38.280 ns 3.6921 ns 0.2024 ns 6.65x faster 0.06x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET 10.0 ABCDE(...)^&*() [75] OrdinalIgnoreCase 25.603 ns 4.5329 ns 0.2485 ns baseline **** 0.0140 176 B ****
'string.IndexOf (no alloc)' .NET 10.0 ABCDE(...)^&*() [75] OrdinalIgnoreCase 8.136 ns 1.4134 ns 0.0775 ns 3.15x faster 0.04x - - NA
'Span.IndexOf (no alloc, managed path)' .NET 10.0 ABCDE(...)^&*() [75] OrdinalIgnoreCase 5.576 ns 1.2278 ns 0.0673 ns 4.59x faster 0.06x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET Framework 4.8 ABCDE(...)^&*() [75] OrdinalIgnoreCase 250.353 ns 21.6537 ns 1.1869 ns baseline 0.0343 217 B
'string.IndexOf (no alloc)' .NET Framework 4.8 ABCDE(...)^&*() [75] OrdinalIgnoreCase 234.304 ns 19.9322 ns 1.0926 ns 1.07x faster 0.01x - - NA
'Span.IndexOf (no alloc, managed path)' .NET Framework 4.8 ABCDE(...)^&*() [75] OrdinalIgnoreCase 248.003 ns 22.2469 ns 1.2194 ns 1.01x faster 0.01x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET 10.0 Lore(...)ris. [191] Ordinal 42.247 ns 5.3467 ns 0.2931 ns baseline **** 0.0325 408 B ****
'string.IndexOf (no alloc)' .NET 10.0 Lore(...)ris. [191] Ordinal 12.758 ns 2.1365 ns 0.1171 ns 3.31x faster 0.03x - - NA
'Span.IndexOf (no alloc, managed path)' .NET 10.0 Lore(...)ris. [191] Ordinal 11.223 ns 5.4845 ns 0.3006 ns 3.77x faster 0.09x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET Framework 4.8 Lore(...)ris. [191] Ordinal 455.926 ns 15.4520 ns 0.8470 ns baseline 0.0710 449 B
'string.IndexOf (no alloc)' .NET Framework 4.8 Lore(...)ris. [191] Ordinal 102.751 ns 13.0885 ns 0.7174 ns 4.44x faster 0.03x - - NA
'Span.IndexOf (no alloc, managed path)' .NET Framework 4.8 Lore(...)ris. [191] Ordinal 82.690 ns 14.8784 ns 0.8155 ns 5.51x faster 0.05x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET 10.0 Lore(...)ris. [191] OrdinalIgnoreCase 42.508 ns 3.0957 ns 0.1697 ns baseline **** 0.0325 408 B ****
'string.IndexOf (no alloc)' .NET 10.0 Lore(...)ris. [191] OrdinalIgnoreCase 12.107 ns 1.8025 ns 0.0988 ns 3.51x faster 0.03x - - NA
'Span.IndexOf (no alloc, managed path)' .NET 10.0 Lore(...)ris. [191] OrdinalIgnoreCase 11.486 ns 3.5957 ns 0.1971 ns 3.70x faster 0.06x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET Framework 4.8 Lore(...)ris. [191] OrdinalIgnoreCase 466.615 ns 114.7948 ns 6.2923 ns baseline 0.0710 449 B
'string.IndexOf (no alloc)' .NET Framework 4.8 Lore(...)ris. [191] OrdinalIgnoreCase 467.335 ns 36.2117 ns 1.9849 ns 1.00x slower 0.01x - - NA
'Span.IndexOf (no alloc, managed path)' .NET Framework 4.8 Lore(...)ris. [191] OrdinalIgnoreCase 495.846 ns 34.5988 ns 1.8965 ns 1.06x slower 0.01x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET 10.0 The q(...)y dog [43] Ordinal 22.623 ns 7.4315 ns 0.4073 ns baseline **** 0.0089 112 B ****
'string.IndexOf (no alloc)' .NET 10.0 The q(...)y dog [43] Ordinal 7.430 ns 11.3120 ns 0.6201 ns 3.06x faster 0.22x - - NA
'Span.IndexOf (no alloc, managed path)' .NET 10.0 The q(...)y dog [43] Ordinal 5.144 ns 3.5674 ns 0.1955 ns 4.40x faster 0.16x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET Framework 4.8 The q(...)y dog [43] Ordinal 203.347 ns 21.2199 ns 1.1631 ns baseline 0.0241 152 B
'string.IndexOf (no alloc)' .NET Framework 4.8 The q(...)y dog [43] Ordinal 43.209 ns 10.8497 ns 0.5947 ns 4.71x faster 0.06x - - NA
'Span.IndexOf (no alloc, managed path)' .NET Framework 4.8 The q(...)y dog [43] Ordinal 29.276 ns 5.5512 ns 0.3043 ns 6.95x faster 0.07x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET 10.0 The q(...)y dog [43] OrdinalIgnoreCase 22.914 ns 16.8940 ns 0.9260 ns baseline **** 0.0089 112 B ****
'string.IndexOf (no alloc)' .NET 10.0 The q(...)y dog [43] OrdinalIgnoreCase 7.511 ns 3.3255 ns 0.1823 ns 3.05x faster 0.12x - - NA
'Span.IndexOf (no alloc, managed path)' .NET 10.0 The q(...)y dog [43] OrdinalIgnoreCase 6.269 ns 0.4002 ns 0.0219 ns 3.66x faster 0.13x - - NA
'ToLower + Contains (with ToLower) - 2x allocations' .NET Framework 4.8 The q(...)y dog [43] OrdinalIgnoreCase 197.499 ns 33.7722 ns 1.8512 ns baseline 0.0241 152 B
'string.IndexOf (no alloc)' .NET Framework 4.8 The q(...)y dog [43] OrdinalIgnoreCase 126.085 ns 28.0671 ns 1.5385 ns 1.57x faster 0.02x - - NA
'Span.IndexOf (no alloc, managed path)' .NET Framework 4.8 The q(...)y dog [43] OrdinalIgnoreCase 145.376 ns 19.2963 ns 1.0577 ns 1.36x faster 0.01x - - NA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment