From be04d232c07580a45a996aa9896bf81344ea1018 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 2 Jul 2023 16:35:45 +0100 Subject: [PATCH] Avoid some allocations on the large object heap during loading. - In MixFile, the Distinct call doesn't presize the HashSet it uses internally. As we know we will enumerate all results, create the HashSet ourselves so that is it presized correctly. - In ObjectCreator, stream the assembly when hashing rather than reading all bytes into memory. These changes avoid some allocations on the large object heap, in turn this means the GC can avoid performing unnecessary Gen 2 collections just to clear down the LOH. --- OpenRA.Game/ObjectCreator.cs | 4 +++- OpenRA.Mods.Cnc/FileSystem/MixFile.cs | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/OpenRA.Game/ObjectCreator.cs b/OpenRA.Game/ObjectCreator.cs index bb9d72d097..e8b6900620 100644 --- a/OpenRA.Game/ObjectCreator.cs +++ b/OpenRA.Game/ObjectCreator.cs @@ -55,7 +55,9 @@ namespace OpenRA // (a) loading duplicate data into the application domain, breaking the world. // (b) crashing if the assembly has already been loaded. // We can't check the internal name of the assembly, so we'll work off the data instead - var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath)); + string hash; + using (var stream = File.OpenRead(resolvedPath)) + hash = CryptoUtil.SHA1Hash(stream); if (!ResolvedAssemblies.TryGetValue(hash, out var assembly)) { diff --git a/OpenRA.Mods.Cnc/FileSystem/MixFile.cs b/OpenRA.Mods.Cnc/FileSystem/MixFile.cs index 77e157b0be..54dcb15015 100644 --- a/OpenRA.Mods.Cnc/FileSystem/MixFile.cs +++ b/OpenRA.Mods.Cnc/FileSystem/MixFile.cs @@ -68,7 +68,7 @@ namespace OpenRA.Mods.Cnc.FileSystem { var classicIndex = new Dictionary(); var crcIndex = new Dictionary(); - IEnumerable allPossibleFilenames = globalFilenames; + var allPossibleFilenames = new HashSet(globalFilenames); // Try and find a local mix database var dbNameClassic = PackageEntry.HashFilename("local mix database.dat", PackageHashType.Classic); @@ -80,14 +80,15 @@ namespace OpenRA.Mods.Cnc.FileSystem using (var content = GetContent(kv.Value)) { var db = new XccLocalDatabase(content); - allPossibleFilenames = allPossibleFilenames.Concat(db.Entries); + allPossibleFilenames.EnsureCapacity(allPossibleFilenames.Count + db.Entries.Length); + allPossibleFilenames.UnionWith(db.Entries); } break; } } - foreach (var filename in allPossibleFilenames.Distinct()) + foreach (var filename in allPossibleFilenames) { var classicHash = PackageEntry.HashFilename(filename, PackageHashType.Classic); var crcHash = PackageEntry.HashFilename(filename, PackageHashType.CRC32); @@ -237,7 +238,7 @@ namespace OpenRA.Mods.Cnc.FileSystem if (globalFilenames == null) if (context.TryOpen("global mix database.dat", out var mixDatabase)) using (var db = new XccGlobalDatabase(mixDatabase)) - globalFilenames = db.Entries.Distinct().ToArray(); + globalFilenames = db.Entries.ToHashSet().ToArray(); package = new MixFile(s, filename, globalFilenames ?? Array.Empty()); return true;