Skip to content

Bad LOH allocations when deregistering from IOptionsMonitor #112127

Open
@Tragetaschen

Description

@Tragetaschen

Description

We have an Orleans cluster and in the silo, each grain depends on global configuration. Since that can change during runtime, we are using IOptionsMonitor and when the grain is done, we dispose the handle we got from the OnChange call. That last dispose call is the lone source of some hefty LOH allocations. The grains come and go constantly and at a certain load on the silo (around 2500 grains), LOH allocations start to happen.

As far as I can tell, IOptionsMonitor.Dispose only deregisters from the internal multicast delegate and this deregistration is the main culprit: The multicast delegate allocates a new invocation list for each -=, so memory consumption becomes O(n) while it's amortized O(1) for += due to exponential growth.

This small benchmark shows the problem. The allocations grow with the number of already registered delegates.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<Benchmark>();

[SimpleJob(iterationCount: 5)]
[MemoryDiagnoser]
public class Benchmark
{
    private Action? _methods;

    [Params(8, 10, 12, 14)]
    public int Power { get; set; }

    [GlobalSetup]
    public void Setup()
    {
        _methods += Method;
        for (var i = 0; i < Power; ++i)
        {
            _methods += _methods;
        }
    }

    [Benchmark]
    public void AddRemove()
    {
        var m = new Action(Method);
        _methods += m;
        _methods -= m;
    }

    private void Method() { }
}
| Method    | Power | Mean       | Error      | StdDev     | Gen0     | Gen1     | Gen2     | Allocated |
|---------- |------ |-----------:|-----------:|-----------:|---------:|---------:|---------:|----------:|
| AddRemove | 8     |   1.512 us |  0.8605 us |  0.2235 us |   0.7629 |   0.0172 |        - |   6.23 KB |
| AddRemove | 10    |   5.376 us |  2.8169 us |  0.4359 us |   2.9602 |   0.1831 |        - |  24.23 KB |
| AddRemove | 12    |  18.840 us |  1.0445 us |  0.1616 us |  11.7493 |   1.4648 |        - |  96.23 KB |
| AddRemove | 14    | 135.817 us | 74.9703 us | 19.4695 us | 124.8779 | 124.8779 | 124.8779 | 384.28 KB |

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions