Skip to content

Instantly share code, notes, and snippets.

@daiplusplus
Last active September 25, 2025 22:29
Show Gist options
  • Select an option

  • Save daiplusplus/72aa75dbb3d7b1cd6746c79a60e0e3f1 to your computer and use it in GitHub Desktop.

Select an option

Save daiplusplus/72aa75dbb3d7b1cd6746c79a60e0e3f1 to your computer and use it in GitHub Desktop.
Dictionary<K,V> vs IReadOnlyDictionary<K,V>
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Reports;
namespace DictInterBench;
public static class Program
{
public static readonly Object?[] CommonN = new Object?[] { 16, 128/*, 1024, 4096*/ };
public static void Main()
{
Summary summary = BenchmarkRunner.Run<BothDictionaryBenchmarks>();
// summary.Dump();
}
/*
Notes:
- Both scenarios share the same Dictionary instance; the difference is interface vs concrete dispatch and any related JIT inlining effects.
- Only read operations are benchmarked because IReadOnlyDictionary<T> doesn’t expose mutating APIs.
- MemoryDiagnoser is enabled to observe allocation (should be zero for these operations).
*/
}
//[MemoryDiagnoser] // we don't care about memory usage
[SimpleJob] // default job; LINQPad will run it fine
//[JsonExporterAttribute.FullCompressed( )]
//[JsonExporterAttribute( fileNameSuffix: "DictionaryBenchmarks.json", indentJson: true, excludeMeasurements: false )]
[CsvExporter]
[CsvMeasurementsExporter]
public class BothDictionaryBenchmarks
{
// Vary the dictionary size to see scaling and dispatch overhead behavior
[Params(
16,
128//,
// 1024//,
// 4096
)]
public Int32 N;
private Dictionary <String,Object?> dict;
private IReadOnlyDictionary<String,Object?> roDict;
private String _hitKey;
private String _missKey;
[GlobalSetup]
public void Setup()
{
this.dict = new Dictionary<String, Object?>( this.N, StringComparer.Ordinal);
for ( Int32 i = 0; i < this.N; i++)
{
// deterministic keys
this.dict.Add($"k{i:D6}", i); // store boxed ints
}
this.roDict = this.dict; // same instance, referenced via the interface
// choose a key that exists and one that does not
this._hitKey = $"k{( this.N / 2):D6}";
this._missKey = $"missing_{this.N}";
}
// ----- IReadOnlyDictionary<T> -----
[Benchmark]
public Object? IReadOnly_TryGetValue_Hit()
{
_ = this.roDict.TryGetValue( this._hitKey, out Object? v );
return v;
}
[Benchmark]
public Object? IReadOnly_TryGetValue_Miss()
{
_ = this.roDict.TryGetValue( this._missKey, out Object? v );
return v;
}
[Benchmark]
public Object? IReadOnly_Indexer_Hit()
{
return this.roDict[this._hitKey];
}
[Benchmark]
public Boolean IReadOnly_ContainsKey_Hit()
{
return this.roDict.ContainsKey( this._hitKey );
}
[Benchmark]
public Int32 IReadOnly_Enumerate_All()
{
Int32 count = 0;
foreach( KeyValuePair<String, Object?> kvp in this.roDict )
{
if( kvp.Value is not null ) count++;
}
return count;
}
// ----- Dictionary<T> (concrete) -----
[Benchmark]
public Object? Dict_TryGetValue_Hit()
{
_ = this.dict.TryGetValue( this._hitKey, out Object? v );
return v;
}
[Benchmark]
public Object? Dict_TryGetValue_Miss()
{
_ = this.dict.TryGetValue( this._missKey, out Object? v );
return v;
}
[Benchmark]
public Object? Dict_Indexer_Hit()
{
return this.dict[this._hitKey];
}
[Benchmark]
public Boolean Dict_ContainsKey_Hit()
{
return this.dict.ContainsKey( this._hitKey );
}
[Benchmark]
public Int32 Dict_Enumerate_All()
{
Int32 count = 0;
foreach ( KeyValuePair<String, Object?> kvp in this.dict )
{
if (kvp.Value is not null) count++;
}
return count;
}
}
[SimpleJob]
[CsvExporter]
[CsvMeasurementsExporter]
public class IReadOnlyDictionaryBenchmarks
{
[Params( new Object?[] { 16, 128, 1024 /*, 4096*/ } )]
public Int32 N;
private IReadOnlyDictionary<String,Object?> roDict;
private String hitKey;
private String missKey;
[GlobalSetup]
public void Setup()
{
Dictionary<String,Object?> dict = new Dictionary<String,Object?>( capacity: this.N, comparer: StringComparer.Ordinal );
for ( Int32 i = 0; i < this.N; i++)
{
// deterministic keys
dict.Add( $"k{i:D6}", i ); // store boxed ints
}
this.roDict = dict; // same instance, referenced via the interface
// choose a key that exists and one that does not
this.hitKey = $"k{( this.N / 2):D6}";
this.missKey = $"missing_{this.N}";
}
[Benchmark]
public Object? TryGetValue_Hit()
{
_ = this.roDict.TryGetValue( this.hitKey, out Object? v );
return v;
}
[Benchmark]
public Object? TryGetValue_Miss()
{
_ = this.roDict.TryGetValue( this.missKey, out Object? v );
return v;
}
[Benchmark]
public Object? Indexer_Hit()
{
return this.roDict[this.hitKey];
}
[Benchmark]
public Boolean ContainsKey_Hit()
{
return this.roDict.ContainsKey( this.hitKey );
}
[Benchmark]
public Int32 Enumerate_All()
{
Int32 count = 0;
foreach( KeyValuePair<String, Object?> kvp in this.roDict )
{
if( kvp.Value is not null ) count++;
}
return count;
}
}
[SimpleJob]
[CsvExporter]
[CsvMeasurementsExporter]
public class ClassDictionaryBenchmarks
{
[Params( new Object?[] { 16, 128, 1024 /*, 4096*/ } )]
public Int32 N;
private Dictionary<String,Object?> dict;
private String hitKey;
private String missKey;
[GlobalSetup]
public void Setup()
{
this.dict = new Dictionary<String,Object?>( capacity: this.N, comparer: StringComparer.Ordinal );
for ( Int32 i = 0; i < this.N; i++)
{
// deterministic keys
this.dict.Add( $"k{i:D6}", i ); // store boxed ints
}
// choose a key that exists and one that does not
this.hitKey = $"k{( this.N / 2):D6}";
this.missKey = $"missing_{this.N}";
}
[Benchmark]
public Object? TryGetValue_Hit()
{
_ = this.dict.TryGetValue( this.hitKey, out Object? v );
return v;
}
[Benchmark]
public Object? TryGetValue_Miss()
{
_ = this.dict.TryGetValue( this.missKey, out Object? v );
return v;
}
[Benchmark]
public Object? Indexer_Hit()
{
return this.dict[this.hitKey];
}
[Benchmark]
public Boolean ContainsKey_Hit()
{
return this.dict.ContainsKey( this.hitKey );
}
[Benchmark]
public Int32 Enumerate_All()
{
Int32 count = 0;
foreach ( KeyValuePair<String, Object?> kvp in this.dict )
{
if (kvp.Value is not null) count++;
}
return count;
}
}
net48 net6.0 net8.0
Method N Mean Error StdDev Mean Error StdDev Mean Error StdDev
Dict_ContainsKey_Hit 16 20.27 0.43 0.753 14.713 0.3133 0.4877 13.114 0.2898 0.2711
Dict_ContainsKey_Hit 128 17.96 0.363 0.485 11.532 0.2509 0.3262 12.604 0.2798 0.333
Dict_Enumerate_All 16 90 1.773 2.76 83.452 1.6642 2.333 14.668 0.3219 0.6354
Dict_Enumerate_All 128 566.91 11.265 18.822 577.78 11.2449 12.0319 126.604 2.3813 3.9125
Dict_Indexer_Hit 16 19.65 0.448 0.748 14.634 0.3226 0.4828 13.742 0.3144 0.2941
Dict_Indexer_Hit 128 17.86 0.404 0.686 11.587 0.2717 0.2269 10.98 0.2141 0.2103
Dict_TryGetValue_Hit 16 20.9 0.47 0.643 15.571 0.3478 0.3866 12.944 0.3071 0.3993
Dict_TryGetValue_Hit 128 19.85 0.409 0.502 12.811 0.3093 0.5335 11.44 0.264 0.5511
Dict_TryGetValue_Miss 16 11.79 0.287 0.268 11.124 0.2663 0.3368 8.135 0.1853 0.206
Dict_TryGetValue_Miss 128 12.44 0.258 0.229 9.024 0.2307 0.2369 6.504 0.1818 0.2777
IReadOnly_ContainsKey_Hit 16 20.89 0.435 0.762 15.868 0.2705 0.2398 13.944 0.3001 0.4107
IReadOnly_ContainsKey_Hit 128 18.5 0.33 0.292 15.843 0.3034 0.2838 11.365 0.2478 0.2069
IReadOnly_Enumerate_All 16 210.96 3.776 3.154 212.092 2.8244 2.642 219.806 1.1967 1.0608
IReadOnly_Enumerate_All 128 1,467.53 29.111 34.654 1,473.21 28.0964 28.8529 1,666.04 4.8648 4.0623
IReadOnly_Indexer_Hit 16 20.13 0.438 0.755 14.993 0.3524 0.4456 13.559 0.2659 0.2357
IReadOnly_Indexer_Hit 128 18.35 0.379 0.674 12.159 0.2932 0.411 11.527 0.2837 0.3977
IReadOnly_TryGetValue_Hit 16 21.13 0.476 0.619 15.774 0.3626 0.5752 14.31 0.3387 0.4636
IReadOnly_TryGetValue_Hit 128 20.07 0.445 0.609 13.184 0.3082 0.432 11.548 0.2854 0.3398
IReadOnly_TryGetValue_Miss 16 12.68 0.266 0.473 11.035 0.25 0.2568 8.526 0.2216 0.2552
IReadOnly_TryGetValue_Miss 128 12.96 0.302 0.336 9.468 0.2417 0.3466 7.34 0.2004 0.2675
Method Job AnalyzeLaunchVariance EvaluateOverhead MaxAbsoluteError MaxRelativeError MinInvokeCount MinIterationTime OutlierMode Affinity EnvironmentVariables Jit LargeAddressAware Platform PowerPlanMode Runtime AllowVeryLargeObjects Concurrent CpuGroups Force HeapAffinitizeMask HeapCount NoAffinitize RetainVm Server Arguments BuildConfiguration Clock EngineFactory NuGetReferences Toolchain IsMutator InvocationCount IterationCount IterationTime LaunchCount MaxIterationCount MaxWarmupIterationCount MemoryRandomization MinIterationCount MinWarmupIterationCount RunStrategy UnrollFactor WarmupCount N Mean Error StdDev
IReadOnly_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 21.13 ns 0.476 ns 0.619 ns
IReadOnly_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 12.68 ns 0.266 ns 0.473 ns
IReadOnly_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 20.13 ns 0.438 ns 0.755 ns
IReadOnly_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 20.89 ns 0.435 ns 0.762 ns
IReadOnly_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 210.96 ns 3.776 ns 3.154 ns
Dict_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 20.90 ns 0.470 ns 0.643 ns
Dict_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 11.79 ns 0.287 ns 0.268 ns
Dict_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 19.65 ns 0.448 ns 0.748 ns
Dict_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 20.27 ns 0.430 ns 0.753 ns
Dict_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 90.00 ns 1.773 ns 2.760 ns
IReadOnly_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 20.07 ns 0.445 ns 0.609 ns
IReadOnly_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 12.96 ns 0.302 ns 0.336 ns
IReadOnly_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 18.35 ns 0.379 ns 0.674 ns
IReadOnly_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 18.50 ns 0.330 ns 0.292 ns
IReadOnly_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 1,467.53 ns 29.111 ns 34.654 ns
Dict_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 19.85 ns 0.409 ns 0.502 ns
Dict_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 12.44 ns 0.258 ns 0.229 ns
Dict_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 17.86 ns 0.404 ns 0.686 ns
Dict_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 17.96 ns 0.363 ns 0.485 ns
Dict_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET Framework 4.8.1 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 566.91 ns 11.265 ns 18.822 ns
Method Job AnalyzeLaunchVariance EvaluateOverhead MaxAbsoluteError MaxRelativeError MinInvokeCount MinIterationTime OutlierMode Affinity EnvironmentVariables Jit LargeAddressAware Platform PowerPlanMode Runtime AllowVeryLargeObjects Concurrent CpuGroups Force HeapAffinitizeMask HeapCount NoAffinitize RetainVm Server Arguments BuildConfiguration Clock EngineFactory NuGetReferences Toolchain IsMutator InvocationCount IterationCount IterationTime LaunchCount MaxIterationCount MaxWarmupIterationCount MemoryRandomization MinIterationCount MinWarmupIterationCount RunStrategy UnrollFactor WarmupCount N Mean Error StdDev
IReadOnly_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 15.774 ns 0.3626 ns 0.5752 ns
IReadOnly_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 11.035 ns 0.2500 ns 0.2568 ns
IReadOnly_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 14.993 ns 0.3524 ns 0.4456 ns
IReadOnly_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 15.868 ns 0.2705 ns 0.2398 ns
IReadOnly_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 212.092 ns 2.8244 ns 2.6420 ns
Dict_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 15.571 ns 0.3478 ns 0.3866 ns
Dict_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 11.124 ns 0.2663 ns 0.3368 ns
Dict_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 14.634 ns 0.3226 ns 0.4828 ns
Dict_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 14.713 ns 0.3133 ns 0.4877 ns
Dict_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 83.452 ns 1.6642 ns 2.3330 ns
IReadOnly_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 13.184 ns 0.3082 ns 0.4320 ns
IReadOnly_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 9.468 ns 0.2417 ns 0.3466 ns
IReadOnly_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 12.159 ns 0.2932 ns 0.4110 ns
IReadOnly_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 15.843 ns 0.3034 ns 0.2838 ns
IReadOnly_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 1,473.212 ns 28.0964 ns 28.8529 ns
Dict_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 12.811 ns 0.3093 ns 0.5335 ns
Dict_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 9.024 ns 0.2307 ns 0.2369 ns
Dict_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 11.587 ns 0.2717 ns 0.2269 ns
Dict_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 11.532 ns 0.2509 ns 0.3262 ns
Dict_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 6.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 577.780 ns 11.2449 ns 12.0319 ns
Method Job AnalyzeLaunchVariance EvaluateOverhead MaxAbsoluteError MaxRelativeError MinInvokeCount MinIterationTime OutlierMode Affinity EnvironmentVariables Jit LargeAddressAware Platform PowerPlanMode Runtime AllowVeryLargeObjects Concurrent CpuGroups Force HeapAffinitizeMask HeapCount NoAffinitize RetainVm Server Arguments BuildConfiguration Clock EngineFactory NuGetReferences Toolchain IsMutator InvocationCount IterationCount IterationTime LaunchCount MaxIterationCount MaxWarmupIterationCount MemoryRandomization MinIterationCount MinWarmupIterationCount RunStrategy UnrollFactor WarmupCount N Mean Error StdDev
IReadOnly_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 14.310 ns 0.3387 ns 0.4636 ns
IReadOnly_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 8.526 ns 0.2216 ns 0.2552 ns
IReadOnly_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 13.559 ns 0.2659 ns 0.2357 ns
IReadOnly_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 13.944 ns 0.3001 ns 0.4107 ns
IReadOnly_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 219.806 ns 1.1967 ns 1.0608 ns
Dict_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 12.944 ns 0.3071 ns 0.3993 ns
Dict_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 8.135 ns 0.1853 ns 0.2060 ns
Dict_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 13.742 ns 0.3144 ns 0.2941 ns
Dict_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 13.114 ns 0.2898 ns 0.2711 ns
Dict_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 16 14.668 ns 0.3219 ns 0.6354 ns
IReadOnly_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 11.548 ns 0.2854 ns 0.3398 ns
IReadOnly_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 7.340 ns 0.2004 ns 0.2675 ns
IReadOnly_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 11.527 ns 0.2837 ns 0.3977 ns
IReadOnly_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 11.365 ns 0.2478 ns 0.2069 ns
IReadOnly_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 1,666.039 ns 4.8648 ns 4.0623 ns
Dict_TryGetValue_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 11.440 ns 0.2640 ns 0.5511 ns
Dict_TryGetValue_Miss DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 6.504 ns 0.1818 ns 0.2777 ns
Dict_Indexer_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 10.980 ns 0.2141 ns 0.2103 ns
Dict_ContainsKey_Hit DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 12.604 ns 0.2798 ns 0.3330 ns
Dict_Enumerate_All DefaultJob False Default Default Default Default Default Default 1111111111111111 Empty RyuJit Default X64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 8.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 128 126.604 ns 2.3813 ns 3.9125 ns
@daiplusplus
Copy link
Author

image

@daiplusplus
Copy link
Author

Some thoughts for possible future revisions:

  1. Problem: The Enumerate_All methods only invoke Dictionary<K,V>.GetEnumerator() (or IReadOnlyDictionary<K,V>.GetEnumerator() once - but then make hundreds+ of calls to either the nonvirtual struct Dictionary<K,V>.Enumerator.MoveNext() method - or the virtual IEnumerator<Kvp<K,V>>.MoveNext() method.
    • We'd need a new extension-method: IReadOnlyDictionary.TryGetValueTypeEnumerator() - and then it starts getting too silly.
  2. Idea: Add a 3rd mode: a readonly struct ReadOnlyDictionary<K,V> wrapper struct which would allow for exposing only read-only members to consumers while avoiding indirection and vtable calls through any interface type.
  3. Idea: Add a 4th mode: adapt methods to use interface, but as generic-type constraints (i.e. like Swift protocol types) used with struct wrappers for zero-virtual-call code-paths - at the cost of hideous amounts of hair-pulling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment