summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/Utilities
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-31 18:32:23 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-05-31 18:32:23 -0400
commit9992915f565578949cad8d9bb8ceb360e0db5c85 (patch)
tree1ff34ba24733fcdf44f52fb8ee10b2a956f1a708 /src/SMAPI/Framework/Utilities
parent9ef3f7edb1589a52794c7da7075996d4a02de6e7 (diff)
downloadSMAPI-9992915f565578949cad8d9bb8ceb360e0db5c85.tar.gz
SMAPI-9992915f565578949cad8d9bb8ceb360e0db5c85.tar.bz2
SMAPI-9992915f565578949cad8d9bb8ceb360e0db5c85.zip
replace MemoryCache with custom cache
This was causing significant frame stutters for some players since the migration to .NET 5 in Stardew Valley 1.5.5.
Diffstat (limited to 'src/SMAPI/Framework/Utilities')
-rw-r--r--src/SMAPI/Framework/Utilities/IntervalMemoryCache.cs57
1 files changed, 57 insertions, 0 deletions
diff --git a/src/SMAPI/Framework/Utilities/IntervalMemoryCache.cs b/src/SMAPI/Framework/Utilities/IntervalMemoryCache.cs
new file mode 100644
index 00000000..d2b69f51
--- /dev/null
+++ b/src/SMAPI/Framework/Utilities/IntervalMemoryCache.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+
+namespace StardewModdingAPI.Framework.Utilities
+{
+ /// <summary>A memory cache with sliding expiry based on custom intervals, with no background processing.</summary>
+ /// <typeparam name="TKey">The cache key type.</typeparam>
+ /// <typeparam name="TValue">The cache value type.</typeparam>
+ /// <remarks>This is optimized for small caches that are reset relatively rarely. Each cache entry is marked as hot (accessed since the interval started) or stale.
+ /// When a new interval is started, stale entries are cleared and hot entries become stale.</remarks>
+ internal class IntervalMemoryCache<TKey, TValue>
+ where TKey : notnull
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The cached values that were accessed during the current interval.</summary>
+ private Dictionary<TKey, TValue> HotCache = new();
+
+ /// <summary>The cached values that will expire on the next interval.</summary>
+ private Dictionary<TKey, TValue> StaleCache = new();
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Get a value from the cache, fetching it first if needed.</summary>
+ /// <param name="cacheKey">The unique key for the cached value.</param>
+ /// <param name="get">Get the latest data if it's not in the cache yet.</param>
+ public TValue GetOrSet(TKey cacheKey, Func<TValue> get)
+ {
+ // from hot cache
+ if (this.HotCache.TryGetValue(cacheKey, out TValue? value))
+ return value;
+
+ // from stale cache
+ if (this.StaleCache.TryGetValue(cacheKey, out value))
+ {
+ this.HotCache[cacheKey] = value;
+ return value;
+ }
+
+ // new value
+ value = get();
+ this.HotCache[cacheKey] = value;
+ return value;
+ }
+
+ /// <summary>Start a new cache interval, removing any stale entries.</summary>
+ public void StartNewInterval()
+ {
+ this.StaleCache.Clear();
+ if (this.HotCache.Count is not 0)
+ (this.StaleCache, this.HotCache) = (this.HotCache, this.StaleCache); // swap hot cache to stale
+ }
+ }
+}