Merge pull request #2740 from Flow-Launcher/new-image-cache

Change Caching Policy
This commit is contained in:
DB P 2024-05-30 15:04:53 +09:00 committed by GitHub
commit fe7c3e47a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 52 deletions

View file

@ -49,7 +49,7 @@
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="FastCache.Cached" Version="1.8.2" />
<PackageReference Include="BitFaster.Caching" Version="2.5.0" />
<PackageReference Include="Fody" Version="6.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View file

@ -3,33 +3,23 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using FastCache;
using FastCache.Services;
using BitFaster.Caching.Lfu;
namespace Flow.Launcher.Infrastructure.Image
{
public class ImageUsage
{
public int usage;
public ImageSource imageSource;
public ImageUsage(int usage, ImageSource image)
{
this.usage = usage;
imageSource = image;
}
}
public class ImageCache
{
private const int MaxCached = 150;
public void Initialize(Dictionary<(string, bool), int> usage)
private ConcurrentLfu<(string, bool), ImageSource> CacheManager { get; set; } = new(MaxCached);
public void Initialize(IEnumerable<(string, bool)> usage)
{
foreach (var key in usage.Keys)
foreach (var key in usage)
{
Cached<ImageUsage>.Save(key, new ImageUsage(usage[key], null), TimeSpan.MaxValue, MaxCached);
CacheManager.AddOrUpdate(key, null);
}
}
@ -37,48 +27,42 @@ namespace Flow.Launcher.Infrastructure.Image
{
get
{
if (!Cached<ImageUsage>.TryGet((path, isFullImage), out var value))
{
return null;
}
value.Value.usage++;
return value.Value.imageSource;
return CacheManager.TryGet((path, isFullImage), out var value) ? value : null;
}
set
{
if (Cached<ImageUsage>.TryGet((path, isFullImage), out var cached))
{
cached.Value.imageSource = value;
cached.Value.usage++;
}
Cached<ImageUsage>.Save((path, isFullImage), new ImageUsage(0, value), TimeSpan.MaxValue,
MaxCached);
CacheManager.AddOrUpdate((path, isFullImage), value);
}
}
public async ValueTask<ImageSource> GetOrAddAsync(string key,
Func<(string, bool), Task<ImageSource>> valueFactory,
bool isFullImage = false)
{
return await CacheManager.GetOrAddAsync((key, isFullImage), valueFactory);
}
public bool ContainsKey(string key, bool isFullImage)
{
return Cached<ImageUsage>.TryGet((key, isFullImage), out _);
return CacheManager.TryGet((key, isFullImage), out _);
}
public bool TryGetValue(string key, bool isFullImage, out ImageSource image)
{
if (Cached<ImageUsage>.TryGet((key, isFullImage), out var value))
if (CacheManager.TryGet((key, isFullImage), out var value))
{
image = value.Value.imageSource;
value.Value.usage++;
image = value;
return image != null;
}
image = null;
return false;
}
public int CacheSize()
{
return CacheManager.TotalCount<(string, bool), ImageUsage>();
return CacheManager.Count;
}
/// <summary>
@ -86,14 +70,14 @@ namespace Flow.Launcher.Infrastructure.Image
/// </summary>
public int UniqueImagesInCache()
{
return CacheManager.EnumerateEntries<(string, bool), ImageUsage>().Select(x => x.Value.imageSource)
return CacheManager.Select(x => x.Value)
.Distinct()
.Count();
}
public IEnumerable<Cached<(string, bool), ImageUsage>> EnumerateEntries()
public IEnumerable<KeyValuePair<(string, bool), ImageSource>> EnumerateEntries()
{
return CacheManager.EnumerateEntries<(string, bool), ImageUsage>();
return CacheManager;
}
}
}

View file

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Flow.Launcher.Infrastructure.Logger;
@ -17,7 +18,7 @@ namespace Flow.Launcher.Infrastructure.Image
{
private static readonly ImageCache ImageCache = new();
private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1);
private static BinaryStorage<Dictionary<(string, bool), int>> _storage;
private static BinaryStorage<List<(string, bool)>> _storage;
private static readonly ConcurrentDictionary<string, string> GuidToKey = new();
private static IImageHashGenerator _hashGenerator;
private static readonly bool EnableImageHash = true;
@ -31,12 +32,12 @@ namespace Flow.Launcher.Infrastructure.Image
public static async Task InitializeAsync()
{
_storage = new BinaryStorage<Dictionary<(string, bool), int>>("Image");
_storage = new BinaryStorage<List<(string, bool)>>("Image");
_hashGenerator = new ImageHashGenerator();
var usage = await LoadStorageToConcurrentDictionaryAsync();
ImageCache.Initialize(usage.ToDictionary(x => x.Key, x => x.Value));
ImageCache.Initialize(usage);
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
{
@ -49,7 +50,7 @@ namespace Flow.Launcher.Infrastructure.Image
{
await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () =>
{
foreach (var ((path, isFullImage), _) in usage)
foreach (var (path, isFullImage) in usage)
{
await LoadAsync(path, isFullImage);
}
@ -66,9 +67,8 @@ namespace Flow.Launcher.Infrastructure.Image
try
{
await _storage.SaveAsync(ImageCache.EnumerateEntries()
.ToDictionary(
x => x.Key,
x => x.Value.usage));
.Select(x => x.Key)
.ToList());
}
finally
{
@ -76,14 +76,12 @@ namespace Flow.Launcher.Infrastructure.Image
}
}
private static async Task<ConcurrentDictionary<(string, bool), int>> LoadStorageToConcurrentDictionaryAsync()
private static async Task<List<(string, bool)>> LoadStorageToConcurrentDictionaryAsync()
{
await storageLock.WaitAsync();
try
{
var loaded = await _storage.TryLoadAsync(new Dictionary<(string, bool), int>());
return new ConcurrentDictionary<(string, bool), int>(loaded);
return await _storage.TryLoadAsync(new List<(string, bool)>());
}
finally
{