Optimized GC usage; speedup of ~18%. GC disabling/enabling is also used.

This commit is contained in:
Kiith-Sa 2012-12-30 00:25:23 +01:00
parent 3f75b57b9e
commit 7e3aa4f476
2 changed files with 106 additions and 44 deletions

View file

@ -10,7 +10,9 @@
*/ */
module dyaml.composer; module dyaml.composer;
import core.memory;
import std.array;
import std.conv; import std.conv;
import std.exception; import std.exception;
import std.typecons; import std.typecons;
@ -47,7 +49,20 @@ final class Composer
Constructor constructor_; Constructor constructor_;
///Nodes associated with anchors. Used by YAML aliases. ///Nodes associated with anchors. Used by YAML aliases.
Node[Anchor] anchors_; 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: public:
/** /**
* Construct a composer. * Construct a composer.
@ -56,7 +71,7 @@ final class Composer
* resolver = Resolver to resolve tags (data types). * resolver = Resolver to resolve tags (data types).
* constructor = Constructor to construct nodes. * 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; parser_ = parser;
resolver_ = resolver; resolver_ = resolver;
@ -109,7 +124,7 @@ final class Composer
"get. use checkNode() to determine if there is a node."); "get. use checkNode() to determine if there is a node.");
Node document = composeDocument(); Node document = composeDocument();
//Ensure that the stream contains no more documents. //Ensure that the stream contains no more documents.
enforce(parser_.checkEvent(EventID.StreamEnd), enforce(parser_.checkEvent(EventID.StreamEnd),
new ComposerException("Expected single document in the stream, " new ComposerException("Expected single document in the stream, "
@ -123,6 +138,23 @@ final class Composer
} }
private: 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. ///Compose a YAML document and return its root node.
Node composeDocument() @trusted Node composeDocument() @trusted
{ {
@ -130,7 +162,7 @@ final class Composer
parser_.getEvent(); parser_.getEvent();
//Compose the root node. //Compose the root node.
Node node = composeNode(); Node node = composeNode(0, 0);
//Drop the DOCUMENT-END event. //Drop the DOCUMENT-END event.
parser_.getEvent(); parser_.getEvent();
@ -139,8 +171,11 @@ final class Composer
return node; return node;
} }
///Compose a node. /// Compose a node.
Node composeNode() @system ///
/// 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)) if(parser_.checkEvent(EventID.Alias))
{ {
@ -182,11 +217,11 @@ final class Composer
} }
else if(parser_.checkEvent(EventID.SequenceStart)) else if(parser_.checkEvent(EventID.SequenceStart))
{ {
result = composeSequenceNode(); result = composeSequenceNode(pairAppenderLevel, nodeAppenderLevel);
} }
else if(parser_.checkEvent(EventID.MappingStart)) else if(parser_.checkEvent(EventID.MappingStart))
{ {
result = composeMappingNode(); result = composeMappingNode(pairAppenderLevel, nodeAppenderLevel);
} }
else{assert(false, "This code should never be reached");} else{assert(false, "This code should never be reached");}
@ -210,21 +245,30 @@ final class Composer
return node; return node;
} }
///Compose a sequence node. /// Compose a sequence node.
Node composeSequenceNode() @system ///
/// 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(); immutable startEvent = parser_.getEvent();
const tag = resolver_.resolve(NodeID.Sequence, startEvent.tag, null, const tag = resolver_.resolve(NodeID.Sequence, startEvent.tag, null,
startEvent.implicit); startEvent.implicit);
Node[] children;
while(!parser_.checkEvent(EventID.SequenceEnd)) 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, Node node = constructor_.node(startEvent.startMark, parser_.getEvent().endMark,
tag, children, startEvent.collectionStyle); tag, nodeAppender.data.dup, startEvent.collectionStyle);
nodeAppender.clear();
return node; return node;
} }
@ -234,16 +278,17 @@ final class Composer
* *
* Node must be a mapping or a sequence of mappings. * Node must be a mapping or a sequence of mappings.
* *
* Params: root = Node to flatten. * Params: root = Node to flatten.
* startMark = Start position of the node. * startMark = Start position of the node.
* endMark = End 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. * 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) void error(Node node)
{ {
//this is Composer, but the code is related to Constructor. //this is Composer, but the code is related to Constructor.
@ -256,6 +301,9 @@ final class Composer
startMark, endMark); startMark, endMark);
} }
ensureAppendersExist(pairAppenderLevel, nodeAppenderLevel);
auto pairAppender = &(pairAppenders_[pairAppenderLevel]);
if(root.isMapping) if(root.isMapping)
{ {
Node[] toMerge; Node[] toMerge;
@ -265,40 +313,53 @@ final class Composer
else else
{ {
auto temp = Node.Pair(key, value); auto temp = Node.Pair(key, value);
merge(result, temp); merge(*pairAppender, temp);
} }
} }
foreach(node; toMerge) foreach(node; toMerge)
{ {
merge(result, flatten(node, startMark, endMark)); merge(*pairAppender, flatten(node, startMark, endMark,
pairAppenderLevel + 1, nodeAppenderLevel));
} }
} }
//Must be a sequence of mappings. //Must be a sequence of mappings.
else if(root.isSequence) foreach(ref Node node; root) else if(root.isSequence) foreach(ref Node node; root)
{ {
if(!node.isType!(Node.Pair[])){error(node);} if(!node.isType!(Node.Pair[])){error(node);}
merge(result, flatten(node, startMark, endMark)); merge(*pairAppender, flatten(node, startMark, endMark,
pairAppenderLevel + 1, nodeAppenderLevel));
} }
else else
{ {
error(root); 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. /// Compose a mapping node.
Node composeMappingNode() @system ///
/// 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(); immutable startEvent = parser_.getEvent();
const tag = resolver_.resolve(NodeID.Mapping, startEvent.tag, null, const tag = resolver_.resolve(NodeID.Mapping, startEvent.tag, null,
startEvent.implicit); startEvent.implicit);
auto pairAppender = &(pairAppenders_[pairAppenderLevel]);
Node.Pair[] children;
Tuple!(Node, Mark)[] toMerge; Tuple!(Node, Mark)[] toMerge;
while(!parser_.checkEvent(EventID.MappingEnd)) 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. //Need to flatten and merge the node referred by YAMLMerge.
if(pair.key.isType!YAMLMerge) if(pair.key.isType!YAMLMerge)
@ -308,17 +369,21 @@ final class Composer
//Not YAMLMerge, just add the pair. //Not YAMLMerge, just add the pair.
else else
{ {
merge(children, pair); merge(*pairAppender, pair);
} }
} }
foreach(node; toMerge) 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, Node node = constructor_.node(startEvent.startMark, parser_.getEvent().endMark,
tag, children, startEvent.collectionStyle); tag, pairAppender.data.dup, startEvent.collectionStyle);
pairAppender.clear();
return node; return node;
} }
} }

View file

@ -12,6 +12,7 @@ module dyaml.node;
import std.algorithm; import std.algorithm;
import std.array;
import std.conv; import std.conv;
import std.datetime; import std.datetime;
import std.exception; import std.exception;
@ -1776,16 +1777,16 @@ package:
* The new pair will only be added if there is not already a pair * The new pair will only be added if there is not already a pair
* with the same key. * 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. * 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;} 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 * Any new pair will only be added if there is not already a pair
* with the same key. * 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. * 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;} bool eq(ref Node.Pair a, ref Node.Pair b){return a.key == b.key;}
//Preallocating to limit GC reallocations. foreach(ref pair; toMerge) if(!canFind!eq(pairs.data, pair))
auto len = pairs.length;
pairs.length = len + toMerge.length;
foreach(ref pair; toMerge) if(!canFind!eq(pairs, pair))
{ {
pairs[len++] = pair; pairs.put(pair);
} }
pairs.length = len;
} }