diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-05-31 18:32:23 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-05-31 18:32:23 -0400 |
commit | 9992915f565578949cad8d9bb8ceb360e0db5c85 (patch) | |
tree | 1ff34ba24733fcdf44f52fb8ee10b2a956f1a708 /src/SMAPI/Framework/Utilities | |
parent | 9ef3f7edb1589a52794c7da7075996d4a02de6e7 (diff) | |
download | SMAPI-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.cs | 57 |
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 + } + } +} |