Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Extensions;
using MicroBenchmarks;

namespace System.Collections
{
[BenchmarkCategory(Categories.Libraries, Categories.Collections, Categories.GenericCollections)]
[GenericTypeArguments(typeof(int))] // value type
[GenericTypeArguments(typeof(string))] // reference type
[GenericTypeArguments(typeof(Guid))] // larger value type
public class DictionaryContainsKey<T>
{
private T[] _found;
private T[] _notFound;
private Dictionary<T, T> _dictionary;

[Params(Utils.DefaultCollectionSize)]
public int Size;

[GlobalSetup]
public void Setup()
{
var allKeys = ValuesGenerator.ArrayOfUniqueValues<T>(Size * 2);
_found = allKeys.Skip(Size).Take(Size).ToArray();
_notFound = allKeys.Take(Size).ToArray();
_dictionary = _found.ToDictionary(k => k, k => default(T)!);
}

[Benchmark]
public bool ContainsKeyTrue()
{
bool result = default;
Dictionary<T, T> collection = _dictionary;
T[] found = _found;
for (int i = 0; i < found.Length; i++)
result ^= collection.ContainsKey(found[i]);
return result;
}

[Benchmark]
public bool ContainsKeyFalse()
{
bool result = default;
Dictionary<T, T> collection = _dictionary;
T[] notFound = _notFound;
for (int i = 0; i < notFound.Length; i++)
result ^= collection.ContainsKey(notFound[i]);
return result;
}
}

/// <summary>
/// Measures ContainsKey on a 1M-entry dictionary (~20 MB) to capture behavior
/// when the hash table far exceeds L1/L2 cache. Probes 8192 keys per invocation
/// for realistic cache-miss pressure while keeping per-call time low enough
/// for stable BDN statistics (~1-2% noise).
/// </summary>
[BenchmarkCategory(Categories.Libraries, Categories.Collections, Categories.GenericCollections)]
public class DictionaryContainsKeyLarge
{
private const int DictSize = 1_000_000;
private const int ProbeCount = 8192;

private int[] _found;
private int[] _notFound;
private Dictionary<int, int> _dictionary;

[GlobalSetup]
public void Setup()
{
var allKeys = ValuesGenerator.ArrayOfUniqueValues<int>(DictSize + ProbeCount);
var inDict = allKeys.Take(DictSize).ToArray();
_found = inDict.Take(ProbeCount).ToArray();
_notFound = allKeys.Skip(DictSize).Take(ProbeCount).ToArray();
_dictionary = inDict.ToDictionary(k => k, k => k);
}

[Benchmark]
public bool ContainsKeyTrue()
{
bool result = default;
var collection = _dictionary;
var found = _found;
for (int i = 0; i < found.Length; i++)
result ^= collection.ContainsKey(found[i]);
return result;
}

[Benchmark]
public bool ContainsKeyFalse()
{
bool result = default;
var collection = _dictionary;
var notFound = _notFound;
for (int i = 0; i < notFound.Length; i++)
result ^= collection.ContainsKey(notFound[i]);
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Extensions;
using MicroBenchmarks;

namespace System.Collections
{
[BenchmarkCategory(Categories.Libraries, Categories.Collections, Categories.GenericCollections)]
[GenericTypeArguments(typeof(int))] // value type
[GenericTypeArguments(typeof(string))] // reference type
[GenericTypeArguments(typeof(Guid))] // larger value type
public class HashSetContains<T>
{
private T[] _found;
private T[] _notFound;
private HashSet<T> _hashSet;

[Params(Utils.DefaultCollectionSize)]
public int Size;

[GlobalSetup]
public void Setup()
{
var allKeys = ValuesGenerator.ArrayOfUniqueValues<T>(Size * 2);
_found = allKeys.Skip(Size).Take(Size).ToArray();
_notFound = allKeys.Take(Size).ToArray();
_hashSet = new HashSet<T>(_found);
}

[Benchmark]
public bool ContainsTrue()
{
bool result = default;
HashSet<T> collection = _hashSet;
T[] found = _found;
for (int i = 0; i < found.Length; i++)
result ^= collection.Contains(found[i]);
return result;
}

[Benchmark]
public bool ContainsFalse()
{
bool result = default;
HashSet<T> collection = _hashSet;
T[] notFound = _notFound;
for (int i = 0; i < notFound.Length; i++)
result ^= collection.Contains(notFound[i]);
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Extensions;
using MicroBenchmarks;

namespace System.Collections
{
[BenchmarkCategory(Categories.Libraries, Categories.Collections, Categories.GenericCollections)]
[GenericTypeArguments(typeof(int), typeof(int))] // small value type key+value
[GenericTypeArguments(typeof(string), typeof(string))] // reference type key+value
[GenericTypeArguments(typeof(Guid), typeof(int))] // larger value type key
public class DictionaryTryRemove<TKey, TValue>
{
private Dictionary<TKey, TValue> _dictionary;
private TKey[] _keys;

[Params(Utils.DefaultCollectionSize)]
public int Size;

[GlobalSetup]
public void Setup()
{
_keys = ValuesGenerator.ArrayOfUniqueValues<TKey>(Size);
_dictionary = _keys.ToDictionary(k => k, k => default(TValue)!);
}

[Benchmark]
public bool TryRemove_Hit()
{
var dict = _dictionary;
var keys = _keys;
bool result = false;
for (int i = 0; i < keys.Length; i++)
{
result = dict.Remove(keys[i], out _);
dict.Add(keys[i], default!);
}
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Extensions;
using MicroBenchmarks;

namespace System.Collections
{
[BenchmarkCategory(Categories.Libraries, Categories.Collections, Categories.GenericCollections)]
[GenericTypeArguments(typeof(int))] // value type
[GenericTypeArguments(typeof(string))] // reference type
[GenericTypeArguments(typeof(Guid))] // larger value type
public class RemoveFalse<T>
{
private T[] _missingKeys;
private HashSet<T> _hashSet;
private Dictionary<T, T> _dictionary;

[Params(Utils.DefaultCollectionSize)]
public int Size;

private T[] Setup()
{
var allKeys = ValuesGenerator.ArrayOfUniqueValues<T>(Size * 2);
_missingKeys = allKeys.Take(Size).ToArray();
return allKeys.Skip(Size).Take(Size).ToArray();
}

[GlobalSetup(Target = nameof(HashSet))]
public void SetupHashSet() => _hashSet = new HashSet<T>(Setup());

[Benchmark]
public bool HashSet()
{
bool result = false;
var collection = _hashSet;
var keys = _missingKeys;
for (int i = 0; i < keys.Length; i++)
result = collection.Remove(keys[i]);
return result;
}

[GlobalSetup(Target = nameof(Dictionary))]
public void SetupDictionary()
{
var source = Setup();
_dictionary = source.ToDictionary(k => k, k => default(T)!);
}

[Benchmark]
public bool Dictionary()
{
bool result = false;
var collection = _dictionary;
var keys = _missingKeys;
for (int i = 0; i < keys.Length; i++)
result = collection.Remove(keys[i]);
return result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Extensions;
using MicroBenchmarks;

namespace System.Collections
{
[BenchmarkCategory(Categories.Libraries, Categories.Collections, Categories.GenericCollections)]
[GenericTypeArguments(typeof(int))] // value type
[GenericTypeArguments(typeof(string))] // reference type
[GenericTypeArguments(typeof(Guid))] // larger value type
public class RemoveTrue<T>
{
private T[] _keys;
private HashSet<T> _hashSet;
private Dictionary<T, T> _dictionary;

[Params(Utils.DefaultCollectionSize)]
public int Size;

[GlobalSetup(Target = nameof(HashSet))]
public void SetupHashSet()
{
_keys = ValuesGenerator.ArrayOfUniqueValues<T>(Size);
_hashSet = new HashSet<T>(_keys);
}

[Benchmark]
public bool HashSet()
{
bool result = false;
var collection = _hashSet;
var keys = _keys;
for (int i = 0; i < keys.Length; i++)
{
result = collection.Remove(keys[i]);
collection.Add(keys[i]);
}
return result;
}

[GlobalSetup(Target = nameof(Dictionary))]
public void SetupDictionary()
{
_keys = ValuesGenerator.ArrayOfUniqueValues<T>(Size);
_dictionary = _keys.ToDictionary(k => k, k => default(T)!);
}

[Benchmark]
public bool Dictionary()
{
bool result = false;
var collection = _dictionary;
var keys = _keys;
for (int i = 0; i < keys.Length; i++)
{
result = collection.Remove(keys[i]);
collection.Add(keys[i], default!);
}
return result;
}
}
}