FrozenDictionary (new in .NET 8) v other dictionaries
Date Added (UTC):
05 Apr 2024 @ 05:30
Date Updated (UTC):13 Apr 2024 @ 00:15
.NET Version(s): Tag(s):
#.Net8PerfImprovement #Collections
Added By:
.NET Developer and tech lead from Ireland!
Benchmark Results:
Benchmark Code:
using System.Collections.Generic;
using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using System.Collections.Frozen;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Immutable;
using BenchmarkDotNet.Environments;
namespace Benchmarks
{
[Config(typeof(Config))]
[HideColumns(Column.Job, Column.RatioSD, Column.AllocRatio, Column.Gen0, Column.Gen1)]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[MemoryDiagnoser]
public class DictionaryLookupBenchmarks
{
private KeyValuePair<string, int>[] _items;
private string[] _keys;
private Dictionary<string, int> _dictionary;
private ReadOnlyDictionary<string, int> _readOnlyDictionary;
private ImmutableDictionary<string, int> _immutableDictionary;
private FrozenDictionary<string, int> _frozenDictionary;
[Params(1000)]
public int Items;
[GlobalSetup]
public void GLobalSetup()
{
_items = Enumerable
.Range(0, Items)
.Select(_ => new KeyValuePair<string, int>(Guid.NewGuid().ToString(), 0))
.ToArray();
_keys = _items.Select(k => k.Key).ToArray();
_dictionary = new Dictionary<string, int>(_items);
_readOnlyDictionary = new ReadOnlyDictionary<string, int>(_items.ToDictionary(i => i.Key, i => i.Value));
_immutableDictionary = ImmutableDictionary.ToImmutableDictionary(_items);
_frozenDictionary = FrozenDictionary.ToFrozenDictionary(_items);
}
[BenchmarkCategory("Construct"), Benchmark(Baseline = true)]
public Dictionary<string, int> ConstructDictionary() =>
new Dictionary<string, int>(_items);
[BenchmarkCategory("Construct"), Benchmark]
public ReadOnlyDictionary<string, int> ConstructReadOnlyDictionary() =>
new ReadOnlyDictionary<string, int>(_items.ToDictionary(i => i.Key, i => i.Value));
[BenchmarkCategory("Construct"), Benchmark]
public ImmutableDictionary<string, int> ConstructImmutableDictionary() =>
ImmutableDictionary.ToImmutableDictionary(_items);
[BenchmarkCategory("Construct"), Benchmark]
public FrozenDictionary<string, int> ConstructFrozenDictionary() =>
FrozenDictionary.ToFrozenDictionary(_items);
[BenchmarkCategory("TryGetValue_Found"), Benchmark(Baseline = true)]
public bool Dictionary_TryGetValue_Found()
{
bool allFound = true;
foreach (string key in _keys)
{
int value;
allFound &= _dictionary.TryGetValue(key, out value);
}
return allFound;
}
[BenchmarkCategory("TryGetValue_Found"), Benchmark]
public bool ReadOnlyDictionary_TryGetValue_Found()
{
bool allFound = true;
foreach (string key in _keys)
{
int value;
allFound &= _readOnlyDictionary.TryGetValue(key, out value);
}
return allFound;
}
[BenchmarkCategory("TryGetValue_Found"), Benchmark]
public bool ImmutableDictionary_TryGetValue_Found()
{
bool allFound = true;
foreach (string key in _keys)
{
int value;
allFound &= _immutableDictionary.TryGetValue(key, out value);
}
return allFound;
}
[BenchmarkCategory("TryGetValue_Found"), Benchmark]
public bool FrozenDictionary_TryGetValue_Found()
{
bool allFound = true;
foreach (string key in _keys)
{
int value;
allFound &= _frozenDictionary.TryGetValue(key, out value);
}
return allFound;
}
private class Config : ManualConfig
{
public Config()
{
AddJob(Job.Default.WithId(".NET 8").WithRuntime(CoreRuntime.Core80));
SummaryStyle =
SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage);
}
}
}
}
Powered by SharpLab
// .NET 8
public Dictionary<string, int> ConstructDictionary()
{
return new Dictionary<string, int>(_items);
}
// .NET 8
public ReadOnlyDictionary<string, int> ConstructReadOnlyDictionary()
{
return new ReadOnlyDictionary<string, int>(Enumerable.ToDictionary(_items, <> c.<> 9__9_0 ?? (<> c.<> 9__9_0 = new Func<KeyValuePair<string, int>, string>(<> c.<> 9.< ConstructReadOnlyDictionary > b__9_0)), <> c.<> 9__9_1 ?? (<> c.<> 9__9_1 = new Func<KeyValuePair<string, int>, int>(<> c.<> 9.< ConstructReadOnlyDictionary > b__9_1))));
}
// .NET 8
public ImmutableDictionary<string, int> ConstructImmutableDictionary()
{
return ImmutableDictionary.ToImmutableDictionary(_items);
}
// .NET 8
public FrozenDictionary<string, int> ConstructFrozenDictionary()
{
return FrozenDictionary.ToFrozenDictionary(_items);
}
// .NET 8
public bool Dictionary_TryGetValue_Found()
{
bool flag = true;
string[] keys = _keys;
int num = 0;
while (num < keys.Length)
{
string key = keys[num];
int value;
flag &= _dictionary.TryGetValue(key, out value);
num++;
}
return flag;
}
// .NET 8
public bool ReadOnlyDictionary_TryGetValue_Found()
{
bool flag = true;
string[] keys = _keys;
int num = 0;
while (num < keys.Length)
{
string key = keys[num];
int value;
flag &= _readOnlyDictionary.TryGetValue(key, out value);
num++;
}
return flag;
}
// .NET 8
public bool ImmutableDictionary_TryGetValue_Found()
{
bool flag = true;
string[] keys = _keys;
int num = 0;
while (num < keys.Length)
{
string key = keys[num];
int value;
flag &= _immutableDictionary.TryGetValue(key, out value);
num++;
}
return flag;
}
// .NET 8
public bool FrozenDictionary_TryGetValue_Found()
{
bool flag = true;
string[] keys = _keys;
int num = 0;
while (num < keys.Length)
{
string key = keys[num];
int value;
flag &= _frozenDictionary.TryGetValue(key, out value);
num++;
}
return flag;
}
Powered by SharpLab
// .NET 8
.method public hidebysig
instance class [System.Collections]System.Collections.Generic.Dictionary`2<string, int32> ConstructDictionary () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 09 43 6f 6e 73 74 72 75 63 74
00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 40 00 00 00 01 5f 01 00 54 02 08 42 61 73
65 6c 69 6e 65 01
)
// Method begins at RVA 0x2155
// Code size 12 (0xc)
.maxstack 8
// sequence point: (line 66, col 13) to (line 66, col 48) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>[] Benchmarks.DictionaryLookupBenchmarks::_items
IL_0006: newobj instance void class [System.Collections]System.Collections.Generic.Dictionary`2<string, int32>::.ctor(class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<!0, !1>>)
IL_000b: ret
}
// .NET 8
.method public hidebysig
instance class [System.Runtime]System.Collections.ObjectModel.ReadOnlyDictionary`2<string, int32> ConstructReadOnlyDictionary () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 09 43 6f 6e 73 74 72 75 63 74
00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 44 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2164
// Code size 79 (0x4f)
.maxstack 4
// sequence point: (line 70, col 13) to (line 70, col 95) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>[] Benchmarks.DictionaryLookupBenchmarks::_items
IL_0006: ldsfld class [System.Runtime]System.Func`2<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, string> Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<>9__9_0'
IL_000b: dup
IL_000c: brtrue.s IL_0025
IL_000e: pop
IL_000f: ldsfld class Benchmarks.DictionaryLookupBenchmarks/'<>c' Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<>9'
IL_0014: ldftn instance string Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<ConstructReadOnlyDictionary>b__9_0'(valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>)
IL_001a: newobj instance void class [System.Runtime]System.Func`2<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, string>::.ctor(object, native int)
IL_001f: dup
IL_0020: stsfld class [System.Runtime]System.Func`2<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, string> Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<>9__9_0'
IL_0025: ldsfld class [System.Runtime]System.Func`2<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, int32> Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<>9__9_1'
IL_002a: dup
IL_002b: brtrue.s IL_0044
IL_002d: pop
IL_002e: ldsfld class Benchmarks.DictionaryLookupBenchmarks/'<>c' Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<>9'
IL_0033: ldftn instance int32 Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<ConstructReadOnlyDictionary>b__9_1'(valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>)
IL_0039: newobj instance void class [System.Runtime]System.Func`2<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, int32>::.ctor(object, native int)
IL_003e: dup
IL_003f: stsfld class [System.Runtime]System.Func`2<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, int32> Benchmarks.DictionaryLookupBenchmarks/'<>c'::'<>9__9_1'
IL_0044: call class [System.Collections]System.Collections.Generic.Dictionary`2<!!1, !!2> [System.Linq]System.Linq.Enumerable::ToDictionary<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>, string, int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Runtime]System.Func`2<!!0, !!1>, class [System.Runtime]System.Func`2<!!0, !!2>)
IL_0049: newobj instance void class [System.Runtime]System.Collections.ObjectModel.ReadOnlyDictionary`2<string, int32>::.ctor(class [System.Runtime]System.Collections.Generic.IDictionary`2<!0, !1>)
IL_004e: ret
}
// .NET 8
.method public hidebysig
instance class [System.Collections.Immutable]System.Collections.Immutable.ImmutableDictionary`2<string, int32> ConstructImmutableDictionary () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 09 43 6f 6e 73 74 72 75 63 74
00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 48 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x21bf
// Code size 12 (0xc)
.maxstack 8
// sequence point: (line 74, col 13) to (line 74, col 62) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>[] Benchmarks.DictionaryLookupBenchmarks::_items
IL_0006: call class [System.Collections.Immutable]System.Collections.Immutable.ImmutableDictionary`2<!!0, !!1> [System.Collections.Immutable]System.Collections.Immutable.ImmutableDictionary::ToImmutableDictionary<string, int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<!!0, !!1>>)
IL_000b: ret
}
// .NET 8
.method public hidebysig
instance class [System.Collections.Immutable]System.Collections.Frozen.FrozenDictionary`2<string, int32> ConstructFrozenDictionary () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 09 43 6f 6e 73 74 72 75 63 74
00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 4c 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x21cc
// Code size 13 (0xd)
.maxstack 8
// sequence point: (line 78, col 13) to (line 78, col 56) in _
IL_0000: ldarg.0
IL_0001: ldfld valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<string, int32>[] Benchmarks.DictionaryLookupBenchmarks::_items
IL_0006: ldnull
IL_0007: call class [System.Collections.Immutable]System.Collections.Frozen.FrozenDictionary`2<!!0, !!1> [System.Collections.Immutable]System.Collections.Frozen.FrozenDictionary::ToFrozenDictionary<string, int32>(class [System.Runtime]System.Collections.Generic.IEnumerable`1<valuetype [System.Runtime]System.Collections.Generic.KeyValuePair`2<!!0, !!1>>, class [System.Runtime]System.Collections.Generic.IEqualityComparer`1<!!0>)
IL_000c: ret
}
// .NET 8
.method public hidebysig
instance bool Dictionary_TryGetValue_Found () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 11 54 72 79 47 65 74 56 61 6c
75 65 5f 46 6f 75 6e 64 00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 54 00 00 00 01 5f 01 00 54 02 08 42 61 73
65 6c 69 6e 65 01
)
// Method begins at RVA 0x21dc
// Code size 46 (0x2e)
.maxstack 4
.locals init (
[0] bool allFound,
[1] string[],
[2] int32,
[3] string key,
[4] int32 'value'
)
// sequence point: (line 87, col 13) to (line 87, col 34) in _
IL_0000: ldc.i4.1
IL_0001: stloc.0
// sequence point: (line 89, col 36) to (line 89, col 41) in _
IL_0002: ldarg.0
IL_0003: ldfld string[] Benchmarks.DictionaryLookupBenchmarks::_keys
IL_0008: stloc.1
IL_0009: ldc.i4.0
IL_000a: stloc.2
// sequence point: hidden
IL_000b: br.s IL_0026
// loop start (head: IL_0026)
// sequence point: (line 89, col 22) to (line 89, col 32) in _
IL_000d: ldloc.1
IL_000e: ldloc.2
IL_000f: ldelem.ref
IL_0010: stloc.3
// sequence point: (line 92, col 17) to (line 92, col 69) in _
IL_0011: ldloc.0
IL_0012: ldarg.0
IL_0013: ldfld class [System.Collections]System.Collections.Generic.Dictionary`2<string, int32> Benchmarks.DictionaryLookupBenchmarks::_dictionary
IL_0018: ldloc.3
IL_0019: ldloca.s 4
IL_001b: callvirt instance bool class [System.Collections]System.Collections.Generic.Dictionary`2<string, int32>::TryGetValue(!0, !1&)
IL_0020: and
IL_0021: stloc.0
// sequence point: hidden
IL_0022: ldloc.2
IL_0023: ldc.i4.1
IL_0024: add
IL_0025: stloc.2
// sequence point: (line 89, col 33) to (line 89, col 35) in _
IL_0026: ldloc.2
IL_0027: ldloc.1
IL_0028: ldlen
IL_0029: conv.i4
IL_002a: blt.s IL_000d
// end loop
// sequence point: (line 95, col 13) to (line 95, col 29) in _
IL_002c: ldloc.0
IL_002d: ret
}
// .NET 8
.method public hidebysig
instance bool ReadOnlyDictionary_TryGetValue_Found () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 11 54 72 79 47 65 74 56 61 6c
75 65 5f 46 6f 75 6e 64 00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 62 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2218
// Code size 46 (0x2e)
.maxstack 4
.locals init (
[0] bool allFound,
[1] string[],
[2] int32,
[3] string key,
[4] int32 'value'
)
// sequence point: (line 101, col 13) to (line 101, col 34) in _
IL_0000: ldc.i4.1
IL_0001: stloc.0
// sequence point: (line 103, col 36) to (line 103, col 41) in _
IL_0002: ldarg.0
IL_0003: ldfld string[] Benchmarks.DictionaryLookupBenchmarks::_keys
IL_0008: stloc.1
IL_0009: ldc.i4.0
IL_000a: stloc.2
// sequence point: hidden
IL_000b: br.s IL_0026
// loop start (head: IL_0026)
// sequence point: (line 103, col 22) to (line 103, col 32) in _
IL_000d: ldloc.1
IL_000e: ldloc.2
IL_000f: ldelem.ref
IL_0010: stloc.3
// sequence point: (line 106, col 17) to (line 106, col 77) in _
IL_0011: ldloc.0
IL_0012: ldarg.0
IL_0013: ldfld class [System.Runtime]System.Collections.ObjectModel.ReadOnlyDictionary`2<string, int32> Benchmarks.DictionaryLookupBenchmarks::_readOnlyDictionary
IL_0018: ldloc.3
IL_0019: ldloca.s 4
IL_001b: callvirt instance bool class [System.Runtime]System.Collections.ObjectModel.ReadOnlyDictionary`2<string, int32>::TryGetValue(!0, !1&)
IL_0020: and
IL_0021: stloc.0
// sequence point: hidden
IL_0022: ldloc.2
IL_0023: ldc.i4.1
IL_0024: add
IL_0025: stloc.2
// sequence point: (line 103, col 33) to (line 103, col 35) in _
IL_0026: ldloc.2
IL_0027: ldloc.1
IL_0028: ldlen
IL_0029: conv.i4
IL_002a: blt.s IL_000d
// end loop
// sequence point: (line 109, col 13) to (line 109, col 29) in _
IL_002c: ldloc.0
IL_002d: ret
}
// .NET 8
.method public hidebysig
instance bool ImmutableDictionary_TryGetValue_Found () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 11 54 72 79 47 65 74 56 61 6c
75 65 5f 46 6f 75 6e 64 00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 70 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2254
// Code size 46 (0x2e)
.maxstack 4
.locals init (
[0] bool allFound,
[1] string[],
[2] int32,
[3] string key,
[4] int32 'value'
)
// sequence point: (line 115, col 13) to (line 115, col 34) in _
IL_0000: ldc.i4.1
IL_0001: stloc.0
// sequence point: (line 117, col 36) to (line 117, col 41) in _
IL_0002: ldarg.0
IL_0003: ldfld string[] Benchmarks.DictionaryLookupBenchmarks::_keys
IL_0008: stloc.1
IL_0009: ldc.i4.0
IL_000a: stloc.2
// sequence point: hidden
IL_000b: br.s IL_0026
// loop start (head: IL_0026)
// sequence point: (line 117, col 22) to (line 117, col 32) in _
IL_000d: ldloc.1
IL_000e: ldloc.2
IL_000f: ldelem.ref
IL_0010: stloc.3
// sequence point: (line 120, col 17) to (line 120, col 78) in _
IL_0011: ldloc.0
IL_0012: ldarg.0
IL_0013: ldfld class [System.Collections.Immutable]System.Collections.Immutable.ImmutableDictionary`2<string, int32> Benchmarks.DictionaryLookupBenchmarks::_immutableDictionary
IL_0018: ldloc.3
IL_0019: ldloca.s 4
IL_001b: callvirt instance bool class [System.Collections.Immutable]System.Collections.Immutable.ImmutableDictionary`2<string, int32>::TryGetValue(!0, !1&)
IL_0020: and
IL_0021: stloc.0
// sequence point: hidden
IL_0022: ldloc.2
IL_0023: ldc.i4.1
IL_0024: add
IL_0025: stloc.2
// sequence point: (line 117, col 33) to (line 117, col 35) in _
IL_0026: ldloc.2
IL_0027: ldloc.1
IL_0028: ldlen
IL_0029: conv.i4
IL_002a: blt.s IL_000d
// end loop
// sequence point: (line 123, col 13) to (line 123, col 29) in _
IL_002c: ldloc.0
IL_002d: ret
}
// .NET 8
.method public hidebysig
instance bool FrozenDictionary_TryGetValue_Found () cil managed
{
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkCategoryAttribute::.ctor(string[]) = (
01 00 01 00 00 00 11 54 72 79 47 65 74 56 61 6c
75 65 5f 46 6f 75 6e 64 00 00
)
.custom instance void [BenchmarkDotNet.Annotations]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor(int32, string) = (
01 00 7e 00 00 00 01 5f 00 00
)
// Method begins at RVA 0x2290
// Code size 46 (0x2e)
.maxstack 4
.locals init (
[0] bool allFound,
[1] string[],
[2] int32,
[3] string key,
[4] int32 'value'
)
// sequence point: (line 129, col 13) to (line 129, col 34) in _
IL_0000: ldc.i4.1
IL_0001: stloc.0
// sequence point: (line 131, col 36) to (line 131, col 41) in _
IL_0002: ldarg.0
IL_0003: ldfld string[] Benchmarks.DictionaryLookupBenchmarks::_keys
IL_0008: stloc.1
IL_0009: ldc.i4.0
IL_000a: stloc.2
// sequence point: hidden
IL_000b: br.s IL_0026
// loop start (head: IL_0026)
// sequence point: (line 131, col 22) to (line 131, col 32) in _
IL_000d: ldloc.1
IL_000e: ldloc.2
IL_000f: ldelem.ref
IL_0010: stloc.3
// sequence point: (line 134, col 17) to (line 134, col 75) in _
IL_0011: ldloc.0
IL_0012: ldarg.0
IL_0013: ldfld class [System.Collections.Immutable]System.Collections.Frozen.FrozenDictionary`2<string, int32> Benchmarks.DictionaryLookupBenchmarks::_frozenDictionary
IL_0018: ldloc.3
IL_0019: ldloca.s 4
IL_001b: callvirt instance bool class [System.Collections.Immutable]System.Collections.Frozen.FrozenDictionary`2<string, int32>::TryGetValue(!0, !1&)
IL_0020: and
IL_0021: stloc.0
// sequence point: hidden
IL_0022: ldloc.2
IL_0023: ldc.i4.1
IL_0024: add
IL_0025: stloc.2
// sequence point: (line 131, col 33) to (line 131, col 35) in _
IL_0026: ldloc.2
IL_0027: ldloc.1
IL_0028: ldlen
IL_0029: conv.i4
IL_002a: blt.s IL_000d
// end loop
// sequence point: (line 137, col 13) to (line 137, col 29) in _
IL_002c: ldloc.0
IL_002d: ret
}
Powered by SharpLab
|
Benchmark Description:
FrozenDictionary (and FrozenSet) are new in .NET 8 and are optimized for reading. Frozen collections ***take longer to construct but offer much faster read times***. In this case of this benchmark run reading a value from a FrozenDictionary was 69% faster than reading a value from a normal Dictionary.
"_What ist the point of “System.Collections.Frozen” compared to “System.Collections.Immutable”?_"
This was asked on the devblogs site previously, here is Stephen Toubs answer ->
[devblogs.microsoft.com](https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-1/comment-page-2/#comment-17497)
The provided benchmark code is designed to measure and compare the performance of constructing and accessing elements in different types of dictionaries in .NET. The benchmarks are set up using the BenchmarkDotNet library, which is a powerful tool for benchmarking .NET code. The specific .NET version targeted is .NET 8, as indicated in the configuration setup.
### General Setup
- **.NET Version**: The benchmarks are configured to run against .NET 8, using the `CoreRuntime.Core80`.
- **Configuration**: A custom configuration class `Config` is defined, which specifies the runtime and modifies the summary style to display ratios in percentage.
- **Memory Diagnoser**: Enabled to measure and report the memory allocations for each benchmark.
- **Columns**: Certain columns like Job, RatioSD, AllocRatio, Gen0, Gen1 are hidden to focus on the most relevant metrics.
- **Grouping**: Benchmarks are grouped by category to make comparisons easier and more organized.
- **Parameters**: A parameter `Items` is defined to control the number of items in the dictionaries, set to 1000 items for these benchmarks.
### Benchmark Methods and Rationale
#### Construct Benchmarks
- **Purpose**: These benchmarks measure the time and memory overhead of constructing different types of dictionaries with a specified number of items.
- **Importance**: Understanding the cost of constructing dictionaries is crucial for performance-sensitive applications, especially when dictionaries are frequently created and disposed.
- **Expected Insights**: Users can expect to see how different dictionary implementations compare in terms of construction performance. Immutable and frozen dictionaries might show higher overhead due to their immutable nature.
#### TryGetValue_Found Benchmarks
- **Purpose**: These benchmarks measure the performance of retrieving values from dictionaries using the `TryGetValue` method, assuming all keys are present.
- **Importance**: The `TryGetValue` method is commonly used for lookups in dictionaries, and its performance is critical for applications that rely heavily on dictionary access.
- **Expected Insights**: These benchmarks will highlight the efficiency of each dictionary type in lookup operations. Immutable and frozen dictionaries might exhibit different performance characteristics compared to mutable dictionaries due to their underlying implementations.
### Specific Aspects Tested
- **Construction Performance**: How quickly and efficiently can each type of dictionary be instantiated and populated with a set number of items?
- **Lookup Performance**: How efficiently can each dictionary type find and return values for existing keys?
### Conclusion
By running these benchmarks, developers can gain valuable insights into the trade-offs between different dictionary implementations in .NET, particularly in terms of construction time, memory allocations, and lookup efficiency. This information can guide the choice of dictionary type based on the specific requirements and constraints of their applications, such as the need for immutability, read-only access, or performance considerations.