From 7e3aa4f47647095cdd27752043f4220150aa65e5 Mon Sep 17 00:00:00 2001 From: Kiith-Sa Date: Sun, 30 Dec 2012 00:25:23 +0100 Subject: [PATCH] Optimized GC usage; speedup of ~18%. GC disabling/enabling is also used. --- dyaml/composer.d | 129 +++++++++++++++++++++++++++++++++++------------ dyaml/node.d | 21 ++++---- 2 files changed, 106 insertions(+), 44 deletions(-) diff --git a/dyaml/composer.d b/dyaml/composer.d index e42b374..df01fac 100644 --- a/dyaml/composer.d +++ b/dyaml/composer.d @@ -10,7 +10,9 @@ */ module dyaml.composer; +import core.memory; +import std.array; import std.conv; import std.exception; import std.typecons; @@ -47,7 +49,20 @@ final class Composer Constructor constructor_; ///Nodes associated with anchors. Used by YAML aliases. Node[Anchor] anchors_; - + + ///Used to reduce allocations when creating pair arrays. + /// + ///We need one appender for each nesting level that involves + ///a pair array, as the inner levels are processed as a + ///part of the outer levels. Used as a stack. + Appender!(Node.Pair[], Node.Pair)[] pairAppenders_; + ///Used to reduce allocations when creating node arrays. + /// + ///We need one appender for each nesting level that involves + ///a node array, as the inner levels are processed as a + ///part of the outer levels. Used as a stack. + Appender!(Node[], Node)[] nodeAppenders_; + public: /** * Construct a composer. @@ -56,7 +71,7 @@ final class Composer * resolver = Resolver to resolve tags (data types). * constructor = Constructor to construct nodes. */ - this(Parser parser, Resolver resolver, Constructor constructor) @safe nothrow pure + this(Parser parser, Resolver resolver, Constructor constructor) @safe { parser_ = parser; resolver_ = resolver; @@ -109,7 +124,7 @@ final class Composer "get. use checkNode() to determine if there is a node."); Node document = composeDocument(); - + //Ensure that the stream contains no more documents. enforce(parser_.checkEvent(EventID.StreamEnd), new ComposerException("Expected single document in the stream, " @@ -123,6 +138,23 @@ final class Composer } private: + ///Ensure that appenders for specified nesting levels exist. + /// + ///Params: pairAppenderLevel = Current level in the pair appender stack. + /// nodeAppenderLevel = Current level the node appender stack. + void ensureAppendersExist(const uint pairAppenderLevel, const uint nodeAppenderLevel) + @trusted + { + while(pairAppenders_.length <= pairAppenderLevel) + { + pairAppenders_ ~= appender!(Node.Pair[])(); + } + while(nodeAppenders_.length <= nodeAppenderLevel) + { + nodeAppenders_ ~= appender!(Node[])(); + } + } + ///Compose a YAML document and return its root node. Node composeDocument() @trusted { @@ -130,7 +162,7 @@ final class Composer parser_.getEvent(); //Compose the root node. - Node node = composeNode(); + Node node = composeNode(0, 0); //Drop the DOCUMENT-END event. parser_.getEvent(); @@ -139,8 +171,11 @@ final class Composer return node; } - ///Compose a node. - Node composeNode() @system + /// Compose a node. + /// + /// Params: pairAppenderLevel = Current level of the pair appender stack. + /// nodeAppenderLevel = Current level of the node appender stack. + Node composeNode(const uint pairAppenderLevel, const uint nodeAppenderLevel) @system { if(parser_.checkEvent(EventID.Alias)) { @@ -182,11 +217,11 @@ final class Composer } else if(parser_.checkEvent(EventID.SequenceStart)) { - result = composeSequenceNode(); + result = composeSequenceNode(pairAppenderLevel, nodeAppenderLevel); } else if(parser_.checkEvent(EventID.MappingStart)) - { - result = composeMappingNode(); + { + result = composeMappingNode(pairAppenderLevel, nodeAppenderLevel); } else{assert(false, "This code should never be reached");} @@ -210,21 +245,30 @@ final class Composer return node; } - ///Compose a sequence node. - Node composeSequenceNode() @system + /// Compose a sequence node. + /// + /// Params: pairAppenderLevel = Current level of the pair appender stack. + /// nodeAppenderLevel = Current level of the node appender stack. + Node composeSequenceNode(const uint pairAppenderLevel, const uint nodeAppenderLevel) + @system { + ensureAppendersExist(pairAppenderLevel, nodeAppenderLevel); + auto nodeAppender = &(nodeAppenders_[nodeAppenderLevel]); + immutable startEvent = parser_.getEvent(); const tag = resolver_.resolve(NodeID.Sequence, startEvent.tag, null, startEvent.implicit); - Node[] children; while(!parser_.checkEvent(EventID.SequenceEnd)) { - children ~= composeNode(); + nodeAppender.put(composeNode(pairAppenderLevel, nodeAppenderLevel + 1)); } + core.memory.GC.disable(); + scope(exit){core.memory.GC.enable();} Node node = constructor_.node(startEvent.startMark, parser_.getEvent().endMark, - tag, children, startEvent.collectionStyle); + tag, nodeAppender.data.dup, startEvent.collectionStyle); + nodeAppender.clear(); return node; } @@ -234,16 +278,17 @@ final class Composer * * Node must be a mapping or a sequence of mappings. * - * Params: root = Node to flatten. - * startMark = Start position of the node. - * endMark = End position of the node. - * + * Params: root = Node to flatten. + * startMark = Start position of the node. + * endMark = End position of the node. + * pairAppenderLevel = Current level of the pair appender stack. + * nodeAppenderLevel = Current level of the node appender stack. + * * Returns: Flattened mapping as pairs. */ - Node.Pair[] flatten(ref Node root, in Mark startMark, in Mark endMark) @system + Node.Pair[] flatten(ref Node root, const Mark startMark, const Mark endMark, + const uint pairAppenderLevel, const uint nodeAppenderLevel) @system { - Node.Pair[] result; - void error(Node node) { //this is Composer, but the code is related to Constructor. @@ -256,6 +301,9 @@ final class Composer startMark, endMark); } + ensureAppendersExist(pairAppenderLevel, nodeAppenderLevel); + auto pairAppender = &(pairAppenders_[pairAppenderLevel]); + if(root.isMapping) { Node[] toMerge; @@ -265,40 +313,53 @@ final class Composer else { auto temp = Node.Pair(key, value); - merge(result, temp); + merge(*pairAppender, temp); } } foreach(node; toMerge) { - merge(result, flatten(node, startMark, endMark)); + merge(*pairAppender, flatten(node, startMark, endMark, + pairAppenderLevel + 1, nodeAppenderLevel)); } } //Must be a sequence of mappings. else if(root.isSequence) foreach(ref Node node; root) { if(!node.isType!(Node.Pair[])){error(node);} - merge(result, flatten(node, startMark, endMark)); + merge(*pairAppender, flatten(node, startMark, endMark, + pairAppenderLevel + 1, nodeAppenderLevel)); } else { error(root); } - return result; + core.memory.GC.disable(); + scope(exit){core.memory.GC.enable();} + auto flattened = pairAppender.data.dup; + pairAppender.clear(); + + return flattened; } - ///Compose a mapping node. - Node composeMappingNode() @system + /// Compose a mapping node. + /// + /// Params: pairAppenderLevel = Current level of the pair appender stack. + /// nodeAppenderLevel = Current level of the node appender stack. + Node composeMappingNode(const uint pairAppenderLevel, const uint nodeAppenderLevel) + @system { + ensureAppendersExist(pairAppenderLevel, nodeAppenderLevel); immutable startEvent = parser_.getEvent(); const tag = resolver_.resolve(NodeID.Mapping, startEvent.tag, null, startEvent.implicit); + auto pairAppender = &(pairAppenders_[pairAppenderLevel]); - Node.Pair[] children; Tuple!(Node, Mark)[] toMerge; while(!parser_.checkEvent(EventID.MappingEnd)) { - auto pair = Node.Pair(composeNode(), composeNode()); + auto pair = Node.Pair(composeNode(pairAppenderLevel + 1, nodeAppenderLevel), + composeNode(pairAppenderLevel + 1, nodeAppenderLevel)); //Need to flatten and merge the node referred by YAMLMerge. if(pair.key.isType!YAMLMerge) @@ -308,17 +369,21 @@ final class Composer //Not YAMLMerge, just add the pair. else { - merge(children, pair); + merge(*pairAppender, pair); } } foreach(node; toMerge) { - merge(children, flatten(node[0], startEvent.startMark, node[1])); + merge(*pairAppender, flatten(node[0], startEvent.startMark, node[1], + pairAppenderLevel + 1, nodeAppenderLevel)); } + core.memory.GC.disable(); + scope(exit){core.memory.GC.enable();} Node node = constructor_.node(startEvent.startMark, parser_.getEvent().endMark, - tag, children, startEvent.collectionStyle); + tag, pairAppender.data.dup, startEvent.collectionStyle); + pairAppender.clear(); return node; } } diff --git a/dyaml/node.d b/dyaml/node.d index 5a7a375..8ed1610 100644 --- a/dyaml/node.d +++ b/dyaml/node.d @@ -12,6 +12,7 @@ module dyaml.node; import std.algorithm; +import std.array; import std.conv; import std.datetime; import std.exception; @@ -1776,16 +1777,16 @@ package: * The new pair will only be added if there is not already a pair * with the same key. * - * Params: pairs = Array of pairs to merge into. + * Params: pairs = Appender managing the array of pairs to merge into. * toMerge = Pair to merge. */ -void merge(ref Node.Pair[] pairs, ref Node.Pair toMerge) @safe +void merge(ref Appender!(Node.Pair[], Node.Pair) pairs, ref Node.Pair toMerge) @trusted { - foreach(ref pair; pairs) + foreach(ref pair; pairs.data) { if(pair.key == toMerge.key){return;} } - pairs ~= toMerge; + pairs.put(toMerge); } /* @@ -1794,19 +1795,15 @@ void merge(ref Node.Pair[] pairs, ref Node.Pair toMerge) @safe * Any new pair will only be added if there is not already a pair * with the same key. * - * Params: pairs = Array of pairs to merge into. + * Params: pairs = Appender managing the array of pairs to merge into. * toMerge = Pairs to merge. */ -void merge(ref Node.Pair[] pairs, Node.Pair[] toMerge) @safe +void merge(ref Appender!(Node.Pair[], Node.Pair) pairs, Node.Pair[] toMerge) @trusted { bool eq(ref Node.Pair a, ref Node.Pair b){return a.key == b.key;} - //Preallocating to limit GC reallocations. - auto len = pairs.length; - pairs.length = len + toMerge.length; - foreach(ref pair; toMerge) if(!canFind!eq(pairs, pair)) + foreach(ref pair; toMerge) if(!canFind!eq(pairs.data, pair)) { - pairs[len++] = pair; + pairs.put(pair); } - pairs.length = len; }