refactor Image Cache to single dictionary

This commit is contained in:
弘韬 张 2020-11-08 00:01:33 +08:00
parent d3d69be9c2
commit bf271b9da7
2 changed files with 37 additions and 37 deletions

View file

@ -11,61 +11,65 @@ namespace Flow.Launcher.Infrastructure.Image
public class ImageCache public class ImageCache
{ {
private const int MaxCached = 50; private const int MaxCached = 50;
public ConcurrentDictionary<string, int> Usage = new ConcurrentDictionary<string, int>(); public ConcurrentDictionary<string, (int usage, ImageSource imageSource)> Data { get; private set; } = new ConcurrentDictionary<string, (int, ImageSource)>();
private readonly ConcurrentDictionary<string, ImageSource> _data = new ConcurrentDictionary<string, ImageSource>();
private const int permissibleFactor = 2; private const int permissibleFactor = 2;
public void Initialization(Dictionary<string, int> usage)
{
foreach (var key in usage.Keys)
{
Data[key] = (usage[key], null);
}
}
public ImageSource this[string path] public ImageSource this[string path]
{ {
get get
{ {
Usage.AddOrUpdate(path, 1, (k, v) => v + 1); if (Data.TryGetValue(path, out var value))
var i = _data[path]; {
return i; value.usage++;
return value.imageSource;
}
else return null;
} }
set set
{ {
_data[path] = value; Data.AddOrUpdate(path, (1, value), (k, v) =>
{
v.imageSource = value;
v.usage++;
return v;
});
// To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size // To prevent the dictionary from drastically increasing in size by caching images, the dictionary size is not allowed to grow more than the permissibleFactor * maxCached size
// This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time // This is done so that we don't constantly perform this resizing operation and also maintain the image cache size at the same time
if (_data.Count > permissibleFactor * MaxCached) if (Data.Count > permissibleFactor * MaxCached)
{ {
// This function resizes the Usage dictionary, taking the top 'maxCached' number of items and filtering the image icons that are not accessed frequently.
Cleanup();
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary. // To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
foreach (var key in _data.Keys)
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
{ {
int dictValue; if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
if (!Usage.TryGetValue(key, out dictValue) && !(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
{ {
ImageSource imgSource; Data.TryRemove(key, out _);
_data.TryRemove(key, out imgSource);
} }
} }
} }
} }
} }
public void Cleanup()
{
var images = Usage
.OrderByDescending(o => o.Value)
.Take(MaxCached)
.ToDictionary(i => i.Key, i => i.Value);
Usage = new ConcurrentDictionary<string, int>(images);
}
public bool ContainsKey(string key) public bool ContainsKey(string key)
{ {
var contains = _data.ContainsKey(key); var contains = Data.ContainsKey(key);
return contains; return contains;
} }
public int CacheSize() public int CacheSize()
{ {
return _data.Count; return Data.Count;
} }
/// <summary> /// <summary>
@ -73,7 +77,7 @@ namespace Flow.Launcher.Infrastructure.Image
/// </summary> /// </summary>
public int UniqueImagesInCache() public int UniqueImagesInCache()
{ {
return _data.Values.Distinct().Count(); return Data.Values.Select(x => x.imageSource).Distinct().Count();
} }
} }

View file

@ -35,7 +35,7 @@ namespace Flow.Launcher.Infrastructure.Image
_storage = new BinaryStorage<Dictionary<string, int>>("Image"); _storage = new BinaryStorage<Dictionary<string, int>>("Image");
_hashGenerator = new ImageHashGenerator(); _hashGenerator = new ImageHashGenerator();
ImageCache.Usage = LoadStorageToConcurrentDictionary(); var usage = LoadStorageToConcurrentDictionary();
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
{ {
@ -48,12 +48,12 @@ namespace Flow.Launcher.Infrastructure.Image
{ {
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () => Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
{ {
ImageCache.Usage.AsParallel().ForAll(x => ImageCache.Data.AsParallel().ForAll(x =>
{ {
Load(x.Key); Load(x.Key);
}); });
}); });
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}"); Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
}); });
} }
@ -61,14 +61,13 @@ namespace Flow.Launcher.Infrastructure.Image
{ {
lock (_storage) lock (_storage)
{ {
ImageCache.Cleanup(); _storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
_storage.Save(new Dictionary<string, int>(ImageCache.Usage));
} }
} }
private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary() private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary()
{ {
lock(_storage) lock (_storage)
{ {
var loaded = _storage.TryLoad(new Dictionary<string, int>()); var loaded = _storage.TryLoad(new Dictionary<string, int>());
@ -222,13 +221,10 @@ namespace Flow.Launcher.Infrastructure.Image
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null; string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null) if (hash != null)
{ {
int ImageCacheValue;
if (GuidToKey.TryGetValue(hash, out string key)) if (GuidToKey.TryGetValue(hash, out string key))
{ // image already exists { // image already exists
if (ImageCache.Usage.TryGetValue(path, out ImageCacheValue)) img = ImageCache[key] ?? img;
{
img = ImageCache[key];
}
} }
else else
{ // new guid { // new guid