diff --git a/examples/yaml_bench/yaml_bench.d b/examples/yaml_bench/yaml_bench.d index f203895..44ac46d 100644 --- a/examples/yaml_bench/yaml_bench.d +++ b/examples/yaml_bench/yaml_bench.d @@ -2,39 +2,21 @@ module dyaml.yaml_bench; //Benchmark that loads, and optionally extracts data from and/or emits a YAML file. +import std.algorithm; 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.string; 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. -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) { @@ -61,86 +43,124 @@ void extract(ref Node document) crawl(document); } -void main(string[] args) +void main(string[] args) //@safe { - bool get = false; - bool dump = false; - bool reload = false; + bool get = false; + bool dump = false; + bool reload = false; + bool quiet = false; + bool verbose = false; bool scanOnly = false; uint runs = 1; - string file = null; - //Parse command line args - foreach(arg; args[1 .. $]) - { - auto parts = arg.split("="); - if(arg[0] == '-') switch(parts[0]) - { - case "--help", "-h": help(); return; - case "--get", "-g": get = true; break; - case "--dump", "-d": dump = true; break; - case "--reload": reload = true; break; - 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; - } + auto help = getopt( + args, + "get|g", "Extract data from the file (using Node.as()).", &get, + "dump|d", "Dump the loaded data (to YAML_FILE.dump).", &dump, + "runs|r", "Repeat parsing the file NUM times.", &runs, + "reload", "Reload the file from the diskl on every repeat By default,"~ + " the file is loaded to memory once and repeatedly parsed from memory.", &reload, + "quiet|q", "Don't print anything.", &quiet, + "verbose|v", "Print even more.", &verbose, + "scan-only|s", "Do not execute the entire parsing process, only scanning. Overrides '--dump'", &scanOnly + ); - file = arg; - } - } - if(file is null) + if (help.helpWanted || (args.length < 2)) { - writeln("\nFile not specified.\n\n"); - help(); + defaultGetoptPrinter( + "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; } + 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 { - 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, // construct them once to remove noise when profiling. auto resolver = new Resolver(); 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 // disk, we need to use a copy of the originally loaded file. if(reload) { fileInMemory = std.file.read(file); } else { fileWorkingCopy[] = fileInMemory[]; } void[] fileToLoad = reload ? fileInMemory : fileWorkingCopy; + + auto loader = Loader(fileToLoad); if(scanOnly) { - auto loader = Loader(fileToLoad); loader.scanBench(); - continue; + return; } - auto loader = Loader(fileToLoad); + loader.resolver = resolver; loader.constructor = constructor; - auto nodes = loader.loadAll(); + nodes = loader.loadAll(); + } + void runDumpBenchmark() @safe + { if(dump) { Dumper(file ~ ".dump").dump(nodes); } + } + void runGetBenchmark() @safe + { if(get) foreach(ref node; nodes) { 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) {