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;
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;
}
}

View file

@ -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;
}