diff --git a/ARMeilleure/Translation/PTC/DegreeOfParallelism.cs b/ARMeilleure/Translation/PTC/DegreeOfParallelism.cs new file mode 100644 index 00000000..e4752c5e --- /dev/null +++ b/ARMeilleure/Translation/PTC/DegreeOfParallelism.cs @@ -0,0 +1,50 @@ +using System; + +namespace ARMeilleure.Translation.PTC +{ + class DegreeOfParallelism + { + public double GiBRef { get; } // GiB. + public double WeightRef { get; } // %. + public double IncrementByGiB { get; } // %. + private double _coefficient; + + public DegreeOfParallelism(double gibRef, double weightRef, double incrementByGiB) + { + GiBRef = gibRef; + WeightRef = weightRef; + IncrementByGiB = incrementByGiB; + + _coefficient = weightRef - (incrementByGiB * gibRef); + } + + public int GetDegreeOfParallelism(int min, int max) + { + double degreeOfParallelism = (GetProcessorCount() * GetWeight(GetAvailableMemoryGiB())) / 100d; + + return Math.Clamp((int)Math.Round(degreeOfParallelism), min, max); + } + + public static double GetProcessorCount() + { + return (double)Environment.ProcessorCount; + } + + public double GetWeight(double gib) + { + return (IncrementByGiB * gib) + _coefficient; + } + + public static double GetAvailableMemoryGiB() + { + GCMemoryInfo gcMemoryInfo = GC.GetGCMemoryInfo(); + + return FromBytesToGiB(gcMemoryInfo.TotalAvailableMemoryBytes - gcMemoryInfo.MemoryLoadBytes); + } + + private static double FromBytesToGiB(long bytes) + { + return Math.ScaleB((double)bytes, -30); + } + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/EncodingCache.cs b/ARMeilleure/Translation/PTC/EncodingCache.cs index b87e0d7a..90d40c47 100644 --- a/ARMeilleure/Translation/PTC/EncodingCache.cs +++ b/ARMeilleure/Translation/PTC/EncodingCache.cs @@ -2,8 +2,8 @@ using System.Text; namespace ARMeilleure.Translation.PTC { - internal static class EncodingCache + static class EncodingCache { - internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + public static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); } } \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 6e1a73f6..32e0e7e8 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -24,9 +24,10 @@ namespace ARMeilleure.Translation.PTC { public static class Ptc { - private const string HeaderMagicString = "PTChd\0\0\0"; + private const string OuterHeaderMagicString = "PTCohd\0\0"; + private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 2155; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 2169; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -41,14 +42,16 @@ namespace ARMeilleure.Translation.PTC private const byte FillingByte = 0x00; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; + // Carriers. private static MemoryStream _infosStream; - private static MemoryStream _codesStream; + private static List _codesList; private static MemoryStream _relocsStream; private static MemoryStream _unwindInfosStream; private static BinaryWriter _infosWriter; - private static readonly ulong _headerMagic; + private static readonly ulong _outerHeaderMagic; + private static readonly ulong _innerHeaderMagic; private static readonly ManualResetEvent _waitEvent; @@ -66,16 +69,17 @@ namespace ARMeilleure.Translation.PTC internal static PtcState State { get; private set; } - // Progress reporting helpers + // Progress reporting helpers. private static volatile int _translateCount; private static volatile int _translateTotalCount; public static event Action PtcStateChanged; static Ptc() { - InitializeMemoryStreams(); + InitializeCarriers(); - _headerMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(HeaderMagicString).AsSpan()); + _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); + _innerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(InnerHeaderMagicString).AsSpan()); _waitEvent = new ManualResetEvent(true); @@ -141,41 +145,41 @@ namespace ARMeilleure.Translation.PTC Enable(); } - private static void InitializeMemoryStreams() + private static void InitializeCarriers() { _infosStream = new MemoryStream(); - _codesStream = new MemoryStream(); + _codesList = new List(); _relocsStream = new MemoryStream(); _unwindInfosStream = new MemoryStream(); _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true); } - private static void DisposeMemoryStreams() + private static void DisposeCarriers() { _infosWriter.Dispose(); _infosStream.Dispose(); - _codesStream.Dispose(); + _codesList.Clear(); _relocsStream.Dispose(); _unwindInfosStream.Dispose(); } - private static bool AreMemoryStreamsEmpty() + private static bool AreCarriersEmpty() { - return _infosStream.Length == 0L && _codesStream.Length == 0L && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L; + return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L; } - private static void ResetMemoryStreamsIfNeeded() + private static void ResetCarriersIfNeeded() { - if (AreMemoryStreamsEmpty()) + if (AreCarriersEmpty()) { return; } - DisposeMemoryStreams(); + DisposeCarriers(); - InitializeMemoryStreams(); + InitializeCarriers(); } private static void PreLoad() @@ -207,28 +211,57 @@ namespace ARMeilleure.Translation.PTC using (FileStream compressedStream = new(fileName, FileMode.Open)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) { - Hash128 currentSizeHash = DeserializeStructure(compressedStream); + OuterHeader outerHeader = DeserializeStructure(compressedStream); - Span sizeBytes = new byte[sizeof(int)]; - compressedStream.Read(sizeBytes); - Hash128 expectedSizeHash = XXHash128.ComputeHash(sizeBytes); - - if (currentSizeHash != expectedSizeHash) + if (!outerHeader.IsHeaderValid()) { InvalidateCompressedStream(compressedStream); return false; } - int size = BinaryPrimitives.ReadInt32LittleEndian(sizeBytes); + if (outerHeader.Magic != _outerHeaderMagic) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (outerHeader.CacheFileVersion != InternalVersion) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (outerHeader.Endianness != GetEndianness()) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (outerHeader.FeatureInfo != GetFeatureInfo()) + { + InvalidateCompressedStream(compressedStream); + + return false; + } + + if (outerHeader.OSPlatform != GetOSPlatform()) + { + InvalidateCompressedStream(compressedStream); + + return false; + } IntPtr intPtr = IntPtr.Zero; try { - intPtr = Marshal.AllocHGlobal(size); + intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize)); - using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite)) + using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite)) { try { @@ -241,96 +274,104 @@ namespace ARMeilleure.Translation.PTC return false; } - int hashSize = Unsafe.SizeOf(); + Debug.Assert(stream.Position == stream.Length); stream.Seek(0L, SeekOrigin.Begin); - Hash128 currentHash = DeserializeStructure(stream); - ReadOnlySpan streamBytes = new(stream.PositionPointer, (int)(stream.Length - stream.Position)); - Hash128 expectedHash = XXHash128.ComputeHash(streamBytes); + InnerHeader innerHeader = DeserializeStructure(stream); - if (currentHash != expectedHash) + if (!innerHeader.IsHeaderValid()) { InvalidateCompressedStream(compressedStream); return false; } - stream.Seek((long)hashSize, SeekOrigin.Begin); - - Header header = ReadHeader(stream); - - if (header.Magic != _headerMagic) + if (innerHeader.Magic != _innerHeaderMagic) { InvalidateCompressedStream(compressedStream); return false; } - if (header.CacheFileVersion != InternalVersion) + ReadOnlySpan infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); + stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); + + Hash128 infosHash = XXHash128.ComputeHash(infosBytes); + + if (innerHeader.InfosHash != infosHash) { InvalidateCompressedStream(compressedStream); return false; } - if (header.Endianness != GetEndianness()) + ReadOnlySpan codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan.Empty; + stream.Seek(innerHeader.CodesLength, SeekOrigin.Current); + + Hash128 codesHash = XXHash128.ComputeHash(codesBytes); + + if (innerHeader.CodesHash != codesHash) { InvalidateCompressedStream(compressedStream); return false; } - if (header.FeatureInfo != GetFeatureInfo()) + ReadOnlySpan relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); + stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); + + Hash128 relocsHash = XXHash128.ComputeHash(relocsBytes); + + if (innerHeader.RelocsHash != relocsHash) { InvalidateCompressedStream(compressedStream); return false; } - if (header.OSPlatform != GetOSPlatform()) + ReadOnlySpan unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); + stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); + + Hash128 unwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); + + if (innerHeader.UnwindInfosHash != unwindInfosHash) { InvalidateCompressedStream(compressedStream); return false; } - if (header.InfosLen % InfoEntry.Stride != 0) + ReadOnlySpan ptcJumpTableBytes = new(stream.PositionPointer, innerHeader.PtcJumpTableLength); + stream.Seek(innerHeader.PtcJumpTableLength, SeekOrigin.Current); + + Debug.Assert(stream.Position == stream.Length); + + Hash128 ptcJumpTableHash = XXHash128.ComputeHash(ptcJumpTableBytes); + + if (innerHeader.PtcJumpTableHash != ptcJumpTableHash) { InvalidateCompressedStream(compressedStream); return false; } - ReadOnlySpan infosBuf = new(stream.PositionPointer, header.InfosLen); - stream.Seek(header.InfosLen, SeekOrigin.Current); + stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); - ReadOnlySpan codesBuf = new(stream.PositionPointer, header.CodesLen); - stream.Seek(header.CodesLen, SeekOrigin.Current); + _infosStream.Write(infosBytes); + stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); - ReadOnlySpan relocsBuf = new(stream.PositionPointer, header.RelocsLen); - stream.Seek(header.RelocsLen, SeekOrigin.Current); + _codesList.ReadFrom(stream); - ReadOnlySpan unwindInfosBuf = new(stream.PositionPointer, header.UnwindInfosLen); - stream.Seek(header.UnwindInfosLen, SeekOrigin.Current); + _relocsStream.Write(relocsBytes); + stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); - try - { - PtcJumpTable = PtcJumpTable.Deserialize(stream); - } - catch - { - PtcJumpTable = new PtcJumpTable(); + _unwindInfosStream.Write(unwindInfosBytes); + stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); - InvalidateCompressedStream(compressedStream); + PtcJumpTable = PtcJumpTable.Deserialize(stream); - return false; - } - - _infosStream.Write(infosBuf); - _codesStream.Write(codesBuf); - _relocsStream.Write(relocsBuf); - _unwindInfosStream.Write(unwindInfosBuf); + Debug.Assert(stream.Position == stream.Length); } } finally @@ -344,33 +385,11 @@ namespace ARMeilleure.Translation.PTC long fileSize = new FileInfo(fileName).Length; - Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()})."); + Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetEntriesCount()})."); return true; } - private static Header ReadHeader(Stream stream) - { - using (BinaryReader headerReader = new(stream, EncodingCache.UTF8NoBOM, true)) - { - Header header = new Header(); - - header.Magic = headerReader.ReadUInt64(); - - header.CacheFileVersion = headerReader.ReadUInt32(); - header.Endianness = headerReader.ReadBoolean(); - header.FeatureInfo = headerReader.ReadUInt64(); - header.OSPlatform = headerReader.ReadUInt32(); - - header.InfosLen = headerReader.ReadInt32(); - header.CodesLen = headerReader.ReadInt32(); - header.RelocsLen = headerReader.ReadInt32(); - header.UnwindInfosLen = headerReader.ReadInt32(); - - return header; - } - } - private static void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); @@ -396,7 +415,7 @@ namespace ARMeilleure.Translation.PTC } finally { - ResetMemoryStreamsIfNeeded(); + ResetCarriersIfNeeded(); PtcJumpTable.ClearIfNeeded(); GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; @@ -409,46 +428,76 @@ namespace ARMeilleure.Translation.PTC { int translatedFuncsCount; - int hashSize = Unsafe.SizeOf(); + InnerHeader innerHeader = new InnerHeader(); - int size = hashSize + Header.Size + GetMemoryStreamsLength() + PtcJumpTable.GetSerializeSize(PtcJumpTable); + innerHeader.Magic = _innerHeaderMagic; - Span sizeBytes = new byte[sizeof(int)]; - BinaryPrimitives.WriteInt32LittleEndian(sizeBytes, size); - Hash128 sizeHash = XXHash128.ComputeHash(sizeBytes); + innerHeader.InfosLength = (int)_infosStream.Length; + innerHeader.CodesLength = _codesList.Length(); + innerHeader.RelocsLength = (int)_relocsStream.Length; + innerHeader.UnwindInfosLength = (int)_unwindInfosStream.Length; + innerHeader.PtcJumpTableLength = PtcJumpTable.GetSerializeSize(PtcJumpTable); - Span sizeHashBytes = new byte[hashSize]; - MemoryMarshal.Write(sizeHashBytes, ref sizeHash); + OuterHeader outerHeader = new OuterHeader(); + + outerHeader.Magic = _outerHeaderMagic; + + outerHeader.CacheFileVersion = InternalVersion; + outerHeader.Endianness = GetEndianness(); + outerHeader.FeatureInfo = GetFeatureInfo(); + outerHeader.OSPlatform = GetOSPlatform(); + + outerHeader.UncompressedStreamSize = + (long)Unsafe.SizeOf() + + innerHeader.InfosLength + + innerHeader.CodesLength + + innerHeader.RelocsLength + + innerHeader.UnwindInfosLength + + innerHeader.PtcJumpTableLength; + + outerHeader.SetHeaderHash(); IntPtr intPtr = IntPtr.Zero; try { - intPtr = Marshal.AllocHGlobal(size); + intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize)); - using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), size, size, FileAccess.ReadWrite)) + using (UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite)) { - stream.Seek((long)hashSize, SeekOrigin.Begin); - - WriteHeader(stream); + stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); + ReadOnlySpan infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); _infosStream.WriteTo(stream); - _codesStream.WriteTo(stream); + + ReadOnlySpan codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan.Empty; + _codesList.WriteTo(stream); + + ReadOnlySpan relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); _relocsStream.WriteTo(stream); + + ReadOnlySpan unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); _unwindInfosStream.WriteTo(stream); + ReadOnlySpan ptcJumpTableBytes = new(stream.PositionPointer, innerHeader.PtcJumpTableLength); PtcJumpTable.Serialize(stream, PtcJumpTable); - stream.Seek((long)hashSize, SeekOrigin.Begin); - ReadOnlySpan streamBytes = new(stream.PositionPointer, (int)(stream.Length - stream.Position)); - Hash128 hash = XXHash128.ComputeHash(streamBytes); + Debug.Assert(stream.Position == stream.Length); + + innerHeader.InfosHash = XXHash128.ComputeHash(infosBytes); + innerHeader.CodesHash = XXHash128.ComputeHash(codesBytes); + innerHeader.RelocsHash = XXHash128.ComputeHash(relocsBytes); + innerHeader.UnwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); + innerHeader.PtcJumpTableHash = XXHash128.ComputeHash(ptcJumpTableBytes); + + innerHeader.SetHeaderHash(); stream.Seek(0L, SeekOrigin.Begin); - SerializeStructure(stream, hash); + SerializeStructure(stream, innerHeader); - translatedFuncsCount = GetInfosEntriesCount(); + translatedFuncsCount = GetEntriesCount(); - ResetMemoryStreamsIfNeeded(); + ResetCarriersIfNeeded(); PtcJumpTable.ClearIfNeeded(); using (FileStream compressedStream = new(fileName, FileMode.OpenOrCreate)) @@ -456,8 +505,7 @@ namespace ARMeilleure.Translation.PTC { try { - compressedStream.Write(sizeHashBytes); - compressedStream.Write(sizeBytes); + SerializeStructure(compressedStream, outerHeader); stream.Seek(0L, SeekOrigin.Begin); stream.CopyTo(deflateStream); @@ -490,67 +538,40 @@ namespace ARMeilleure.Translation.PTC } } - private static int GetMemoryStreamsLength() - { - return (int)_infosStream.Length + (int)_codesStream.Length + (int)_relocsStream.Length + (int)_unwindInfosStream.Length; - } - - private static void WriteHeader(Stream stream) - { - using (BinaryWriter headerWriter = new(stream, EncodingCache.UTF8NoBOM, true)) - { - headerWriter.Write((ulong)_headerMagic); // Header.Magic - - headerWriter.Write((uint)InternalVersion); // Header.CacheFileVersion - headerWriter.Write((bool)GetEndianness()); // Header.Endianness - headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo - headerWriter.Write((uint)GetOSPlatform()); // Header.OSPlatform - - headerWriter.Write((int)_infosStream.Length); // Header.InfosLen - headerWriter.Write((int)_codesStream.Length); // Header.CodesLen - headerWriter.Write((int)_relocsStream.Length); // Header.RelocsLen - headerWriter.Write((int)_unwindInfosStream.Length); // Header.UnwindInfosLen - } - } - internal static void LoadTranslations(ConcurrentDictionary funcs, IMemoryManager memory, JumpTable jumpTable) { - if (AreMemoryStreamsEmpty()) + if (AreCarriersEmpty()) { return; } - Debug.Assert(funcs.Count == 0); - _infosStream.Seek(0L, SeekOrigin.Begin); - _codesStream.Seek(0L, SeekOrigin.Begin); _relocsStream.Seek(0L, SeekOrigin.Begin); _unwindInfosStream.Seek(0L, SeekOrigin.Begin); using (BinaryReader infosReader = new(_infosStream, EncodingCache.UTF8NoBOM, true)) - using (BinaryReader codesReader = new(_codesStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader relocsReader = new(_relocsStream, EncodingCache.UTF8NoBOM, true)) using (BinaryReader unwindInfosReader = new(_unwindInfosStream, EncodingCache.UTF8NoBOM, true)) { - for (int i = 0; i < GetInfosEntriesCount(); i++) + for (int index = 0; index < GetEntriesCount(); index++) { InfoEntry infoEntry = ReadInfo(infosReader); if (infoEntry.Stubbed) { - SkipCode(infoEntry.CodeLen); + SkipCode(index, infoEntry.CodeLength); SkipReloc(infoEntry.RelocEntriesCount); SkipUnwindInfo(unwindInfosReader); } else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.HighCq) { - Span code = ReadCode(codesReader, infoEntry.CodeLen); + byte[] code = ReadCode(index, infoEntry.CodeLength); if (infoEntry.RelocEntriesCount != 0) { RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); - PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable); + PatchCode(code.AsSpan(), relocEntries, memory.PageTablePointer, jumpTable); } UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); @@ -564,9 +585,10 @@ namespace ARMeilleure.Translation.PTC else { infoEntry.Stubbed = true; + infoEntry.CodeLength = 0; UpdateInfo(infoEntry); - StubCode(infoEntry.CodeLen); + StubCode(index); StubReloc(infoEntry.RelocEntriesCount); StubUnwindInfo(unwindInfosReader); } @@ -574,7 +596,6 @@ namespace ARMeilleure.Translation.PTC } if (_infosStream.Position < _infosStream.Length || - _codesStream.Position < _codesStream.Length || _relocsStream.Position < _relocsStream.Length || _unwindInfosStream.Position < _unwindInfosStream.Length) { @@ -589,9 +610,9 @@ namespace ARMeilleure.Translation.PTC Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded"); } - private static int GetInfosEntriesCount() + private static int GetEntriesCount() { - return (int)_infosStream.Length / InfoEntry.Stride; + return _codesList.Count; } private static InfoEntry ReadInfo(BinaryReader infosReader) @@ -602,15 +623,17 @@ namespace ARMeilleure.Translation.PTC infoEntry.GuestSize = infosReader.ReadUInt64(); infoEntry.HighCq = infosReader.ReadBoolean(); infoEntry.Stubbed = infosReader.ReadBoolean(); - infoEntry.CodeLen = infosReader.ReadInt32(); + infoEntry.CodeLength = infosReader.ReadInt32(); infoEntry.RelocEntriesCount = infosReader.ReadInt32(); return infoEntry; } - private static void SkipCode(int codeLen) + [Conditional("DEBUG")] + private static void SkipCode(int index, int codeLength) { - _codesStream.Seek(codeLen, SeekOrigin.Current); + Debug.Assert(_codesList[index].Length == 0); + Debug.Assert(codeLength == 0); } private static void SkipReloc(int relocEntriesCount) @@ -625,13 +648,11 @@ namespace ARMeilleure.Translation.PTC _unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current); } - private static Span ReadCode(BinaryReader codesReader, int codeLen) + private static byte[] ReadCode(int index, int codeLength) { - Span codeBuf = new byte[codeLen]; + Debug.Assert(_codesList[index].Length == codeLength); - codesReader.Read(codeBuf); - - return codeBuf; + return _codesList[index]; } private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) @@ -701,9 +722,9 @@ namespace ARMeilleure.Translation.PTC return new UnwindInfo(pushEntries, prologueSize); } - private static TranslatedFunction FastTranslate(ReadOnlySpan code, ulong guestSize, UnwindInfo unwindInfo, bool highCq) + private static TranslatedFunction FastTranslate(byte[] code, ulong guestSize, UnwindInfo unwindInfo, bool highCq) { - CompiledFunction cFunc = new CompiledFunction(code.ToArray(), unwindInfo); + CompiledFunction cFunc = new CompiledFunction(code, unwindInfo); IntPtr codePtr = JitCache.Map(cFunc); @@ -723,16 +744,13 @@ namespace ARMeilleure.Translation.PTC _infosWriter.Write((ulong)infoEntry.GuestSize); _infosWriter.Write((bool)infoEntry.HighCq); _infosWriter.Write((bool)infoEntry.Stubbed); - _infosWriter.Write((int)infoEntry.CodeLen); + _infosWriter.Write((int)infoEntry.CodeLength); _infosWriter.Write((int)infoEntry.RelocEntriesCount); } - private static void StubCode(int codeLen) + private static void StubCode(int index) { - for (int i = 0; i < codeLen; i++) - { - _codesStream.WriteByte(FillingByte); - } + _codesList[index] = Array.Empty(); } private static void StubReloc(int relocEntriesCount) @@ -757,9 +775,14 @@ namespace ARMeilleure.Translation.PTC { var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs); - if (profiledFuncsToTranslate.Count == 0) + _translateCount = 0; + _translateTotalCount = profiledFuncsToTranslate.Count; + + int degreeOfParallelism = new DegreeOfParallelism(4d, 75d, 12.5d).GetDegreeOfParallelism(0, 32); + + if (_translateTotalCount == 0 || degreeOfParallelism == 0) { - ResetMemoryStreamsIfNeeded(); + ResetCarriersIfNeeded(); PtcJumpTable.ClearIfNeeded(); GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; @@ -767,8 +790,7 @@ namespace ARMeilleure.Translation.PTC return; } - _translateCount = 0; - _translateTotalCount = profiledFuncsToTranslate.Count; + Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}"); PtcStateChanged?.Invoke(PtcLoadingState.Start, _translateCount, _translateTotalCount); @@ -813,11 +835,9 @@ namespace ARMeilleure.Translation.PTC Translator.DisposePools(); } - int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4; - List threads = new List(); - for (int i = 0; i < maxDegreeOfParallelism; i++) + for (int i = 0; i < degreeOfParallelism; i++) { Thread thread = new Thread(TranslateFuncs); thread.IsBackground = true; @@ -835,7 +855,7 @@ namespace ARMeilleure.Translation.PTC PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount); - Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated"); + Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism}"); PtcJumpTable.Initialize(jumpTable); @@ -849,7 +869,7 @@ namespace ARMeilleure.Translation.PTC private static void ReportProgress(object state) { - const int refreshRate = 50; // ms + const int refreshRate = 50; // ms. AutoResetEvent endEvent = (AutoResetEvent)state; @@ -877,11 +897,10 @@ namespace ARMeilleure.Translation.PTC _infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize _infosWriter.Write((bool)highCq); // InfoEntry.HighCq _infosWriter.Write((bool)false); // InfoEntry.Stubbed - _infosWriter.Write((int)ptcInfo.Code.Length); // InfoEntry.CodeLen + _infosWriter.Write((int)ptcInfo.Code.Length); // InfoEntry.CodeLength _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount - // WriteCode. - _codesStream.Write(ptcInfo.Code.AsSpan()); + WriteCode(ptcInfo.Code.AsSpan()); // WriteReloc. ptcInfo.RelocStream.WriteTo(_relocsStream); @@ -891,6 +910,11 @@ namespace ARMeilleure.Translation.PTC } } + private static void WriteCode(ReadOnlySpan code) + { + _codesList.Add(code.ToArray()); + } + private static bool GetEndianness() { return BitConverter.IsLittleEndian; @@ -913,21 +937,68 @@ namespace ARMeilleure.Translation.PTC return osPlatform; } - private struct Header + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 49*/)] + private struct OuterHeader { - public const int Size = 41; // Bytes. - public ulong Magic; public uint CacheFileVersion; + public bool Endianness; public ulong FeatureInfo; public uint OSPlatform; - public int InfosLen; - public int CodesLen; - public int RelocsLen; - public int UnwindInfosLen; + public long UncompressedStreamSize; + + public Hash128 HeaderHash; + + public void SetHeaderHash() + { + Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf() - Unsafe.SizeOf())); + } + + public bool IsHeaderValid() + { + Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf() - Unsafe.SizeOf())) == HeaderHash; + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)] + private struct InnerHeader + { + public ulong Magic; + + public int InfosLength; + public long CodesLength; + public int RelocsLength; + public int UnwindInfosLength; + public int PtcJumpTableLength; + + public Hash128 InfosHash; + public Hash128 CodesHash; + public Hash128 RelocsHash; + public Hash128 UnwindInfosHash; + public Hash128 PtcJumpTableHash; + + public Hash128 HeaderHash; + + public void SetHeaderHash() + { + Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf() - Unsafe.SizeOf())); + } + + public bool IsHeaderValid() + { + Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); + + return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader).Slice(0, Unsafe.SizeOf() - Unsafe.SizeOf())) == HeaderHash; + } } private struct InfoEntry @@ -938,7 +1009,7 @@ namespace ARMeilleure.Translation.PTC public ulong GuestSize; public bool HighCq; public bool Stubbed; - public int CodeLen; + public int CodeLength; public int RelocEntriesCount; } @@ -983,7 +1054,7 @@ namespace ARMeilleure.Translation.PTC Wait(); _waitEvent.Dispose(); - DisposeMemoryStreams(); + DisposeCarriers(); } } } diff --git a/ARMeilleure/Translation/PTC/PtcFormatter.cs b/ARMeilleure/Translation/PTC/PtcFormatter.cs index 7346b484..753c01c8 100644 --- a/ARMeilleure/Translation/PTC/PtcFormatter.cs +++ b/ARMeilleure/Translation/PTC/PtcFormatter.cs @@ -6,11 +6,11 @@ using System.Runtime.InteropServices; namespace ARMeilleure.Translation.PTC { - public class PtcFormatter + static class PtcFormatter { #region "Deserialize" [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Dictionary DeserializeDictionary(Stream stream, Func valueFunc) where TKey : unmanaged + public static Dictionary DeserializeDictionary(Stream stream, Func valueFunc) where TKey : struct { Dictionary dictionary = new(); @@ -28,7 +28,7 @@ namespace ARMeilleure.Translation.PTC } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static List DeserializeList(Stream stream) where T : unmanaged + public static List DeserializeList(Stream stream) where T : struct { List list = new(); @@ -45,7 +45,7 @@ namespace ARMeilleure.Translation.PTC } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T DeserializeStructure(Stream stream) where T : unmanaged + public static T DeserializeStructure(Stream stream) where T : struct { T structure = default(T); @@ -58,7 +58,7 @@ namespace ARMeilleure.Translation.PTC #region "GetSerializeSize" [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetSerializeSizeDictionary(Dictionary dictionary, Func valueFunc) where TKey : unmanaged + public static int GetSerializeSizeDictionary(Dictionary dictionary, Func valueFunc) where TKey : struct { int size = 0; @@ -74,7 +74,7 @@ namespace ARMeilleure.Translation.PTC } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetSerializeSizeList(List list) where T : unmanaged + public static int GetSerializeSizeList(List list) where T : struct { int size = 0; @@ -88,7 +88,7 @@ namespace ARMeilleure.Translation.PTC #region "Serialize" [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SerializeDictionary(Stream stream, Dictionary dictionary, Action valueAction) where TKey : unmanaged + public static void SerializeDictionary(Stream stream, Dictionary dictionary, Action valueAction) where TKey : struct { SerializeStructure(stream, dictionary.Count); @@ -100,7 +100,7 @@ namespace ARMeilleure.Translation.PTC } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SerializeList(Stream stream, List list) where T : unmanaged + public static void SerializeList(Stream stream, List list) where T : struct { SerializeStructure(stream, list.Count); @@ -111,11 +111,59 @@ namespace ARMeilleure.Translation.PTC } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SerializeStructure(Stream stream, T structure) where T : unmanaged + public static void SerializeStructure(Stream stream, T structure) where T : struct { Span spanT = MemoryMarshal.CreateSpan(ref structure, 1); stream.Write(MemoryMarshal.AsBytes(spanT)); } #endregion + + #region "Extension methods" + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadFrom(this List list, Stream stream) where T : struct + { + int count = DeserializeStructure(stream); + + for (int i = 0; i < count; i++) + { + int itemLength = DeserializeStructure(stream); + + T[] item = new T[itemLength]; + + stream.Read(MemoryMarshal.AsBytes(item.AsSpan())); + + list.Add(item); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Length(this List list) where T : struct + { + long size = 0L; + + size += Unsafe.SizeOf(); + + foreach (T[] item in list) + { + size += Unsafe.SizeOf(); + size += item.Length; + } + + return size; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteTo(this List list, Stream stream) where T : struct + { + SerializeStructure(stream, list.Count); + + foreach (T[] item in list) + { + SerializeStructure(stream, item.Length); + + stream.Write(MemoryMarshal.AsBytes(item.AsSpan())); + } + } + #endregion } } \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/PtcInfo.cs b/ARMeilleure/Translation/PTC/PtcInfo.cs index bbecb56f..920469c7 100644 --- a/ARMeilleure/Translation/PTC/PtcInfo.cs +++ b/ARMeilleure/Translation/PTC/PtcInfo.cs @@ -59,4 +59,4 @@ namespace ARMeilleure.Translation.PTC UnwindInfoStream.Dispose(); } } -} +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/PtcJumpTable.cs b/ARMeilleure/Translation/PTC/PtcJumpTable.cs index 75dcba50..40a30329 100644 --- a/ARMeilleure/Translation/PTC/PtcJumpTable.cs +++ b/ARMeilleure/Translation/PTC/PtcJumpTable.cs @@ -17,7 +17,7 @@ namespace ARMeilleure.Translation.PTC { public int EntryIndex; public long GuestAddress; - public TAddress HostAddress; // int + public TAddress HostAddress; public TableEntry(int entryIndex, long guestAddress, TAddress hostAddress) { diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index 8782a794..d7b2b0f8 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -399,4 +399,4 @@ namespace ARMeilleure.Translation.PTC } } } -} +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/RelocEntry.cs b/ARMeilleure/Translation/PTC/RelocEntry.cs index bb77e1f0..52d73db8 100644 --- a/ARMeilleure/Translation/PTC/RelocEntry.cs +++ b/ARMeilleure/Translation/PTC/RelocEntry.cs @@ -18,4 +18,4 @@ namespace ARMeilleure.Translation.PTC return $"({nameof(Position)} = {Position}, {nameof(Index)} = {Index})"; } } -} +} \ No newline at end of file diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index f64912b3..73a321fd 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -103,6 +103,7 @@ namespace ARMeilleure.Translation if (Ptc.State == PtcState.Enabled) { + Debug.Assert(_funcs.Count == 0); Ptc.LoadTranslations(_funcs, _memory, _jumpTable); Ptc.MakeAndSaveTranslations(_funcs, _memory, _jumpTable); }