Skip to content

Commit 77a7325

Browse files
authored
better initial size (#422)
* estimate * smaller table * tools * cleanup * calculate 4% gap * rename * verify max size * lookup * 33% * handle overflow * handle overflow * commen * comment * rename arg * cleanup hashtools ---------
1 parent 63d9b61 commit 77a7325

File tree

11 files changed

+461
-170
lines changed

11 files changed

+461
-170
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
using Xunit.Abstractions;
4+
5+
namespace BitFaster.Caching.UnitTests
6+
{
7+
public class ConcurrentDictionarySizeTests
8+
{
9+
private readonly ITestOutputHelper testOutputHelper;
10+
11+
public ConcurrentDictionarySizeTests(ITestOutputHelper testOutputHelper)
12+
{
13+
this.testOutputHelper = testOutputHelper;
14+
}
15+
16+
[Theory]
17+
[InlineData(3, 7)]
18+
[InlineData(8, 11)]
19+
[InlineData(12, 17)]
20+
[InlineData(196, 197)]
21+
[InlineData(500, 197)]
22+
public void NextPrimeGreaterThan(int input, int nextPrime)
23+
{
24+
ConcurrentDictionarySize.NextPrimeGreaterThan(input).Should().Be(nextPrime);
25+
}
26+
27+
[Theory]
28+
[InlineData(3, 7)]
29+
[InlineData(8, 11)]
30+
[InlineData(12, 17)]
31+
[InlineData(196, 137)]
32+
[InlineData(276, 179)]
33+
[InlineData(330, 221)]
34+
[InlineData(1553355606, 250478587)] // test larger than last SizeMap entry
35+
[InlineData(2003828731, 250478587)] // test overflow
36+
public void Estimate(int input, int optimal)
37+
{
38+
ConcurrentDictionarySize.Estimate(input).Should().Be(optimal);
39+
}
40+
}
41+
}

BitFaster.Caching.UnitTests/HashTablePrimesTests.cs

Lines changed: 0 additions & 121 deletions
This file was deleted.
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace BitFaster.Caching
5+
{
6+
internal class ConcurrentDictionarySize
7+
{
8+
internal static int NextPrimeGreaterThan(int min)
9+
{
10+
foreach (int prime in Primes)
11+
{
12+
if (prime > min)
13+
{
14+
return prime;
15+
}
16+
}
17+
18+
return 197;
19+
}
20+
21+
/// <summary>
22+
/// Estimate the size of the ConcurrentDictionary constructor capacity arg to use for the given desired cache size.
23+
/// </summary>
24+
/// <remarks>
25+
/// To minimize collisions, ideal case is is for ConcurrentDictionary to have a prime number of buckets, and
26+
/// for the bucket count to be about 33% greater than the cache capacity (load factor of 0.75).
27+
/// See load factor here: https://en.wikipedia.org/wiki/Hash_table
28+
/// </remarks>
29+
/// <param name="desiredSize">The desired cache size</param>
30+
/// <returns>The estimated optimal ConcurrentDictionary capacity</returns>
31+
internal static int Estimate(int desiredSize)
32+
{
33+
// Size map entries are approx 4% apart in the worst case, so increase by 29% to target 33%.
34+
// In practice, this leads to the number of buckets being somewhere between 29% and 40% greater
35+
// than cache capacity.
36+
try
37+
{
38+
checked
39+
{
40+
desiredSize = (int)(desiredSize * 1.29);
41+
}
42+
43+
// When small, exact size hashtable to nearest larger prime number
44+
if (desiredSize < 197)
45+
{
46+
return NextPrimeGreaterThan(desiredSize);
47+
}
48+
49+
// When large, size to approx 10% of desired size to save memory. Initial value is chosen such
50+
// that 4x ConcurrentDictionary grow operations will select a prime number slightly larger
51+
// than desired size.
52+
foreach (var pair in SizeMap)
53+
{
54+
if (pair.Key > desiredSize)
55+
{
56+
return pair.Value;
57+
}
58+
}
59+
}
60+
catch (OverflowException)
61+
{
62+
// return largest
63+
}
64+
65+
// Use largest mapping: ConcurrentDictionary will resize to max array size after 4x grow calls.
66+
return SizeMap[SizeMap.Length - 1].Value;
67+
}
68+
69+
#if NETSTANDARD2_0
70+
internal static int[] Primes = new int[] {
71+
#else
72+
internal static ReadOnlySpan<int> Primes => new int[] {
73+
#endif
74+
7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197
75+
};
76+
77+
#if NETSTANDARD2_0
78+
internal static KeyValuePair<int, int>[] SizeMap =
79+
#else
80+
internal static ReadOnlySpan<KeyValuePair<int, int>> SizeMap =>
81+
#endif
82+
new KeyValuePair<int, int>[129]
83+
{
84+
new KeyValuePair<int, int>(197, 197),
85+
new KeyValuePair<int, int>(277, 137),
86+
new KeyValuePair<int, int>(331, 163),
87+
new KeyValuePair<int, int>(359, 179),
88+
new KeyValuePair<int, int>(397, 197),
89+
new KeyValuePair<int, int>(443, 221),
90+
new KeyValuePair<int, int>(499, 247),
91+
new KeyValuePair<int, int>(557, 137),
92+
new KeyValuePair<int, int>(599, 149),
93+
new KeyValuePair<int, int>(677, 167),
94+
new KeyValuePair<int, int>(719, 179),
95+
new KeyValuePair<int, int>(797, 197),
96+
new KeyValuePair<int, int>(839, 209),
97+
new KeyValuePair<int, int>(887, 221),
98+
new KeyValuePair<int, int>(1061, 131),
99+
new KeyValuePair<int, int>(1117, 137),
100+
new KeyValuePair<int, int>(1237, 151),
101+
new KeyValuePair<int, int>(1439, 179),
102+
new KeyValuePair<int, int>(1559, 193),
103+
new KeyValuePair<int, int>(1777, 221),
104+
new KeyValuePair<int, int>(2011, 247),
105+
new KeyValuePair<int, int>(2179, 269),
106+
new KeyValuePair<int, int>(2347, 289),
107+
new KeyValuePair<int, int>(2683, 331),
108+
new KeyValuePair<int, int>(2797, 347),
109+
new KeyValuePair<int, int>(3359, 419),
110+
new KeyValuePair<int, int>(3917, 487),
111+
new KeyValuePair<int, int>(4363, 541),
112+
new KeyValuePair<int, int>(4597, 571),
113+
new KeyValuePair<int, int>(5879, 733),
114+
new KeyValuePair<int, int>(7517, 937),
115+
new KeyValuePair<int, int>(8731, 1087),
116+
new KeyValuePair<int, int>(9839, 1229),
117+
new KeyValuePair<int, int>(17467, 2179),
118+
new KeyValuePair<int, int>(18397, 2297),
119+
new KeyValuePair<int, int>(20357, 2543),
120+
new KeyValuePair<int, int>(24317, 3037),
121+
new KeyValuePair<int, int>(25919, 3239),
122+
new KeyValuePair<int, int>(29759, 3719),
123+
new KeyValuePair<int, int>(31357, 3917),
124+
new KeyValuePair<int, int>(33599, 4199),
125+
new KeyValuePair<int, int>(38737, 4841),
126+
new KeyValuePair<int, int>(41117, 5137),
127+
new KeyValuePair<int, int>(48817, 6101),
128+
new KeyValuePair<int, int>(61819, 7723),
129+
new KeyValuePair<int, int>(72959, 9119),
130+
new KeyValuePair<int, int>(86011, 10747),
131+
new KeyValuePair<int, int>(129277, 16157),
132+
new KeyValuePair<int, int>(140797, 17597),
133+
new KeyValuePair<int, int>(164477, 20557),
134+
new KeyValuePair<int, int>(220411, 27547),
135+
new KeyValuePair<int, int>(233851, 29227),
136+
new KeyValuePair<int, int>(294397, 36797),
137+
new KeyValuePair<int, int>(314879, 39359),
138+
new KeyValuePair<int, int>(338683, 42331),
139+
new KeyValuePair<int, int>(389117, 48637),
140+
new KeyValuePair<int, int>(409597, 51197),
141+
new KeyValuePair<int, int>(436477, 54557),
142+
new KeyValuePair<int, int>(609277, 76157),
143+
new KeyValuePair<int, int>(651517, 81437),
144+
new KeyValuePair<int, int>(737279, 92159),
145+
new KeyValuePair<int, int>(849917, 106237),
146+
new KeyValuePair<int, int>(1118203, 139771),
147+
new KeyValuePair<int, int>(1269757, 158717),
148+
new KeyValuePair<int, int>(1440763, 180091),
149+
new KeyValuePair<int, int>(1576957, 197117),
150+
new KeyValuePair<int, int>(1684477, 210557),
151+
new KeyValuePair<int, int>(2293757, 286717),
152+
new KeyValuePair<int, int>(2544637, 318077),
153+
new KeyValuePair<int, int>(2666491, 333307),
154+
new KeyValuePair<int, int>(2846717, 355837),
155+
new KeyValuePair<int, int>(3368957, 421117),
156+
new KeyValuePair<int, int>(3543037, 442877),
157+
new KeyValuePair<int, int>(4472827, 559099),
158+
new KeyValuePair<int, int>(4710397, 588797),
159+
new KeyValuePair<int, int>(5038079, 629759),
160+
new KeyValuePair<int, int>(5763067, 720379),
161+
new KeyValuePair<int, int>(6072317, 759037),
162+
new KeyValuePair<int, int>(6594557, 824317),
163+
new KeyValuePair<int, int>(7913467, 989179),
164+
new KeyValuePair<int, int>(8257531, 1032187),
165+
new KeyValuePair<int, int>(9175037, 1146877),
166+
new KeyValuePair<int, int>(9633787, 1204219),
167+
new KeyValuePair<int, int>(10076159, 1259519),
168+
new KeyValuePair<int, int>(11386877, 1423357),
169+
new KeyValuePair<int, int>(14020603, 1752571),
170+
new KeyValuePair<int, int>(16056317, 2007037),
171+
new KeyValuePair<int, int>(19496957, 2437117),
172+
new KeyValuePair<int, int>(20848637, 2606077),
173+
new KeyValuePair<int, int>(24084479, 3010559),
174+
new KeyValuePair<int, int>(27934717, 3491837),
175+
new KeyValuePair<int, int>(29589499, 3698683),
176+
new KeyValuePair<int, int>(32788477, 4098557),
177+
new KeyValuePair<int, int>(36044797, 4505597),
178+
new KeyValuePair<int, int>(38051837, 4756477),
179+
new KeyValuePair<int, int>(43581437, 5447677),
180+
new KeyValuePair<int, int>(51814397, 6476797),
181+
new KeyValuePair<int, int>(56688637, 7086077),
182+
new KeyValuePair<int, int>(60948479, 7618559),
183+
new KeyValuePair<int, int>(69631997, 8703997),
184+
new KeyValuePair<int, int>(75366397, 9420797),
185+
new KeyValuePair<int, int>(78643199, 9830399),
186+
new KeyValuePair<int, int>(96337919, 12042239),
187+
new KeyValuePair<int, int>(106168319, 13271039),
188+
new KeyValuePair<int, int>(115671037, 14458877),
189+
new KeyValuePair<int, int>(132382717, 16547837),
190+
new KeyValuePair<int, int>(144179197, 18022397),
191+
new KeyValuePair<int, int>(165150719, 20643839),
192+
new KeyValuePair<int, int>(178257917, 22282237),
193+
new KeyValuePair<int, int>(188743679, 23592959),
194+
new KeyValuePair<int, int>(209715197, 26214397),
195+
new KeyValuePair<int, int>(254279677, 31784957),
196+
new KeyValuePair<int, int>(297271291, 37158907),
197+
new KeyValuePair<int, int>(314572799, 39321599),
198+
new KeyValuePair<int, int>(385351679, 48168959),
199+
new KeyValuePair<int, int>(453509117, 56688637),
200+
new KeyValuePair<int, int>(517472251, 64684027),
201+
new KeyValuePair<int, int>(644874239, 80609279),
202+
new KeyValuePair<int, int>(673710077, 84213757),
203+
new KeyValuePair<int, int>(770703359, 96337919),
204+
new KeyValuePair<int, int>(849346559, 106168319),
205+
new KeyValuePair<int, int>(903086077, 112885757),
206+
new KeyValuePair<int, int>(1145044987, 143130619),
207+
new KeyValuePair<int, int>(1233125371, 154140667),
208+
new KeyValuePair<int, int>(1321205759, 165150719),
209+
new KeyValuePair<int, int>(1394606077, 174325757),
210+
new KeyValuePair<int, int>(1635778559, 204472319),
211+
new KeyValuePair<int, int>(1855979519, 231997439),
212+
new KeyValuePair<int, int>(2003828731, 250478587),
213+
};
214+
}
215+
}

0 commit comments

Comments
 (0)