improve benchmark subpackage

This commit is contained in:
Cameron Ross 2018-04-28 23:43:49 -03:00
parent aa1af974e8
commit 48f08ca3cc
No known key found for this signature in database
GPG key ID: 777897D98DC91C54

View file

@ -2,39 +2,21 @@
module dyaml.yaml_bench; module dyaml.yaml_bench;
//Benchmark that loads, and optionally extracts data from and/or emits a YAML file. //Benchmark that loads, and optionally extracts data from and/or emits a YAML file.
import std.algorithm;
import std.conv; import std.conv;
import std.datetime; import std.datetime.systime;
import std.datetime.stopwatch;
import std.file;
import std.getopt;
import std.range;
import std.stdio; import std.stdio;
import std.string; import std.string;
import dyaml; import dyaml;
///Print help information.
void help()
{
string help =
"D:YAML benchmark\n"~
"Copyright (C) 2011-2014 Ferdinand Majerech\n"~
"Usage: yaml_bench [OPTION ...] [YAML_FILE]\n"~
"\n"~
"Loads and optionally extracts data and/or dumps a YAML file.\n"~
"\n"~
"Available options:\n"~
" -h --help Show this help information.\n"~
" -g --get Extract data from the file (using Node.as()).\n"~
" -d --dump Dump the loaded data (to YAML_FILE.dump).\n"~
" -r --runs=NUM Repeat parsing the file NUM times.\n"~
" --reload Reload the file from the diskl on every repeat\n"~
" By default, the file is loaded to memory once\n"~
" and repeatedly parsed from memory.\n"~
" -s --scan-only Do not execute the entire parsing process, only\n"~
" scanning. Overrides '--dump'.\n";
writeln(help);
}
///Get data out of every node. ///Get data out of every node.
void extract(ref Node document) void extract(ref Node document) @safe
{ {
void crawl(ref Node root) void crawl(ref Node root) @safe
{ {
if(root.isScalar) switch(root.tag) if(root.isScalar) switch(root.tag)
{ {
@ -61,86 +43,124 @@ void extract(ref Node document)
crawl(document); crawl(document);
} }
void main(string[] args) void main(string[] args) //@safe
{ {
bool get = false; bool get = false;
bool dump = false; bool dump = false;
bool reload = false; bool reload = false;
bool quiet = false;
bool verbose = false;
bool scanOnly = false; bool scanOnly = false;
uint runs = 1; uint runs = 1;
string file = null;
//Parse command line args auto help = getopt(
foreach(arg; args[1 .. $]) args,
{ "get|g", "Extract data from the file (using Node.as()).", &get,
auto parts = arg.split("="); "dump|d", "Dump the loaded data (to YAML_FILE.dump).", &dump,
if(arg[0] == '-') switch(parts[0]) "runs|r", "Repeat parsing the file NUM times.", &runs,
{ "reload", "Reload the file from the diskl on every repeat By default,"~
case "--help", "-h": help(); return; " the file is loaded to memory once and repeatedly parsed from memory.", &reload,
case "--get", "-g": get = true; break; "quiet|q", "Don't print anything.", &quiet,
case "--dump", "-d": dump = true; break; "verbose|v", "Print even more.", &verbose,
case "--reload": reload = true; break; "scan-only|s", "Do not execute the entire parsing process, only scanning. Overrides '--dump'", &scanOnly
case "--scan-only", "-s": scanOnly = true; break; );
case "--runs", "-r": runs = parts[1].to!uint; break;
default: writeln("\nUnknown argument: ", arg, "\n\n"); help(); return;
}
else
{
if(file !is null)
{
writeln("\nUnknown argument or file specified twice: ", arg, "\n\n");
help();
return;
}
file = arg; if (help.helpWanted || (args.length < 2))
}
}
if(file is null)
{ {
writeln("\nFile not specified.\n\n"); defaultGetoptPrinter(
help(); "D:YAML benchmark\n"~
"Copyright (C) 2011-2018 Ferdinand Majerech, Cameron \"Herringway\" Ross\n"~
"Usage: yaml_bench [OPTION ...] [YAML_FILE]\n\n"~
"Loads and optionally extracts data and/or dumps a YAML file.\n",
help.options
);
return; return;
} }
string file = args[1];
auto stopWatch = StopWatch(AutoStart.yes);
void[] fileInMemory;
if(!reload) { fileInMemory = std.file.read(file); }
void[] fileWorkingCopy = fileInMemory.dup;
auto loadTime = stopWatch.peek();
stopWatch.reset();
try try
{ {
import std.file;
void[] fileInMemory;
if(!reload) { fileInMemory = std.file.read(file); }
void[] fileWorkingCopy = fileInMemory.dup;
// Instead of constructing a resolver/constructor with each Loader, // Instead of constructing a resolver/constructor with each Loader,
// construct them once to remove noise when profiling. // construct them once to remove noise when profiling.
auto resolver = new Resolver(); auto resolver = new Resolver();
auto constructor = new Constructor(); auto constructor = new Constructor();
while(runs--) auto constructTime = stopWatch.peek();
Node[] nodes;
void runLoaderBenchmark() //@safe
{ {
// Loading the file rewrites the loaded buffer, so if we don't reload from // Loading the file rewrites the loaded buffer, so if we don't reload from
// disk, we need to use a copy of the originally loaded file. // disk, we need to use a copy of the originally loaded file.
if(reload) { fileInMemory = std.file.read(file); } if(reload) { fileInMemory = std.file.read(file); }
else { fileWorkingCopy[] = fileInMemory[]; } else { fileWorkingCopy[] = fileInMemory[]; }
void[] fileToLoad = reload ? fileInMemory : fileWorkingCopy; void[] fileToLoad = reload ? fileInMemory : fileWorkingCopy;
auto loader = Loader(fileToLoad);
if(scanOnly) if(scanOnly)
{ {
auto loader = Loader(fileToLoad);
loader.scanBench(); loader.scanBench();
continue; return;
} }
auto loader = Loader(fileToLoad);
loader.resolver = resolver; loader.resolver = resolver;
loader.constructor = constructor; loader.constructor = constructor;
auto nodes = loader.loadAll(); nodes = loader.loadAll();
}
void runDumpBenchmark() @safe
{
if(dump) if(dump)
{ {
Dumper(file ~ ".dump").dump(nodes); Dumper(file ~ ".dump").dump(nodes);
} }
}
void runGetBenchmark() @safe
{
if(get) foreach(ref node; nodes) if(get) foreach(ref node; nodes)
{ {
extract(node); extract(node);
} }
} }
auto totalTime = benchmark!(runLoaderBenchmark, runDumpBenchmark, runGetBenchmark)(runs);
if (!quiet)
{
auto enabledOptions =
only(
get ? "Get" : "",
dump ? "Dump" : "",
reload ? "Reload" : "",
scanOnly ? "Scan Only": ""
).filter!(x => x != "");
if (!enabledOptions.empty)
{
writefln!"Options enabled: %-(%s, %)"(enabledOptions);
}
if (verbose)
{
if (!reload)
{
writeln("Time to load file: ", loadTime);
}
writeln("Time to set up resolver & constructor: ", constructTime);
}
writeln("Runs: ", runs);
foreach(time, func, enabled; lockstep(totalTime[], only("Loader", "Dumper", "Get"), only(true, dump, get)))
{
if (enabled)
{
writeln("Average time spent on ", func, ": ", time / runs);
writeln("Total time spent on ", func, ": ", time);
}
}
}
} }
catch(YAMLException e) catch(YAMLException e)
{ {