Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using BenchmarkDotNet.Attributes;
- using Microsoft.Extensions.Options;
- using System;
- using System.Collections.Concurrent;
- using System.Diagnostics.CodeAnalysis;
- using System.Runtime.Intrinsics.Arm;
- using System.Security.Cryptography;
- using System.Threading;
- using static System.Runtime.InteropServices.JavaScript.JSType;
- namespace OptionsMonitor
- {
- // For more information on the VS BenchmarkDotNet Diagnosers see https://learn.microsoft.com/visualstudio/profiling/profiling-with-benchmark-dotnet
- [MemoryDiagnoser]
- public class Benchmarks
- {
- private OptionsCache<MyOptions> optionsCache;
- private OptionsCacheBetter<MyOptions> optionsCacheBetter;
- [GlobalSetup]
- public void Setup()
- {
- optionsCache = new OptionsCache<MyOptions>();
- optionsCache.TryAdd(null, new MyOptions { Name = "Default", Value = 42 });
- optionsCacheBetter = new OptionsCacheBetter<MyOptions>();
- optionsCacheBetter.TryAdd(null, new MyOptions { Name = "Default", Value = 42 });
- }
- [Benchmark]
- public string Baseline()
- {
- var opt = optionsCache.GetOrAdd(null, () => new MyOptions { Name = "Default", Value = 42 });
- return opt.Name;
- }
- [Benchmark]
- public string Better()
- {
- var opt = optionsCacheBetter.GetOrAdd(null, () => new MyOptions { Name = "Default", Value = 42 });
- return opt.Name;
- }
- }
- class MyOptions
- {
- public string? Name { get; set; }
- public int Value { get; set; }
- }
- public class OptionsCacheBetter<TOptions> :
- IOptionsMonitorCache<TOptions>
- where TOptions : class
- {
- private readonly ConcurrentDictionary<string, Lazy<TOptions>> _cache = new ConcurrentDictionary<string, Lazy<TOptions>>(concurrencyLevel: 1, capacity: 31, StringComparer.Ordinal); // 31 == default capacity
- private Lazy<TOptions>? _defaultOptions = null;
- /// <summary>
- /// Clears all options instances from the cache.
- /// </summary>
- public void Clear() => _cache.Clear();
- /// <summary>
- /// Gets a named options instance, or adds a new instance created with <paramref name="createOptions"/>.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <param name="createOptions">The function used to create the new instance.</param>
- /// <returns>The options instance.</returns>
- public virtual TOptions GetOrAdd(string? name, Func<TOptions> createOptions)
- {
- ArgumentNullException.ThrowIfNull(createOptions);
- name ??= Options.DefaultName;
- Lazy<TOptions> value;
- if (name == Options.DefaultName)
- {
- if (_defaultOptions is null)
- {
- // We need a reference to the new instance to be able to return it. Usage of `return _defaultOptions.Value`
- // could technically save us some allocations but it would have a risk of sneaky race condition of .Clear
- // being called between the Interlocked.CompareExchange call assigning new value and the return, leading to NRE.
- var newDefaultOptions = new Lazy<TOptions>(createOptions);
- var result = Interlocked.CompareExchange(ref _defaultOptions, newDefaultOptions, null);
- return result is not null ? result.Value : newDefaultOptions.Value;
- }
- return _defaultOptions.Value;
- }
- #if NET || NETSTANDARD2_1
- value = _cache.GetOrAdd(name, static (name, createOptions) => new Lazy<TOptions>(createOptions), createOptions);
- #else
- if (!_cache.TryGetValue(name, out value))
- {
- value = _cache.GetOrAdd(name, new Lazy<TOptions>(createOptions));
- }
- #endif
- return value.Value;
- }
- internal TOptions GetOrAdd<TArg>(string? name, Func<string, TArg, TOptions> createOptions, TArg factoryArgument)
- {
- // For compatibility, fall back to public GetOrAdd() if we're in a derived class.
- // For simplicity, we do the same for older frameworks that don't support the factoryArgument overload of GetOrAdd().
- if (name == Options.DefaultName)
- {
- if (_defaultOptions is null)
- {
- // We need a reference to the new instance to be able to return it. Usage of `return _defaultOptions.Value`
- // could technically save us some allocations but it would have a risk of sneaky race condition of .Clear
- // being called between the Interlocked.CompareExchange call assigning new value and the return, leading to NRE.
- var newDefaultOptions = new Lazy<TOptions>(() => createOptions(Options.DefaultName, factoryArgument));
- var result = Interlocked.CompareExchange(ref _defaultOptions, newDefaultOptions, null);
- return result is not null ? result.Value : newDefaultOptions.Value;
- }
- return _defaultOptions.Value;
- }
- #if NET || NETSTANDARD2_1
- if (GetType() != typeof(OptionsCache<TOptions>))
- #endif
- {
- // copying captured variables to locals avoids allocating a closure if we don't enter the if
- string? localName = name;
- Func<string, TArg, TOptions> localCreateOptions = createOptions;
- TArg localFactoryArgument = factoryArgument;
- return GetOrAdd(name, () => localCreateOptions(localName ?? Options.DefaultName, localFactoryArgument));
- }
- #if NET || NETSTANDARD2_1
- return _cache.GetOrAdd(
- name ?? Options.DefaultName,
- static (name, arg) => new Lazy<TOptions>(() => arg.createOptions(name, arg.factoryArgument)), (createOptions, factoryArgument)).Value;
- #endif
- }
- /// <summary>
- /// Gets a named options instance, if available.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <param name="options">The options instance.</param>
- /// <returns><see langword="true"/> if the options were retrieved; otherwise, <see langword="false"/>.</returns>
- internal bool TryGetValue(string? name, [MaybeNullWhen(false)] out TOptions options)
- {
- if (name == Options.DefaultName)
- {
- if (_defaultOptions is { } defaultOptions)
- {
- options = defaultOptions.Value;
- return true;
- }
- options = default;
- return false;
- }
- if (_cache.TryGetValue(name ?? Options.DefaultName, out Lazy<TOptions>? lazy))
- {
- options = lazy.Value;
- return true;
- }
- options = default;
- return false;
- }
- /// <summary>
- /// Tries to adds a new option to the cache.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <param name="options">The options instance.</param>
- /// <returns><see langword="true"/> if the option was added; <see langword="false"/> if the name already exists.</returns>
- public virtual bool TryAdd(string? name, TOptions options)
- {
- ArgumentNullException.ThrowIfNull(options);
- if (name == Options.DefaultName)
- {
- if (_defaultOptions is not null)
- {
- return false; // Default options already exist
- }
- var result = Interlocked.CompareExchange(ref _defaultOptions, new Lazy<TOptions>(() => options), null);
- return result is null;
- }
- return _cache.TryAdd(name ?? Options.DefaultName, new Lazy<TOptions>(
- #if !(NET || NETSTANDARD2_1)
- () =>
- #endif
- options));
- }
- /// <summary>
- /// Tries to remove an options instance.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <returns><see langword="true"/> if anything was removed; otherwise, <see langword="false"/>.</returns>
- public virtual bool TryRemove(string? name)
- {
- if (name == Options.DefaultName)
- {
- if (_defaultOptions is not null)
- {
- var result = Interlocked.Exchange(ref _defaultOptions, null);
- return result is not null;
- }
- return false;
- }
- return _cache.TryRemove(name ?? Options.DefaultName, out _);
- }
- }
- public class OptionsCache<TOptions> :
- IOptionsMonitorCache<TOptions>
- where TOptions : class
- {
- private readonly ConcurrentDictionary<string, Lazy<TOptions>> _cache = new ConcurrentDictionary<string, Lazy<TOptions>>(concurrencyLevel: 1, capacity: 31, StringComparer.Ordinal); // 31 == default capacity
- /// <summary>
- /// Clears all options instances from the cache.
- /// </summary>
- public void Clear() => _cache.Clear();
- /// <summary>
- /// Gets a named options instance, or adds a new instance created with <paramref name="createOptions"/>.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <param name="createOptions">The function used to create the new instance.</param>
- /// <returns>The options instance.</returns>
- public virtual TOptions GetOrAdd(string? name, Func<TOptions> createOptions)
- {
- ArgumentNullException.ThrowIfNull(createOptions);
- name ??= Options.DefaultName;
- Lazy<TOptions> value;
- #if NET || NETSTANDARD2_1
- value = _cache.GetOrAdd(name, static (name, createOptions) => new Lazy<TOptions>(createOptions), createOptions);
- #else
- if (!_cache.TryGetValue(name, out value))
- {
- value = _cache.GetOrAdd(name, new Lazy<TOptions>(createOptions));
- }
- #endif
- return value.Value;
- }
- internal TOptions GetOrAdd<TArg>(string? name, Func<string, TArg, TOptions> createOptions, TArg factoryArgument)
- {
- // For compatibility, fall back to public GetOrAdd() if we're in a derived class.
- // For simplicity, we do the same for older frameworks that don't support the factoryArgument overload of GetOrAdd().
- #if NET || NETSTANDARD2_1
- if (GetType() != typeof(OptionsCache<TOptions>))
- #endif
- {
- // copying captured variables to locals avoids allocating a closure if we don't enter the if
- string? localName = name;
- Func<string, TArg, TOptions> localCreateOptions = createOptions;
- TArg localFactoryArgument = factoryArgument;
- return GetOrAdd(name, () => localCreateOptions(localName ?? Options.DefaultName, localFactoryArgument));
- }
- #if NET || NETSTANDARD2_1
- return _cache.GetOrAdd(
- name ?? Options.DefaultName,
- static (name, arg) => new Lazy<TOptions>(() => arg.createOptions(name, arg.factoryArgument)), (createOptions, factoryArgument)).Value;
- #endif
- }
- /// <summary>
- /// Gets a named options instance, if available.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <param name="options">The options instance.</param>
- /// <returns><see langword="true"/> if the options were retrieved; otherwise, <see langword="false"/>.</returns>
- internal bool TryGetValue(string? name, [MaybeNullWhen(false)] out TOptions options)
- {
- if (_cache.TryGetValue(name ?? Options.DefaultName, out Lazy<TOptions>? lazy))
- {
- options = lazy.Value;
- return true;
- }
- options = default;
- return false;
- }
- /// <summary>
- /// Tries to adds a new option to the cache.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <param name="options">The options instance.</param>
- /// <returns><see langword="true"/> if the option was added; <see langword="false"/> if the name already exists.</returns>
- public virtual bool TryAdd(string? name, TOptions options)
- {
- ArgumentNullException.ThrowIfNull(options);
- return _cache.TryAdd(name ?? Options.DefaultName, new Lazy<TOptions>(
- #if !(NET || NETSTANDARD2_1)
- () =>
- #endif
- options));
- }
- /// <summary>
- /// Tries to remove an options instance.
- /// </summary>
- /// <param name="name">The name of the options instance.</param>
- /// <returns><see langword="true"/> if anything was removed; otherwise, <see langword="false"/>.</returns>
- public virtual bool TryRemove(string? name) =>
- _cache.TryRemove(name ?? Options.DefaultName, out _);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment