TranslatorCache.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System.Collections.Concurrent;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Runtime.CompilerServices;
  5. using System.Threading;
  6. namespace ChocolArm64
  7. {
  8. class TranslatorCache
  9. {
  10. //Maximum size of the cache, the unit used is completely arbitrary.
  11. private const int MaxTotalSize = 0x800000;
  12. //Minimum time required in milliseconds for a method to be eligible for deletion.
  13. private const int MinTimeDelta = 2 * 60000;
  14. //Minimum number of calls required to update the timestamp.
  15. private const int MinCallCountForUpdate = 250;
  16. private class CacheBucket
  17. {
  18. public TranslatedSub Subroutine { get; private set; }
  19. public LinkedListNode<long> Node { get; private set; }
  20. public int CallCount { get; set; }
  21. public int Size { get; private set; }
  22. public long Timestamp { get; private set; }
  23. public CacheBucket(TranslatedSub subroutine, LinkedListNode<long> node, int size)
  24. {
  25. Subroutine = subroutine;
  26. Size = size;
  27. UpdateNode(node);
  28. }
  29. public void UpdateNode(LinkedListNode<long> node)
  30. {
  31. Node = node;
  32. Timestamp = GetTimestamp();
  33. }
  34. }
  35. private ConcurrentDictionary<long, CacheBucket> _cache;
  36. private LinkedList<long> _sortedCache;
  37. private int _totalSize;
  38. public TranslatorCache()
  39. {
  40. _cache = new ConcurrentDictionary<long, CacheBucket>();
  41. _sortedCache = new LinkedList<long>();
  42. }
  43. public void AddOrUpdate(long position, TranslatedSub subroutine, int size)
  44. {
  45. ClearCacheIfNeeded();
  46. lock (_sortedCache)
  47. {
  48. _totalSize += size;
  49. LinkedListNode<long> node = _sortedCache.AddLast(position);
  50. CacheBucket newBucket = new CacheBucket(subroutine, node, size);
  51. _cache.AddOrUpdate(position, newBucket, (key, bucket) =>
  52. {
  53. _totalSize -= bucket.Size;
  54. _sortedCache.Remove(bucket.Node);
  55. return newBucket;
  56. });
  57. }
  58. }
  59. public bool HasSubroutine(long position)
  60. {
  61. return _cache.ContainsKey(position);
  62. }
  63. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  64. public bool TryGetSubroutine(long position, out TranslatedSub subroutine)
  65. {
  66. if (_cache.TryGetValue(position, out CacheBucket bucket))
  67. {
  68. if (bucket.CallCount++ > MinCallCountForUpdate)
  69. {
  70. if (Monitor.TryEnter(_sortedCache))
  71. {
  72. try
  73. {
  74. //The bucket value on the dictionary may have changed between the
  75. //time we get the value from the dictionary, and we acquire the
  76. //lock. So we need to ensure we are working with the latest value,
  77. //we can do that by getting the value again, inside the lock.
  78. if (_cache.TryGetValue(position, out CacheBucket latestBucket))
  79. {
  80. latestBucket.CallCount = 0;
  81. _sortedCache.Remove(latestBucket.Node);
  82. latestBucket.UpdateNode(_sortedCache.AddLast(position));
  83. }
  84. }
  85. finally
  86. {
  87. Monitor.Exit(_sortedCache);
  88. }
  89. }
  90. }
  91. subroutine = bucket.Subroutine;
  92. return true;
  93. }
  94. subroutine = default(TranslatedSub);
  95. return false;
  96. }
  97. private void ClearCacheIfNeeded()
  98. {
  99. long timestamp = GetTimestamp();
  100. while (_totalSize > MaxTotalSize)
  101. {
  102. lock (_sortedCache)
  103. {
  104. LinkedListNode<long> node = _sortedCache.First;
  105. if (node == null)
  106. {
  107. break;
  108. }
  109. CacheBucket bucket = _cache[node.Value];
  110. long timeDelta = timestamp - bucket.Timestamp;
  111. if (timeDelta <= MinTimeDelta)
  112. {
  113. break;
  114. }
  115. if (_cache.TryRemove(node.Value, out bucket))
  116. {
  117. _totalSize -= bucket.Size;
  118. _sortedCache.Remove(bucket.Node);
  119. }
  120. }
  121. }
  122. }
  123. private static long GetTimestamp()
  124. {
  125. long timestamp = Stopwatch.GetTimestamp();
  126. return timestamp / (Stopwatch.Frequency / 1000);
  127. }
  128. }
  129. }