mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Basic prototype string interning
From my extremely rough and unscientific tests, this saves like 15 MB of client memory on the main menu. Probably also just improves load speed on startup too. It's per file to keep the implementation simple.
This commit is contained in:
@@ -53,7 +53,7 @@ public partial class PrototypeManager
|
||||
|
||||
var extractedList = new List<ExtractedMappingData>();
|
||||
var i = 0;
|
||||
foreach (var document in DataNodeParser.ParseYamlStream(reader))
|
||||
foreach (var document in DataNodeParser.ParseYamlStream(reader, internStrings: true))
|
||||
{
|
||||
i += 1;
|
||||
LoadedData?.Invoke(document);
|
||||
@@ -152,7 +152,7 @@ public partial class PrototypeManager
|
||||
return;
|
||||
|
||||
var i = 0;
|
||||
foreach (var document in DataNodeParser.ParseYamlStream(reader))
|
||||
foreach (var document in DataNodeParser.ParseYamlStream(reader, internStrings: true))
|
||||
{
|
||||
LoadedData?.Invoke(document);
|
||||
|
||||
@@ -254,7 +254,7 @@ public partial class PrototypeManager
|
||||
_hasEverBeenReloaded = true;
|
||||
|
||||
var i = 0;
|
||||
foreach (var document in DataNodeParser.ParseYamlStream(stream))
|
||||
foreach (var document in DataNodeParser.ParseYamlStream(stream, internStrings: true))
|
||||
{
|
||||
LoadedData?.Invoke(document);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
@@ -17,22 +18,31 @@ public static class DataNodeParser
|
||||
{
|
||||
public static IEnumerable<DataNodeDocument> ParseYamlStream(TextReader reader)
|
||||
{
|
||||
return ParseYamlStream(new Parser(reader));
|
||||
return ParseYamlStream(reader, internStrings: false);
|
||||
}
|
||||
|
||||
internal static IEnumerable<DataNodeDocument> ParseYamlStream(Parser parser)
|
||||
internal static IEnumerable<DataNodeDocument> ParseYamlStream(TextReader reader, bool internStrings)
|
||||
{
|
||||
return ParseYamlStream(new Parser(reader), internStrings);
|
||||
}
|
||||
|
||||
internal static IEnumerable<DataNodeDocument> ParseYamlStream(Parser parser, bool internStrings = false)
|
||||
{
|
||||
var state = new ParserState(internStrings);
|
||||
|
||||
parser.Consume<StreamStart>();
|
||||
|
||||
while (!parser.TryConsume<StreamEnd>(out _))
|
||||
{
|
||||
yield return ParseDocument(parser);
|
||||
yield return ParseDocument(parser, state);
|
||||
}
|
||||
|
||||
// System.Console.WriteLine(state.TotalStringsSaved);
|
||||
}
|
||||
|
||||
private static DataNodeDocument ParseDocument(Parser parser)
|
||||
private static DataNodeDocument ParseDocument(Parser parser, ParserState parserState)
|
||||
{
|
||||
var state = new DocumentState();
|
||||
var state = new DocumentState(parserState);
|
||||
|
||||
parser.Consume<DocumentStart>();
|
||||
|
||||
@@ -78,7 +88,11 @@ public static class DataNodeParser
|
||||
private static ValueDataNode ParseValue(Parser parser, DocumentState state)
|
||||
{
|
||||
var ev = parser.Consume<Scalar>();
|
||||
var node = new ValueDataNode(ev){Tag = ConvertTag(ev.Tag)};
|
||||
var node = new ValueDataNode(ev)
|
||||
{
|
||||
Tag = ConvertTag(ev.Tag, state.ParserState),
|
||||
Value = state.ParserState.InternString(ev.Value)
|
||||
};
|
||||
|
||||
NodeParsed(node, ev, false, state);
|
||||
|
||||
@@ -100,7 +114,7 @@ public static class DataNodeParser
|
||||
var ev = parser.Consume<SequenceStart>();
|
||||
|
||||
var node = new SequenceDataNode();
|
||||
node.Tag = ConvertTag(ev.Tag);
|
||||
node.Tag = ConvertTag(ev.Tag, state.ParserState);
|
||||
node.Start = ev.Start;
|
||||
|
||||
var unresolvedAlias = false;
|
||||
@@ -127,14 +141,14 @@ public static class DataNodeParser
|
||||
var ev = parser.Consume<MappingStart>();
|
||||
|
||||
var node = new MappingDataNode();
|
||||
node.Tag = ConvertTag(ev.Tag);
|
||||
node.Tag = ConvertTag(ev.Tag, state.ParserState);
|
||||
|
||||
var unresolvedAlias = false;
|
||||
|
||||
MappingEnd mapEnd;
|
||||
while (!parser.TryConsume(out mapEnd))
|
||||
{
|
||||
var key = ParseKey(parser);
|
||||
var key = state.ParserState.InternString(ParseKey(parser));
|
||||
var value = Parse(parser, state);
|
||||
|
||||
node.Add(key, value);
|
||||
@@ -218,13 +232,14 @@ public static class DataNodeParser
|
||||
return node;
|
||||
}
|
||||
|
||||
private static string ConvertTag(TagName tag)
|
||||
private static string ConvertTag(TagName tag, ParserState state)
|
||||
{
|
||||
return (tag.IsNonSpecific || tag.IsEmpty) ? null : tag.Value;
|
||||
return (tag.IsNonSpecific || tag.IsEmpty) ? null : state.InternString(tag.Value);
|
||||
}
|
||||
|
||||
private sealed class DocumentState
|
||||
private sealed class DocumentState(ParserState parserState)
|
||||
{
|
||||
public readonly ParserState ParserState = parserState;
|
||||
public readonly Dictionary<AnchorName, DataNode> Anchors = new();
|
||||
public ValueList<DataNode> UnresolvedAliasOwners;
|
||||
}
|
||||
@@ -256,6 +271,37 @@ public static class DataNodeParser
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
|
||||
private sealed class ParserState(bool internStrings)
|
||||
{
|
||||
public readonly HashSet<string>? StringInternIndex = internStrings ? [] : null;
|
||||
//public int TotalStringsSaved = 0;
|
||||
|
||||
[return: NotNullIfNotNull(nameof(str))]
|
||||
public string? InternString(string? str)
|
||||
{
|
||||
if (StringInternIndex == null)
|
||||
return str;
|
||||
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
// Use a basic string interning system to avoid releasing a bunch of equivalent strings.
|
||||
// This avoids having thousands of identical strings for stuff like "type" in prototypes stored in memory.
|
||||
if (StringInternIndex.TryGetValue(str, out var indexedString))
|
||||
{
|
||||
// if (!ReferenceEquals(str, indexedString))
|
||||
// TotalStringsSaved += 1;
|
||||
|
||||
return indexedString;
|
||||
}
|
||||
|
||||
StringInternIndex.Add(str);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DataParseException : Exception
|
||||
|
||||
Reference in New Issue
Block a user