-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the new Lock type to Concurrent Dicionary #104575
base: main
Are you sure you want to change the base?
Add the new Lock type to Concurrent Dicionary #104575
Conversation
Tagging subscribers to this area: @dotnet/area-system-collections |
{ | ||
locks[i] = new object(); | ||
locks[i] = new Lock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now allocating an extra object. Is it worth it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The microbenchmarks from the performance repo that use ConcurrentDictionary were tested with this change and got the following results:
// * Summary *
BenchmarkDotNet v0.13.13-nightly.20240311.145, Windows 11 (10.0.22631.3880/23H2/2023Update/SunValley3) (Hyper-V)
AMD EPYC 7763, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.100-preview.2.24157.14
[Host] : .NET 9.0.0 (9.0.24.12805), X64 RyuJIT AVX2
Job-DHIZMB : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
Job-XIGAPM : .NET 9.0.0 (42.42.42.42424), X64 RyuJIT AVX2
PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20
MinIterationCount=15 WarmupCount=1
| Type | Method | Job | Toolchain | Count | Size | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|--------------------------------- |--------------------- |----------- |------------------------- |------ |----- |------------:|----------:|----------:|------------:|------------:|------------:|------:|--------:|-------:|-------:|----------:|------------:|
| CtorDefaultSize<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | ? | 146.9 ns | 1.90 ns | 1.69 ns | 146.9 ns | 144.5 ns | 150.6 ns | 1.00 | 0.02 | 0.0599 | - | 1008 B | 1.00 |
| CtorDefaultSize<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | ? | 154.4 ns | 2.94 ns | 2.60 ns | 154.7 ns | 150.6 ns | 158.6 ns | 1.05 | 0.02 | 0.0766 | - | 1288 B | 1.28 |
| | | | | | | | | | | | | | | | | | |
| CtorDefaultSize<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | ? | 153.5 ns | 2.50 ns | 2.34 ns | 152.9 ns | 149.0 ns | 157.1 ns | 1.00 | 0.02 | 0.0601 | - | 1008 B | 1.00 |
| CtorDefaultSize<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | ? | 167.7 ns | 2.21 ns | 1.96 ns | 167.7 ns | 164.0 ns | 171.7 ns | 1.09 | 0.02 | 0.0768 | - | 1288 B | 1.28 |
| | | | | | | | | | | | | | | | | | |
| TryAddDefaultSize<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | 512 | ? | 35,559.7 ns | 142.64 ns | 111.36 ns | 35,587.8 ns | 35,336.2 ns | 35,691.1 ns | 1.00 | 0.00 | 5.7078 | 0.9989 | 96672 B | 1.00 |
| TryAddDefaultSize<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | 512 | ? | 33,709.7 ns | 193.49 ns | 161.57 ns | 33,719.4 ns | 33,359.2 ns | 33,906.9 ns | 0.95 | 0.01 | 6.2633 | 1.4659 | 104888 B | 1.08 |
| | | | | | | | | | | | | | | | | | |
| TryAddDefaultSize<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | 512 | ? | 47,032.4 ns | 426.37 ns | 356.03 ns | 46,839.7 ns | 46,595.5 ns | 47,572.0 ns | 1.00 | 0.01 | 6.1937 | 1.5015 | 105736 B | 1.00 |
| TryAddDefaultSize<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | 512 | ? | 45,092.8 ns | 361.32 ns | 301.71 ns | 45,087.3 ns | 44,725.2 ns | 45,851.2 ns | 0.96 | 0.01 | 6.6643 | 1.6210 | 113952 B | 1.08 |
| | | | | | | | | | | | | | | | | | |
| TryAddGiventSize<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | 512 | ? | 24,125.2 ns | 160.18 ns | 141.99 ns | 24,149.0 ns | 23,912.9 ns | 24,347.5 ns | 1.00 | 0.01 | 3.1342 | 0.4609 | 53192 B | 1.00 |
| TryAddGiventSize<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | 512 | ? | 21,889.2 ns | 87.38 ns | 77.46 ns | 21,894.8 ns | 21,766.5 ns | 22,016.3 ns | 0.91 | 0.01 | 3.1381 | 0.4358 | 53280 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| TryAddGiventSize<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | 512 | ? | 33,368.4 ns | 280.54 ns | 248.69 ns | 33,312.0 ns | 33,023.7 ns | 33,729.8 ns | 1.00 | 0.01 | 3.6058 | 0.6677 | 60624 B | 1.00 |
| TryAddGiventSize<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | 512 | ? | 31,855.5 ns | 141.53 ns | 118.19 ns | 31,862.5 ns | 31,674.6 ns | 32,023.8 ns | 0.95 | 0.01 | 3.5497 | 0.5071 | 60712 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| ContainsKeyFalse<Int32, Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 2,137.6 ns | 2.29 ns | 2.03 ns | 2,137.5 ns | 2,133.5 ns | 2,141.5 ns | 1.00 | 0.00 | - | - | - | NA |
| ContainsKeyFalse<Int32, Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 2,144.4 ns | 12.83 ns | 10.72 ns | 2,141.1 ns | 2,135.6 ns | 2,171.0 ns | 1.00 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| ContainsKeyFalse<String, String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 6,749.0 ns | 60.43 ns | 56.52 ns | 6,725.9 ns | 6,700.9 ns | 6,892.5 ns | 1.00 | 0.01 | - | - | - | NA |
| ContainsKeyFalse<String, String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 6,702.5 ns | 7.91 ns | 6.61 ns | 6,702.5 ns | 6,692.5 ns | 6,714.9 ns | 0.99 | 0.01 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| ContainsKeyTrue<Int32, Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 2,328.8 ns | 5.03 ns | 4.46 ns | 2,328.0 ns | 2,323.2 ns | 2,339.0 ns | 1.00 | 0.00 | - | - | - | NA |
| ContainsKeyTrue<Int32, Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 2,323.4 ns | 7.95 ns | 7.05 ns | 2,324.8 ns | 2,302.4 ns | 2,330.3 ns | 1.00 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| ContainsKeyTrue<String, String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 9,051.3 ns | 12.19 ns | 10.18 ns | 9,051.3 ns | 9,036.7 ns | 9,071.9 ns | 1.00 | 0.00 | - | - | - | NA |
| ContainsKeyTrue<String, String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 9,166.5 ns | 26.97 ns | 21.06 ns | 9,163.3 ns | 9,126.9 ns | 9,215.3 ns | 1.01 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| TryGetValueFalse<Int32, Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 809.2 ns | 3.61 ns | 3.38 ns | 807.7 ns | 805.3 ns | 816.4 ns | 1.00 | 0.01 | - | - | - | NA |
| TryGetValueFalse<Int32, Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 757.5 ns | 2.76 ns | 2.31 ns | 757.8 ns | 752.8 ns | 760.9 ns | 0.94 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| TryGetValueFalse<String, String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 5,382.0 ns | 9.90 ns | 8.77 ns | 5,379.7 ns | 5,371.4 ns | 5,400.3 ns | 1.00 | 0.00 | - | - | - | NA |
| TryGetValueFalse<String, String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 5,368.2 ns | 11.25 ns | 9.97 ns | 5,365.8 ns | 5,354.6 ns | 5,384.0 ns | 1.00 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| TryGetValueTrue<Int32, Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 1,028.3 ns | 5.74 ns | 4.79 ns | 1,029.6 ns | 1,018.0 ns | 1,034.2 ns | 1.00 | 0.01 | - | - | - | NA |
| TryGetValueTrue<Int32, Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 1,018.1 ns | 7.80 ns | 6.92 ns | 1,018.3 ns | 1,006.8 ns | 1,030.0 ns | 0.99 | 0.01 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| TryGetValueTrue<String, String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 6,856.2 ns | 27.24 ns | 25.48 ns | 6,846.1 ns | 6,832.0 ns | 6,923.0 ns | 1.00 | 0.01 | - | - | - | NA |
| TryGetValueTrue<String, String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 6,853.1 ns | 17.52 ns | 15.54 ns | 6,848.5 ns | 6,832.4 ns | 6,882.9 ns | 1.00 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| CtorGivenSize<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 195.4 ns | 5.16 ns | 5.94 ns | 194.8 ns | 184.9 ns | 208.3 ns | 1.00 | 0.04 | 0.2658 | 0.0041 | 4448 B | 1.00 |
| CtorGivenSize<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 198.8 ns | 4.99 ns | 5.75 ns | 199.7 ns | 188.8 ns | 207.0 ns | 1.02 | 0.04 | 0.2705 | 0.0041 | 4536 B | 1.02 |
| | | | | | | | | | | | | | | | | | |
| CtorGivenSize<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 200.3 ns | 4.96 ns | 5.71 ns | 201.2 ns | 188.5 ns | 209.8 ns | 1.00 | 0.04 | 0.2651 | 0.0041 | 4448 B | 1.00 |
| CtorGivenSize<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 208.5 ns | 5.94 ns | 6.84 ns | 209.4 ns | 197.8 ns | 225.5 ns | 1.04 | 0.04 | 0.2707 | 0.0041 | 4536 B | 1.02 |
| | | | | | | | | | | | | | | | | | |
| AddGivenSize<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 23,733.1 ns | 173.66 ns | 145.01 ns | 23,736.0 ns | 23,521.6 ns | 24,027.5 ns | 1.00 | 0.01 | 3.1250 | 0.4735 | 53192 B | 1.00 |
| AddGivenSize<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 21,869.5 ns | 172.93 ns | 144.41 ns | 21,836.5 ns | 21,589.6 ns | 22,208.2 ns | 0.92 | 0.01 | 3.1557 | 0.4383 | 53280 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| AddGivenSize<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 34,198.4 ns | 496.09 ns | 464.04 ns | 34,005.0 ns | 33,664.4 ns | 35,291.5 ns | 1.00 | 0.02 | 3.6212 | 0.6706 | 60624 B | 1.00 |
| AddGivenSize<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 31,877.2 ns | 127.37 ns | 99.44 ns | 31,882.3 ns | 31,719.9 ns | 32,068.1 ns | 0.93 | 0.01 | 3.5497 | 0.5071 | 60712 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| CtorFromCollection<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 21,349.9 ns | 203.97 ns | 180.81 ns | 21,357.7 ns | 21,086.6 ns | 21,701.8 ns | 1.00 | 0.01 | 2.9721 | 0.4246 | 50960 B | 1.00 |
| CtorFromCollection<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 20,783.7 ns | 104.29 ns | 87.09 ns | 20,788.1 ns | 20,611.7 ns | 20,929.2 ns | 0.97 | 0.01 | 2.9880 | 0.4150 | 51240 B | 1.01 |
| | | | | | | | | | | | | | | | | | |
| CtorFromCollection<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 33,567.2 ns | 354.90 ns | 314.61 ns | 33,482.9 ns | 33,278.8 ns | 34,427.2 ns | 1.00 | 0.01 | 3.4722 | 0.5342 | 58568 B | 1.00 |
| CtorFromCollection<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 33,210.4 ns | 263.84 ns | 220.32 ns | 33,133.4 ns | 33,002.3 ns | 33,788.0 ns | 0.99 | 0.01 | 3.4211 | 0.5263 | 58848 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| CreateAddAndClear<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 41,078.2 ns | 178.16 ns | 157.94 ns | 41,072.5 ns | 40,857.0 ns | 41,358.6 ns | 1.00 | 0.01 | 5.8901 | 0.8181 | 99120 B | 1.00 |
| CreateAddAndClear<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 37,487.7 ns | 181.74 ns | 151.76 ns | 37,522.6 ns | 37,251.5 ns | 37,796.3 ns | 0.91 | 0.00 | 6.3836 | 1.4846 | 107336 B | 1.08 |
| | | | | | | | | | | | | | | | | | |
| CreateAddAndClear<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 52,386.4 ns | 423.13 ns | 375.09 ns | 52,289.1 ns | 51,567.3 ns | 53,020.4 ns | 1.00 | 0.01 | 6.4156 | 1.0348 | 108184 B | 1.00 |
| CreateAddAndClear<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 48,391.9 ns | 232.04 ns | 193.76 ns | 48,380.8 ns | 48,053.7 ns | 48,826.8 ns | 0.92 | 0.01 | 6.7724 | 1.1610 | 116400 B | 1.08 |
| | | | | | | | | | | | | | | | | | |
| IterateForEach<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 6,504.7 ns | 12.54 ns | 10.47 ns | 6,504.2 ns | 6,491.8 ns | 6,532.0 ns | 1.00 | 0.00 | - | - | 56 B | 1.00 |
| IterateForEach<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 6,501.1 ns | 9.04 ns | 8.01 ns | 6,499.1 ns | 6,490.1 ns | 6,516.5 ns | 1.00 | 0.00 | - | - | 56 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| IterateForEach<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 11,213.8 ns | 17.22 ns | 15.26 ns | 11,210.4 ns | 11,190.8 ns | 11,239.8 ns | 1.00 | 0.00 | - | - | 64 B | 1.00 |
| IterateForEach<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 11,235.4 ns | 20.17 ns | 17.88 ns | 11,230.3 ns | 11,210.9 ns | 11,270.7 ns | 1.00 | 0.00 | - | - | 64 B | 1.00 |
| | | | | | | | | | | | | | | | | | |
| IndexerSet<Int32> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 12,385.7 ns | 25.35 ns | 22.47 ns | 12,380.9 ns | 12,352.8 ns | 12,436.3 ns | 1.00 | 0.00 | - | - | - | NA |
| IndexerSet<Int32> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 10,775.9 ns | 32.44 ns | 30.35 ns | 10,772.9 ns | 10,713.8 ns | 10,814.7 ns | 0.87 | 0.00 | - | - | - | NA |
| | | | | | | | | | | | | | | | | | |
| IndexerSet<String> | ConcurrentDictionary | Job-DHIZMB | \CD-Baseline\corerun.exe | ? | 512 | 19,120.0 ns | 34.56 ns | 28.86 ns | 19,117.7 ns | 19,070.8 ns | 19,181.9 ns | 1.00 | 0.00 | - | - | - | NA |
| IndexerSet<String> | ConcurrentDictionary | Job-XIGAPM | \CD-Changed\corerun.exe | ? | 512 | 17,607.4 ns | 43.18 ns | 40.39 ns | 17,593.5 ns | 17,547.0 ns | 17,687.8 ns | 0.92 | 0.00 | - | - | - | NA |
// * Warnings *
Environment
Summary -> Benchmark was executed on the virtual machine with Hyper-V hypervisor. Virtualization can affect the measurement result.
// * Hints *
Outliers
CtorDefaultSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (160.56 ns)
CtorDefaultSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (168.01 ns)
CtorDefaultSize<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (180.52 ns)
TryAddDefaultSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 3 outliers were removed (35.95 us..36.51 us)
TryAddDefaultSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (34.30 us, 35.56 us)
TryAddDefaultSize<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (49.20 us, 49.22 us)
TryAddDefaultSize<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (46.25 us, 46.41 us)
TryAddGiventSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (24.90 us)
TryAddGiventSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (22.64 us)
TryAddGiventSize<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (34.48 us)
TryAddGiventSize<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (32.38 us, 33.04 us)
ContainsKeyFalse<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (2.16 us)
ContainsKeyFalse<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (2.18 us, 2.28 us)
ContainsKeyFalse<String, String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (6.78 us, 6.89 us)
ContainsKeyTrue<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (2.37 us)
ContainsKeyTrue<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed, 2 outliers were detected (2.31 us, 2.34 us)
ContainsKeyTrue<String, String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (9.11 us, 9.13 us)
ContainsKeyTrue<String, String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 3 outliers were removed (9.29 us..9.37 us)
TryGetValueFalse<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (768.02 ns, 778.45 ns)
TryGetValueFalse<String, String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (5.47 us)
TryGetValueFalse<String, String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (5.44 us)
TryGetValueTrue<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (1.06 us, 1.14 us)
TryGetValueTrue<Int32, Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (1.04 us)
TryGetValueTrue<String, String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (6.95 us)
AddGivenSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (24.30 us, 24.34 us)
AddGivenSize<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (22.26 us, 22.53 us)
AddGivenSize<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 3 outliers were removed (32.46 us..33.00 us)
CtorFromCollection<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (22.65 us)
CtorFromCollection<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (21.10 us, 21.85 us)
CtorFromCollection<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (34.69 us)
CtorFromCollection<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (34.03 us, 34.27 us)
CreateAddAndClear<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (42.19 us)
CreateAddAndClear<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (37.98 us, 38.88 us)
CreateAddAndClear<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (54.51 us)
CreateAddAndClear<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (49.32 us, 50.11 us)
IterateForEach<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (6.54 us, 6.54 us)
IterateForEach<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (6.54 us)
IterateForEach<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (11.28 us)
IterateForEach<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Changed\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (11.38 us)
IndexerSet<Int32>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 1 outlier was removed (12.64 us)
IndexerSet<String>.ConcurrentDictionary: PowerPlanMode=00000000-0000-0000-0000-000000000000, Toolchain=\CD-Baseline\corerun.exe, IterationTime=250ms, MaxIterationCount=20, MinIterationCount=15, WarmupCount=1 -> 2 outliers were removed (19.24 us, 19.38 us)
// * Legends *
Count : Value of the 'Count' parameter
Size : Value of the 'Size' parameter
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Median : Value separating the higher half of all measurements (50th percentile)
Min : Minimum
Max : Maximum
Ratio : Mean of the ratio distribution ([Current]/[Baseline])
RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline])
Gen0 : GC Generation 0 collects per 1000 operations
Gen1 : GC Generation 1 collects per 1000 operations
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
Alloc Ratio : Allocated memory ratio distribution ([Current]/[Baseline])
1 ns : 1 Nanosecond (0.000000001 sec)
The following test case was also used to test the average number of accesses per unit of time the ConcurrentDictionary can handle with this change:
using System.Collections.Concurrent;
using System.Diagnostics;
const int KeyPoolCount = 128 << 10;
const int KeyPoolMask = KeyPoolCount - 1;
const int KeyCount = 4 << 10;
const int KeyMask = KeyCount - 1;
// Populate the key pool with unique nonzero keys. Zero is reserved for ownership.
var keyPool = new int[KeyPoolCount];
for (int i = 0; i < KeyPoolCount; i++)
keyPool[i] = i + 1;
// Populate the keys that will exist in the CD with unique random keys from the pool. The pool entries from which the
// keys are taken are replaced with zeroes.
int rngSeed;
if (args.Length == 0)
{
rngSeed = new Random().Next();
Console.WriteLine($"Rng seed: {rngSeed}");
}
else
rngSeed = (int)uint.Parse(args[0]);
var rng = new Random(rngSeed);
var keys = new int[KeyCount];
for (int i = 0; i < KeyCount; i++)
{
int keyPoolIndex = rng.Next() & KeyPoolMask;
int key = keyPool[keyPoolIndex];
if (key == 0)
{
for (int j = keyPoolIndex + 1; ; j++)
{
keyPoolIndex = j & KeyPoolMask;
key = keyPool[keyPoolIndex];
if (key != 0)
break;
}
}
keyPool[keyPoolIndex] = 0;
keys[i] = key;
}
// Populate the CD with the unique keys
var cd = new ConcurrentDictionary<int, int>();
for (int i = 0; i < KeyCount; i++)
{
if (!cd.TryAdd(keys[i], 0))
throw new InvalidOperationException();
}
int threadCount = Environment.ProcessorCount;
var threadIterationCounters = new int[(threadCount + 1) * 32];
ParameterizedThreadStart threadMain = data =>
{
var tupleData = (Tuple<int, int>)data;
int threadIndex = tupleData.Item1;
int rngSeed = tupleData.Item2;
var rng = new Random(rngSeed);
ref int threadIterationCounter = ref threadIterationCounters[(threadIndex + 1) * 32];
var localKeyPool = keyPool;
var localKeys = keys;
var localCd = cd;
while (true)
{
int randomInt = rng.Next();
// Select a new key and take ownership of the key pool entry
int keyPoolIndex = randomInt & KeyPoolMask;
int newKey = Interlocked.Exchange(ref localKeyPool[keyPoolIndex], 0);
if (newKey == 0)
{
for (int j = keyPoolIndex + 1; ; j++)
{
keyPoolIndex = j & KeyPoolMask;
newKey = Interlocked.Exchange(ref localKeyPool[keyPoolIndex], 0);
if (newKey != 0)
break;
}
}
// Select an existing key and take ownership of the existing key entry
int keyIndex = randomInt & KeyMask;
int key = Interlocked.Exchange(ref localKeys[keyIndex], 0);
if (key == 0)
{
for (int j = keyIndex + 1; ; j++)
{
keyIndex = j & KeyMask;
key = Interlocked.Exchange(ref localKeys[keyIndex], 0);
if (key != 0)
break;
}
}
if (!cd.TryRemove(key, out _) || !cd.TryAdd(newKey, 0))
throw new InvalidOperationException();
// Release the keys and ownerships
localKeyPool[keyPoolIndex] = key;
localKeys[keyIndex] = newKey;
threadIterationCounter++;
}
};
for (int i = 0; i < threadCount; i++)
{
var t = new Thread(threadMain);
t.IsBackground = true;
t.Start(new Tuple<int, int>(i, rng.Next()));
}
var sw = new Stopwatch();
// Warmup
Thread.Sleep(6000);
// Measurements
int totalIterations = 0;
double totalDurationMs = 0;
for (int i = 0; i < 8; i++)
{
int startIterations = 0;
int endIterations = 0;
for (int j = 0; j < threadCount; j++)
startIterations += threadIterationCounters[(j + 1) * 32];
sw.Restart();
Thread.Sleep(1000);
for (int j = 0; j < threadCount; j++)
endIterations += threadIterationCounters[(j + 1) * 32];
sw.Stop();
int iterations = endIterations - startIterations;
double durationMs = sw.Elapsed.TotalMilliseconds;
Console.WriteLine($"Iterations/ms: {iterations / durationMs,10:0.0}");
totalIterations += iterations;
totalDurationMs += durationMs;
}
Console.WriteLine($"Average iter/ms: {totalIterations / totalDurationMs,10:0.0}");
After running the test several times, the results were the following:
Baseline | Changed | Diff (%) | |
---|---|---|---|
Average iterations/ms | 40486.8 | 40549.2 | 0.15% |
The tests so far show that the performance of ConcurrentDictionary with these changes is relatively fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CtorDefaultSize<Int32>
shows exactly the same allocation before/after. How is that possible? The new version is obviously allocating one more Lock object than it was before. The Lock objects are also bigger than the System.Objects that were being used before. How should I think about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, seems like I had some error while collecting the data, I updated the comment above
No description provided.