All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.plugins.csharp.S6612.html Maven / Gradle / Ivy

There is a newer version: 10.2.0.105762
Show newest version

Why is this an issue?

When using the ConcurrentDictionary, there are many overloads of the GetOrAdd and AddOrUpdate methods that take both a TKey argument and a lambda that expects a TKey parameter. This means that the right side of the lambda can be written using either the lambda’s parameter or the method’s argument. However, using the method’s argument leads to the lambda capturing it, and the compiler will need to generate a class and instantiate it before the call. This means memory allocations, as well as more time spend during Garbage Collection.

What is the potential impact?

We measured a significant improvement both in execution time and memory allocation. For more details see the Benchmarks section from the More info tab.

How to fix it

When you are using the ConcurrentDictionary methods GetOrAdd or AddOrUpdate, reference the key by using the lambda’s parameter instead of the method’s one.

Code examples

Noncompliant code example

int UpdateValue(ConcurrentDictionary<int, int> dict, int key) =>
    dict.GetOrAdd(key, _ => key + 42);

Compliant solution

int UpdateValue(ConcurrentDictionary<int, int> dict, int key) =>
    dict.GetOrAdd(key, x => x + 42);

Resources

Documentation

Benchmarks

Method Runtime Mean Standard Deviation Allocated

Capture

.NET 7.0

68.52 ms

4.450 ms

88000063 B

Lambda

.NET 7.0

39.29 ms

3.712 ms

50 B

Capture

.NET Framework 4.6.2

74.58 ms

5.199 ms

88259787 B

Lambda

.NET Framework 4.6.2

42.03 ms

2.752 ms

-

Glossary

The results were generated by running the following snippet with BenchmarkDotNet:

private ConcurrentDictionary<int, string> dict;
private List<int> data;

[Params(1_000_000)]
public int N { get; set; }

[GlobalSetup]
public void Setup()
{
    dict = new ConcurrentDictionary<int, string>();
    data = Enumerable.Range(0, N).OrderBy(_ => Guid.NewGuid()).ToList();
}

[Benchmark(baseline=true)]
public void Capture()
{
    foreach (var guid in data)
    {
        dict.GetOrAdd(guid, _ => $"{guid}"); // "guid" is captured
    }
}

[Benchmark]
public void Lambda()
{
    foreach (var guid in data)
    {
        dict.GetOrAdd(guid, x => $"{x}"); // no capture
    }
}

Hardware configuration:

BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.203
  [Host]               : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
  .NET 7.0             : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
  .NET Framework 4.6.2 : .NET Framework 4.8.1 (4.8.9139.0), X64 RyuJIT VectorSize=256




© 2015 - 2024 Weber Informatics LLC | Privacy Policy