Skip to content

Instantly share code, notes, and snippets.

@davepcallan
Last active February 8, 2024 19:04
Show Gist options
  • Save davepcallan/fc0bb7b84893db4dedf554d16b86d0d6 to your computer and use it in GitHub Desktop.
Save davepcallan/fc0bb7b84893db4dedf554d16b86d0d6 to your computer and use it in GitHub Desktop.
FrozenDictionary in .NET 8 benchmarks compared to Dictionary/ReadOnlyDictionary etc.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0</TargetFrameworks>
<ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.3.2064" />
</ItemGroup>
</Project>
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;
namespace BenchmarkDotNet.Samples
{
[Config(typeof(Config))]
[HideColumns(Column.Job, Column.RatioSD, Column.AllocRatio, Column.Gen0, Column.Gen1)]
[SimpleJob(RuntimeMoniker.Net80)]
[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;
private FrozenDictionary<string, int> _frozenDictionaryReadOptimized;
[Params(100, 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, false);
_frozenDictionaryReadOptimized = FrozenDictionary.ToFrozenDictionary(_items, true); //optimizedForReads
}
[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, false);
[BenchmarkCategory("Construct"), Benchmark]
public FrozenDictionary<string, int> ConstructFrozenDictionaryOptimized() =>
FrozenDictionary.ToFrozenDictionary(_items, true); //optimizedForReads
[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;
}
[BenchmarkCategory("TryGetValue_Found"), Benchmark]
public bool FrozenDictionaryOptimized_TryGetValue_Found()
{
bool allFound = true;
foreach (string key in _keys)
{
int value;
allFound &= _frozenDictionaryReadOptimized.TryGetValue(key, out value);
}
return allFound;
}
private class Config : ManualConfig
{
public Config()
{
SummaryStyle =
SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage);
}
}
}
}
using BenchmarkDotNet.Running;
internal class Program
{
private static void Main(string[] args)
{
//choose on command line from multiple benchmarks
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run();
}
}
@davepcallan
Copy link
Author

Will probably change the *_TryGetValue_Found() methods to call a generic method which take an IDictionary.

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