From 90163087a0a0e3a472556c3fe1a363bdc9703596 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Tue, 13 Apr 2021 03:24:36 +0200 Subject: [PATCH] PPTC vs. giant ExeFS. (#2168) * PPTC vs. giant ExeFS. * InternalVersion = 2168 * Add new heuristic algorithm for calculating the number of threads for parallel translations that also takes into account the user's free physical memory and not just the number of CPU cores. * Nit. * Add an outer Header structure and add the hashes for both this new structure and the existing "inner" Header structure. * InternalVersion = 2169 --- .../Translation/PTC/DegreeOfParallelism.cs | 50 ++ ARMeilleure/Translation/PTC/EncodingCache.cs | 4 +- ARMeilleure/Translation/PTC/Ptc.cs | 435 ++++++++++-------- ARMeilleure/Translation/PTC/PtcFormatter.cs | 66 ++- ARMeilleure/Translation/PTC/PtcInfo.cs | 2 +- ARMeilleure/Translation/PTC/PtcJumpTable.cs | 2 +- ARMeilleure/Translation/PTC/PtcProfiler.cs | 2 +- ARMeilleure/Translation/PTC/RelocEntry.cs | 2 +- ARMeilleure/Translation/Translator.cs | 1 + 9 files changed, 367 insertions(+), 197 deletions(-) create mode 100644 ARMeilleure/Translation/PTC/DegreeOfParallelism.cs diff --git a/ARMeilleure/Translation/PTC/DegreeOfParallelism.cs b/ARMeilleure/Translation/PTC/DegreeOfParallelism.cs new file mode 100644 index 0000000000..e4752c5e3f --- /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 b87e0d7a39..90d40c475c 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 6e1a73f665..32e0e7e8cb 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 7346b48449..753c01c80c 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 bbecb56f24..920469c73c 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 75dcba50ca..40a3032917 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 8782a79459..d7b2b0f85d 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 bb77e1f0ff..52d73db8b4 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 f64912b3b4..73a321fd2f 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); }