Optimized GC usage; speedup of ~18%. GC disabling/enabling is also used.
This commit is contained in:
parent
3f75b57b9e
commit
7e3aa4f476
129
dyaml/composer.d
129
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;
|
||||
}
|
||||
}
|
||||
|
|
21
dyaml/node.d
21
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue