From 43ed3ba0c88e7504f94b98a122f9223629f67bce Mon Sep 17 00:00:00 2001 From: Mark Cilia Vincenti Date: Sat, 28 Jun 2025 16:00:30 +0200 Subject: [PATCH 1/2] Speed optimization for .NET 9.0+ through System.Threading.Lock --- BitFaster.Caching/Atomic/AsyncAtomicFactory.cs | 3 ++- BitFaster.Caching/Atomic/AtomicFactory.cs | 3 ++- BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs | 3 ++- BitFaster.Caching/Atomic/ScopedAtomicFactory.cs | 5 +++-- BitFaster.Caching/BitFaster.Caching.csproj | 10 +++++++--- BitFaster.Caching/Lru/ClassicLru.cs | 11 ++++++----- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/BitFaster.Caching/Atomic/AsyncAtomicFactory.cs b/BitFaster.Caching/Atomic/AsyncAtomicFactory.cs index ff48ce40..eb1b9165 100644 --- a/BitFaster.Caching/Atomic/AsyncAtomicFactory.cs +++ b/BitFaster.Caching/Atomic/AsyncAtomicFactory.cs @@ -137,6 +137,7 @@ private async ValueTask CreateValueAsync(K key, TFactory valueFacto private class Initializer { + private readonly Lock locker = LockFactory.Create(); private bool isInitialized; private Task? valueTask; @@ -178,7 +179,7 @@ private Task DoubleCheck(Task value) return valueTask!; } - lock (this) + lock (locker) { if (!isInitialized) { diff --git a/BitFaster.Caching/Atomic/AtomicFactory.cs b/BitFaster.Caching/Atomic/AtomicFactory.cs index 8aaf8e3f..60876969 100644 --- a/BitFaster.Caching/Atomic/AtomicFactory.cs +++ b/BitFaster.Caching/Atomic/AtomicFactory.cs @@ -159,13 +159,14 @@ public override int GetHashCode() #pragma warning disable CA2002 // Do not lock on objects with weak identity private class Initializer { + private readonly Lock locker = LockFactory.Create(); private bool isInitialized; private V? value; private ExceptionDispatchInfo? exceptionDispatch; public V CreateValue(K key, TFactory valueFactory) where TFactory : struct, IValueFactory { - lock (this) + lock (locker) { if (isInitialized) { diff --git a/BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs b/BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs index 839b51a1..3a4acefc 100644 --- a/BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs +++ b/BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs @@ -147,6 +147,7 @@ public void Dispose() private class Initializer { + private readonly Lock locker = LockFactory.Create(); private bool isTaskInitialized; private bool isTaskCompleted; private bool isDisposeRequested; @@ -197,7 +198,7 @@ private Task> DoubleCheck(Task> value) return task!; } - lock (this) + lock (locker) { if (!isTaskInitialized) { diff --git a/BitFaster.Caching/Atomic/ScopedAtomicFactory.cs b/BitFaster.Caching/Atomic/ScopedAtomicFactory.cs index 55661e99..e7686f35 100644 --- a/BitFaster.Caching/Atomic/ScopedAtomicFactory.cs +++ b/BitFaster.Caching/Atomic/ScopedAtomicFactory.cs @@ -162,12 +162,13 @@ public void Dispose() #pragma warning disable CA2002 // Do not lock on objects with weak identity private class Initializer { + private readonly Lock locker = LockFactory.Create(); private bool isInitialized; private Scoped? value; public Scoped CreateScope(K key, TFactory valueFactory) where TFactory : struct, IValueFactory> { - lock (this) + lock (locker) { if (isInitialized) { @@ -183,7 +184,7 @@ public Scoped CreateScope(K key, TFactory valueFactory) where TFact public Scoped TryCreateDisposedScope() { - lock (this) + lock (locker) { if (isInitialized) { diff --git a/BitFaster.Caching/BitFaster.Caching.csproj b/BitFaster.Caching/BitFaster.Caching.csproj index 55f0e8dc..d6fe0021 100644 --- a/BitFaster.Caching/BitFaster.Caching.csproj +++ b/BitFaster.Caching/BitFaster.Caching.csproj @@ -1,8 +1,8 @@ - netstandard2.0;netcoreapp3.1;net6.0 - 11.0 + netstandard2.0;netcoreapp3.1;net6.0;net9.0 + latest Alex Peck BitFaster.Caching @@ -54,7 +54,11 @@ - + + + + + diff --git a/BitFaster.Caching/Lru/ClassicLru.cs b/BitFaster.Caching/Lru/ClassicLru.cs index b277c9ea..a7b07dd0 100644 --- a/BitFaster.Caching/Lru/ClassicLru.cs +++ b/BitFaster.Caching/Lru/ClassicLru.cs @@ -24,6 +24,7 @@ public sealed class ClassicLru : ICacheExt, IAsyncCacheExt, IB private readonly int capacity; private readonly ConcurrentDictionary> dictionary; private readonly LinkedList linkedList = new(); + private readonly Lock locker = LockFactory.Create(); private readonly CacheMetrics metrics = new(); private readonly CachePolicy policy; @@ -120,7 +121,7 @@ private bool TryAdd(K key, V value) { LinkedListNode? first = null; - lock (this.linkedList) + lock (this.locker) { if (linkedList.Count >= capacity) { @@ -303,7 +304,7 @@ private void OnRemove(LinkedListNode node) // the List & Dictionary. Now thread A will try to move x to the end of the list. if (node.List != null) { - lock (this.linkedList) + lock (this.locker) { if (node.List != null) { @@ -350,7 +351,7 @@ public void AddOrUpdate(K key, V value) { LinkedListNode? first = null; - lock (this.linkedList) + lock (this.locker) { if (linkedList.Count >= capacity) { @@ -410,7 +411,7 @@ public void Trim(int itemCount) { LinkedListNode? first = null; - lock (this.linkedList) + lock (this.locker) { if (linkedList.Count > 0) { @@ -440,7 +441,7 @@ private void LockAndMoveToEnd(LinkedListNode node) return; } - lock (this.linkedList) + lock (this.locker) { if (node.List == null) { From b5b0bb0583bb602df9a4cbb034697a9cb6fa18b7 Mon Sep 17 00:00:00 2001 From: Mark Cilia Vincenti Date: Sat, 28 Jun 2025 16:07:50 +0200 Subject: [PATCH 2/2] Use source generator --- BitFaster.Caching/BitFaster.Caching.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BitFaster.Caching/BitFaster.Caching.csproj b/BitFaster.Caching/BitFaster.Caching.csproj index d6fe0021..013998c9 100644 --- a/BitFaster.Caching/BitFaster.Caching.csproj +++ b/BitFaster.Caching/BitFaster.Caching.csproj @@ -54,7 +54,10 @@ - + + all + analyzers +