Add basic base class to begin to standardise function across caching components

This commit is contained in:
Dean Herbert 2020-11-06 13:26:18 +09:00
parent 14bb079feb
commit 0103b12575
4 changed files with 28 additions and 19 deletions

View File

@ -11,11 +11,11 @@
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Framework.Utils;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
@ -27,14 +27,11 @@ namespace osu.Game.Beatmaps
/// A component which performs and acts as a central cache for difficulty calculations of beatmap/ruleset/mod combinations.
/// Currently not persisted between game sessions.
/// </summary>
public class BeatmapDifficultyCache : Component
public class BeatmapDifficultyCache : MemoryCachingComponent<BeatmapDifficultyCache.DifficultyCacheLookup, StarDifficulty>
{
// Too many simultaneous updates can lead to stutters. One thread seems to work fine for song select display purposes.
private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyCache));
// A permanent cache to prevent re-computations.
private readonly ConcurrentDictionary<DifficultyCacheLookup, StarDifficulty> difficultyCache = new ConcurrentDictionary<DifficultyCacheLookup, StarDifficulty>();
// All bindables that should be updated along with the current ruleset + mods.
private readonly LockedWeakList<BindableStarDifficulty> trackedBindables = new LockedWeakList<BindableStarDifficulty>();
@ -243,7 +240,7 @@ private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, BeatmapIn
var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(beatmapInfo));
var attributes = calculator.Calculate(key.Mods);
return difficultyCache[key] = new StarDifficulty(attributes);
return Cache[key] = new StarDifficulty(attributes);
}
catch (BeatmapInvalidForRulesetException e)
{
@ -254,7 +251,7 @@ private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, BeatmapIn
if (rulesetInfo.Equals(beatmapInfo.Ruleset))
{
Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
return difficultyCache[key] = new StarDifficulty();
return Cache[key] = new StarDifficulty();
}
// Check the cache first because this is now a different ruleset than the one previously guarded against.
@ -265,7 +262,7 @@ private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, BeatmapIn
}
catch
{
return difficultyCache[key] = new StarDifficulty();
return Cache[key] = new StarDifficulty();
}
}
@ -294,7 +291,7 @@ private bool tryGetExisting(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo, IE
}
key = new DifficultyCacheLookup(beatmapInfo.ID, rulesetInfo.ID.Value, mods);
return difficultyCache.TryGetValue(key, out existingDifficulty);
return Cache.TryGetValue(key, out existingDifficulty);
}
protected override void Dispose(bool isDisposing)

View File

@ -0,0 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Concurrent;
using osu.Framework.Graphics;
namespace osu.Game.Database
{
/// <summary>
/// A component which performs lookups (or calculations) and caches the results.
/// Currently not persisted between game sessions.
/// </summary>
public abstract class MemoryCachingComponent<TLookup, TValue> : Component
{
protected readonly ConcurrentDictionary<TLookup, TValue> Cache = new ConcurrentDictionary<TLookup, TValue>();
}
}

View File

@ -2,13 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Database;
namespace osu.Game.Scoring
{
@ -16,12 +15,8 @@ namespace osu.Game.Scoring
/// A component which performs and acts as a central cache for performance calculations of locally databased scores.
/// Currently not persisted between game sessions.
/// </summary>
public class ScorePerformanceCache : Component
public class ScorePerformanceCache : MemoryCachingComponent<ScorePerformanceCache.PerformanceCacheLookup, double>
{
// this cache will grow indefinitely per session and should be considered temporary.
// this whole component should likely be replaced with database persistence.
private readonly ConcurrentDictionary<PerformanceCacheLookup, double> performanceCache = new ConcurrentDictionary<PerformanceCacheLookup, double>();
[Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; }
@ -34,7 +29,7 @@ public class ScorePerformanceCache : Component
{
var lookupKey = new PerformanceCacheLookup(score);
if (performanceCache.TryGetValue(lookupKey, out double performance))
if (Cache.TryGetValue(lookupKey, out double performance))
return Task.FromResult((double?)performance);
return computePerformanceAsync(score, lookupKey, token);
@ -54,7 +49,7 @@ public class ScorePerformanceCache : Component
var total = calculator?.Calculate();
if (total.HasValue)
performanceCache[lookupKey] = total.Value;
Cache[lookupKey] = total.Value;
return total;
}

View File

@ -37,7 +37,7 @@ private void load(ScorePerformanceCache performanceCache)
else
{
performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token)
.ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token);
.ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token);
}
}