mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-11 18:20:26 +09:00
Remove some allocations related to storing CacheEntry scopes (#45563)
* CacheEntryStack._previous is never used so it can be removed * remove CacheEntryStack and ScopeLease as it's enough to have just AsyncLocal<CacheEntry> * the first Entry created has no previous entry, so the field is set to null * Update src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntryHelper.cs Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com> Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
This commit is contained in:
parent
d21fe17023
commit
50c2de9edb
4 changed files with 30 additions and 99 deletions
|
@ -24,7 +24,7 @@ namespace Microsoft.Extensions.Caching.Memory
|
|||
private TimeSpan? _absoluteExpirationRelativeToNow;
|
||||
private TimeSpan? _slidingExpiration;
|
||||
private long? _size;
|
||||
private IDisposable _scope;
|
||||
private CacheEntry _previous; // this field is not null only before the entry is added to the cache
|
||||
private object _value;
|
||||
private int _state; // actually a [Flag] enum called "State"
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace Microsoft.Extensions.Caching.Memory
|
|||
{
|
||||
Key = key ?? throw new ArgumentNullException(nameof(key));
|
||||
_cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
|
||||
_scope = CacheEntryHelper.EnterScope(this);
|
||||
_previous = CacheEntryHelper.EnterScope(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -145,11 +145,6 @@ namespace Microsoft.Extensions.Caching.Memory
|
|||
{
|
||||
IsDisposed = true;
|
||||
|
||||
// Ensure the _scope reference is cleared because it can reference other CacheEntry instances.
|
||||
// This CacheEntry is going to be put into a MemoryCache, and we don't want to root unnecessary objects.
|
||||
_scope.Dispose();
|
||||
_scope = null;
|
||||
|
||||
// Don't commit or propagate options if the CacheEntry Value was never set.
|
||||
// We assume an exception occurred causing the caller to not set the Value successfully,
|
||||
// so don't use this entry.
|
||||
|
@ -157,11 +152,14 @@ namespace Microsoft.Extensions.Caching.Memory
|
|||
{
|
||||
_cache.SetEntry(this);
|
||||
|
||||
if (CanPropagateOptions())
|
||||
if (_previous != null && CanPropagateOptions())
|
||||
{
|
||||
PropagateOptions(CacheEntryHelper.Current);
|
||||
PropagateOptions(_previous);
|
||||
}
|
||||
}
|
||||
|
||||
CacheEntryHelper.ExitScope(this, _previous);
|
||||
_previous = null; // we don't want to root unnecessary objects
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,65 +1,32 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Extensions.Caching.Memory
|
||||
{
|
||||
internal class CacheEntryHelper
|
||||
internal static class CacheEntryHelper
|
||||
{
|
||||
private static readonly AsyncLocal<CacheEntryStack> _scopes = new AsyncLocal<CacheEntryStack>();
|
||||
|
||||
internal static CacheEntryStack Scopes
|
||||
{
|
||||
get { return _scopes.Value; }
|
||||
set { _scopes.Value = value; }
|
||||
}
|
||||
private static readonly AsyncLocal<CacheEntry> _current = new AsyncLocal<CacheEntry>();
|
||||
|
||||
internal static CacheEntry Current
|
||||
{
|
||||
get
|
||||
{
|
||||
CacheEntryStack scopes = GetOrCreateScopes();
|
||||
return scopes.Peek();
|
||||
}
|
||||
get => _current.Value;
|
||||
private set => _current.Value = value;
|
||||
}
|
||||
|
||||
internal static IDisposable EnterScope(CacheEntry entry)
|
||||
internal static CacheEntry EnterScope(CacheEntry current)
|
||||
{
|
||||
CacheEntryStack scopes = GetOrCreateScopes();
|
||||
|
||||
var scopeLease = new ScopeLease(scopes);
|
||||
Scopes = scopes.Push(entry);
|
||||
|
||||
return scopeLease;
|
||||
CacheEntry previous = Current;
|
||||
Current = current;
|
||||
return previous;
|
||||
}
|
||||
|
||||
private static CacheEntryStack GetOrCreateScopes()
|
||||
internal static void ExitScope(CacheEntry current, CacheEntry previous)
|
||||
{
|
||||
CacheEntryStack scopes = Scopes;
|
||||
if (scopes == null)
|
||||
{
|
||||
scopes = CacheEntryStack.Empty;
|
||||
Scopes = scopes;
|
||||
}
|
||||
|
||||
return scopes;
|
||||
}
|
||||
|
||||
private sealed class ScopeLease : IDisposable
|
||||
{
|
||||
private readonly CacheEntryStack _cacheEntryStack;
|
||||
|
||||
public ScopeLease(CacheEntryStack cacheEntryStack)
|
||||
{
|
||||
_cacheEntryStack = cacheEntryStack;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Scopes = _cacheEntryStack;
|
||||
}
|
||||
Debug.Assert(Current == current, "Entries disposed in invalid order");
|
||||
Current = previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.Caching.Memory
|
||||
{
|
||||
internal class CacheEntryStack
|
||||
{
|
||||
private readonly CacheEntryStack _previous;
|
||||
private readonly CacheEntry _entry;
|
||||
|
||||
private CacheEntryStack()
|
||||
{
|
||||
}
|
||||
|
||||
private CacheEntryStack(CacheEntryStack previous, CacheEntry entry)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(previous));
|
||||
}
|
||||
|
||||
_previous = previous;
|
||||
_entry = entry;
|
||||
}
|
||||
|
||||
public static CacheEntryStack Empty { get; } = new CacheEntryStack();
|
||||
|
||||
public CacheEntryStack Push(CacheEntry c)
|
||||
{
|
||||
return new CacheEntryStack(this, c);
|
||||
}
|
||||
|
||||
public CacheEntry Peek()
|
||||
{
|
||||
return _entry;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -214,17 +214,23 @@ namespace Microsoft.Extensions.Caching.Memory
|
|||
object GetScope(ICacheEntry entry)
|
||||
{
|
||||
return entry.GetType()
|
||||
.GetField("_scope", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetField("_previous", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetValue(entry);
|
||||
}
|
||||
|
||||
var cache = CreateCache();
|
||||
|
||||
ICacheEntry entry = cache.CreateEntry("myKey");
|
||||
Assert.NotNull(GetScope(entry));
|
||||
ICacheEntry first = cache.CreateEntry("myKey1");
|
||||
Assert.Null(GetScope(first)); // it's the first entry, so it has no previous cache entry set
|
||||
|
||||
entry.Dispose();
|
||||
Assert.Null(GetScope(entry));
|
||||
ICacheEntry second = cache.CreateEntry("myKey2");
|
||||
Assert.NotNull(GetScope(second)); // it's not first, so it has previous set
|
||||
Assert.Same(first, GetScope(second)); // second.previous is set to first
|
||||
|
||||
second.Dispose();
|
||||
Assert.Null(GetScope(second));
|
||||
first.Dispose();
|
||||
Assert.Null(GetScope(first));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue