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.
This commit is contained in:
RoosterDragon
2023-07-02 16:35:45 +01:00
committed by abcdefg30
parent 659ec5e335
commit be04d232c0
2 changed files with 8 additions and 5 deletions

View File

@@ -55,7 +55,9 @@ namespace OpenRA
// (a) loading duplicate data into the application domain, breaking the world. // (a) loading duplicate data into the application domain, breaking the world.
// (b) crashing if the assembly has already been loaded. // (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 // 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)) if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
{ {

View File

@@ -68,7 +68,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
{ {
var classicIndex = new Dictionary<string, PackageEntry>(); var classicIndex = new Dictionary<string, PackageEntry>();
var crcIndex = new Dictionary<string, PackageEntry>(); var crcIndex = new Dictionary<string, PackageEntry>();
IEnumerable<string> allPossibleFilenames = globalFilenames; var allPossibleFilenames = new HashSet<string>(globalFilenames);
// Try and find a local mix database // Try and find a local mix database
var dbNameClassic = PackageEntry.HashFilename("local mix database.dat", PackageHashType.Classic); 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)) using (var content = GetContent(kv.Value))
{ {
var db = new XccLocalDatabase(content); var db = new XccLocalDatabase(content);
allPossibleFilenames = allPossibleFilenames.Concat(db.Entries); allPossibleFilenames.EnsureCapacity(allPossibleFilenames.Count + db.Entries.Length);
allPossibleFilenames.UnionWith(db.Entries);
} }
break; break;
} }
} }
foreach (var filename in allPossibleFilenames.Distinct()) foreach (var filename in allPossibleFilenames)
{ {
var classicHash = PackageEntry.HashFilename(filename, PackageHashType.Classic); var classicHash = PackageEntry.HashFilename(filename, PackageHashType.Classic);
var crcHash = PackageEntry.HashFilename(filename, PackageHashType.CRC32); var crcHash = PackageEntry.HashFilename(filename, PackageHashType.CRC32);
@@ -237,7 +238,7 @@ namespace OpenRA.Mods.Cnc.FileSystem
if (globalFilenames == null) if (globalFilenames == null)
if (context.TryOpen("global mix database.dat", out var mixDatabase)) if (context.TryOpen("global mix database.dat", out var mixDatabase))
using (var db = new XccGlobalDatabase(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<string>()); package = new MixFile(s, filename, globalFilenames ?? Array.Empty<string>());
return true; return true;