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
{
private const int MaxCached = 50;
public ConcurrentDictionary<string, int> Usage = new ConcurrentDictionary<string, int>();
private readonly ConcurrentDictionary<string, ImageSource> _data = new ConcurrentDictionary<string, ImageSource>();
public ConcurrentDictionary<string, (int usage, ImageSource imageSource)> Data { get; private set; } = new ConcurrentDictionary<string, (int, ImageSource)>();
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]
{
get
{
Usage.AddOrUpdate(path, 1, (k, v) => v + 1);
var i = _data[path];
return i;
if (Data.TryGetValue(path, out var value))
{
value.usage++;
return value.imageSource;
}
else return null;
}
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
// 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.
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 (!Usage.TryGetValue(key, out dictValue) && !(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
{
ImageSource imgSource;
_data.TryRemove(key, out imgSource);
Data.TryRemove(key, out _);
}
}
}
}
}
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)
{
var contains = _data.ContainsKey(key);
var contains = Data.ContainsKey(key);
return contains;
}
public int CacheSize()
{
return _data.Count;
return Data.Count;
}
/// <summary>
@ -73,7 +77,7 @@ namespace Flow.Launcher.Infrastructure.Image
/// </summary>
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");
_hashGenerator = new ImageHashGenerator();
ImageCache.Usage = LoadStorageToConcurrentDictionary();
var usage = LoadStorageToConcurrentDictionary();
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", () =>
{
ImageCache.Usage.AsParallel().ForAll(x =>
ImageCache.Data.AsParallel().ForAll(x =>
{
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)
{
ImageCache.Cleanup();
_storage.Save(new Dictionary<string, int>(ImageCache.Usage));
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
}
}
private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary()
{
lock(_storage)
lock (_storage)
{
var loaded = _storage.TryLoad(new Dictionary<string, int>());
@ -222,13 +221,10 @@ namespace Flow.Launcher.Infrastructure.Image
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null)
{
int ImageCacheValue;
if (GuidToKey.TryGetValue(hash, out string key))
{ // image already exists
if (ImageCache.Usage.TryGetValue(path, out ImageCacheValue))
{
img = ImageCache[key];
}
img = ImageCache[key] ?? img;
}
else
{ // new guid