Optimize TranslationMapping by using short, array and pre-allocated list

Saving about 60% memory (measured about 500K in production, depending on queries)
This commit is contained in:
VictoriousRaptor 2026-01-03 13:44:21 +08:00
parent 83883c4b05
commit 6f5a620495
3 changed files with 33 additions and 12 deletions

View file

@ -119,7 +119,7 @@ namespace Flow.Launcher.Infrastructure
{
var resultList = WordsHelper.GetPinyinList(content);
var resultBuilder = new StringBuilder(_settings.UseDoublePinyin ? 3 : 4); // Pre-allocate with estimated capacity
var map = new TranslationMapping();
var map = new TranslationMapping(resultList.Length);
var previousIsChinese = false;
@ -157,10 +157,7 @@ namespace Flow.Launcher.Infrastructure
map.EndConstruct();
var translation = resultBuilder.ToString();
var result = (translation, map);
return _pinyinCache[content] = result;
return _pinyinCache[content] = (resultBuilder.ToString(), map);
}
/// <summary>

View file

@ -9,18 +9,38 @@ namespace Flow.Launcher.Infrastructure
// Assuming one original item maps to multi translated items
// list[i] is the last translated index + 1 of original index i
private readonly List<int> _originalToTranslated = new();
// Using short instead of int to save memory
private List<short> _originalToTranslatedBuilder;
private short[] _originalToTranslated;
public TranslationMapping(int capacityHint = 16)
{
_originalToTranslatedBuilder = new List<short>(capacityHint);
}
public void AddNewIndex(int translatedIndex, int length)
{
if (_isConstructed)
throw new InvalidOperationException("Mapping shouldn't be changed after construction");
_originalToTranslated.Add(translatedIndex + length);
var value = translatedIndex + length;
if (value > short.MaxValue)
throw new ArgumentOutOfRangeException(nameof(translatedIndex),
"Translation index exceeds maximum supported value (32,767)");
_originalToTranslatedBuilder.Add((short)value);
}
public int MapToOriginalIndex(int translatedIndex)
{
var searchResult = _originalToTranslated.BinarySearch(translatedIndex);
if (_originalToTranslated == null)
throw new InvalidOperationException("Mapping must be constructed before use");
if (translatedIndex > short.MaxValue)
throw new ArgumentOutOfRangeException(nameof(translatedIndex),
"Translation index exceeds maximum supported value (32,767)");
var searchResult = Array.BinarySearch(_originalToTranslated, (short)translatedIndex);
return searchResult >= 0 ? searchResult + 1 : ~searchResult;
}
@ -28,6 +48,10 @@ namespace Flow.Launcher.Infrastructure
{
if (_isConstructed)
throw new InvalidOperationException("Mapping has already been constructed");
// Convert to array to save memory (no List overhead, no excess capacity)
_originalToTranslated = _originalToTranslatedBuilder.ToArray();
_originalToTranslatedBuilder = null; // Allow GC to collect the List
_isConstructed = true;
}
}

View file

@ -57,15 +57,15 @@ namespace Flow.Launcher.Test
private static int GetOriginalToTranslatedCount(TranslationMapping mapping)
{
var field = typeof(TranslationMapping).GetField("_originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance);
var list = (List<int>)field.GetValue(mapping);
return list.Count;
var array = (short[])field.GetValue(mapping);
return array.Length;
}
private static int GetOriginalToTranslatedAt(TranslationMapping mapping, int index)
{
var field = typeof(TranslationMapping).GetField("_originalToTranslated", BindingFlags.NonPublic | BindingFlags.Instance);
var list = (List<int>)field.GetValue(mapping);
return list[index];
var array = (short[])field.GetValue(mapping);
return array[index];
}
}
}