c1b7340023
* Timing: Optimize Timestamp Aquisition Currently, we make use of Environment.TickCount in a number of places. This has some downsides, mainly being that the TickCount is a signed 32-bit integer, and has an effective limit of ~25 days before overflowing and wrapping around. Due to the signed-ness of the value, this also caused issues with negative numbers. This resolves these issues by using a 64-bit tick count obtained from Performance Counters (via the Stopwatch class). This has a beneficial side effect of being significantly more accurate than the TickCount. * Timing: Rename ElapsedTicks to ElapsedMilliseconds and expose TicksPerX * Timing: Some style changes * Timing: Align static variable initialization
165 lines
4.5 KiB
C#
165 lines
4.5 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
|
|
namespace ChocolArm64
|
|
{
|
|
class ATranslatorCache
|
|
{
|
|
//Maximum size of the cache, in bytes, measured in ARM code size.
|
|
private const int MaxTotalSize = 4 * 1024 * 256;
|
|
|
|
//Minimum time required in milliseconds for a method to be eligible for deletion.
|
|
private const int MinTimeDelta = 2 * 60000;
|
|
|
|
//Minimum number of calls required to update the timestamp.
|
|
private const int MinCallCountForUpdate = 250;
|
|
|
|
private class CacheBucket
|
|
{
|
|
public ATranslatedSub Subroutine { get; private set; }
|
|
|
|
public LinkedListNode<long> Node { get; private set; }
|
|
|
|
public int CallCount { get; set; }
|
|
|
|
public int Size { get; private set; }
|
|
|
|
public long Timestamp { get; private set; }
|
|
|
|
public CacheBucket(ATranslatedSub Subroutine, LinkedListNode<long> Node, int Size)
|
|
{
|
|
this.Subroutine = Subroutine;
|
|
this.Size = Size;
|
|
|
|
UpdateNode(Node);
|
|
}
|
|
|
|
public void UpdateNode(LinkedListNode<long> Node)
|
|
{
|
|
this.Node = Node;
|
|
|
|
Timestamp = GetTimestamp();
|
|
}
|
|
}
|
|
|
|
private ConcurrentDictionary<long, CacheBucket> Cache;
|
|
|
|
private LinkedList<long> SortedCache;
|
|
|
|
private int TotalSize;
|
|
|
|
public ATranslatorCache()
|
|
{
|
|
Cache = new ConcurrentDictionary<long, CacheBucket>();
|
|
|
|
SortedCache = new LinkedList<long>();
|
|
}
|
|
|
|
public void AddOrUpdate(long Position, ATranslatedSub Subroutine, int Size)
|
|
{
|
|
ClearCacheIfNeeded();
|
|
|
|
TotalSize += Size;
|
|
|
|
lock (SortedCache)
|
|
{
|
|
LinkedListNode<long> Node = SortedCache.AddLast(Position);
|
|
|
|
CacheBucket NewBucket = new CacheBucket(Subroutine, Node, Size);
|
|
|
|
Cache.AddOrUpdate(Position, NewBucket, (Key, Bucket) =>
|
|
{
|
|
TotalSize -= Bucket.Size;
|
|
|
|
SortedCache.Remove(Bucket.Node);
|
|
|
|
return NewBucket;
|
|
});
|
|
}
|
|
}
|
|
|
|
public bool HasSubroutine(long Position)
|
|
{
|
|
return Cache.ContainsKey(Position);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool TryGetSubroutine(long Position, out ATranslatedSub Subroutine)
|
|
{
|
|
if (Cache.TryGetValue(Position, out CacheBucket Bucket))
|
|
{
|
|
if (Bucket.CallCount++ > MinCallCountForUpdate)
|
|
{
|
|
if (Monitor.TryEnter(SortedCache))
|
|
{
|
|
try
|
|
{
|
|
Bucket.CallCount = 0;
|
|
|
|
SortedCache.Remove(Bucket.Node);
|
|
|
|
Bucket.UpdateNode(SortedCache.AddLast(Position));
|
|
}
|
|
finally
|
|
{
|
|
Monitor.Exit(SortedCache);
|
|
}
|
|
}
|
|
}
|
|
|
|
Subroutine = Bucket.Subroutine;
|
|
|
|
return true;
|
|
}
|
|
|
|
Subroutine = default(ATranslatedSub);
|
|
|
|
return false;
|
|
}
|
|
|
|
private void ClearCacheIfNeeded()
|
|
{
|
|
long Timestamp = GetTimestamp();
|
|
|
|
while (TotalSize > MaxTotalSize)
|
|
{
|
|
lock (SortedCache)
|
|
{
|
|
LinkedListNode<long> Node = SortedCache.First;
|
|
|
|
if (Node == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
CacheBucket Bucket = Cache[Node.Value];
|
|
|
|
long TimeDelta = Bucket.Timestamp - Timestamp;
|
|
|
|
if (TimeDelta <= MinTimeDelta)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Cache.TryRemove(Node.Value, out Bucket))
|
|
{
|
|
TotalSize -= Bucket.Size;
|
|
|
|
SortedCache.Remove(Bucket.Node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static long GetTimestamp()
|
|
{
|
|
long timestamp = Stopwatch.GetTimestamp();
|
|
|
|
return timestamp / (Stopwatch.Frequency / 1000);
|
|
}
|
|
}
|
|
} |