1
2 module dyaml.yaml_bench;
3 //Benchmark that loads, and optionally extracts data from and/or emits a YAML file.
4
5 import std.algorithm;
6 import std.conv;
7 import std.datetime.systime;
8 import std.datetime.stopwatch;
9 import std.file;
10 import std.getopt;
11 import std.range;
12 import std.stdio;
13 import std.string;
14 import dyaml;
15
16 ///Get data out of every node.
17 void extract(ref Node document) @safe
18 {
19 void crawl(ref Node root) @safe
20 {
21 final switch (root.nodeID)
22 {
23 case NodeID.scalar:
24 switch(root.tag)
25 {
26 case "tag:yaml.org,2002:null": auto value = root.as!YAMLNull; break;
27 case "tag:yaml.org,2002:bool": auto value = root.as!bool; break;
28 case "tag:yaml.org,2002:int": auto value = root.as!long; break;
29 case "tag:yaml.org,2002:float": auto value = root.as!real; break;
30 case "tag:yaml.org,2002:binary": auto value = root.as!(ubyte[]); break;
31 case "tag:yaml.org,2002:timestamp": auto value = root.as!SysTime; break;
32 case "tag:yaml.org,2002:str": auto value = root.as!string; break;
33 default: writeln("Unrecognozed tag: ", root.tag);
34 }
35 break;
36 case NodeID.sequence:
37 foreach(ref Node node; root)
38 {
39 crawl(node);
40 }
41 break;
42 case NodeID.mapping:
43 foreach(ref Node key, ref Node value; root)
44 {
45 crawl(key);
46 crawl(value);
47 }
48 break;
49 case NodeID.invalid:
50 assert(0);
51 }
52 }
53
54 crawl(document);
55 }
56
57 void main(string[] args) //@safe
58 {
59 import std.array : array;
60 bool get = false;
61 bool dump = false;
62 bool reload = false;
63 bool quiet = false;
64 bool verbose = false;
65 bool scanOnly = false;
66 uint runs = 1;
67
68 auto help = getopt(
69 args,
70 "get|g", "Extract data from the file (using Node.as()).", &get,
71 "dump|d", "Dump the loaded data (to YAML_FILE.dump).", &dump,
72 "runs|r", "Repeat parsing the file NUM times.", &runs,
73 "reload", "Reload the file from the diskl on every repeat By default,"~
74 " the file is loaded to memory once and repeatedly parsed from memory.", &reload,
75 "quiet|q", "Don't print anything.", &quiet,
76 "verbose|v", "Print even more.", &verbose,
77 "scan-only|s", "Do not execute the entire parsing process, only scanning. Overrides '--dump'", &scanOnly
78 );
79
80 if (help.helpWanted || (args.length < 2))
81 {
82 defaultGetoptPrinter(
83 "D:YAML benchmark\n"~
84 "Copyright (C) 2011-2018 Ferdinand Majerech, Cameron \"Herringway\" Ross\n"~
85 "Usage: yaml_bench [OPTION ...] [YAML_FILE]\n\n"~
86 "Loads and optionally extracts data and/or dumps a YAML file.\n",
87 help.options
88 );
89 return;
90 }
91
92 string file = args[1];
93
94 auto stopWatch = StopWatch(AutoStart.yes);
95 void[] fileInMemory;
96 if(!reload) { fileInMemory = std.file.read(file); }
97 void[] fileWorkingCopy = fileInMemory.dup;
98 auto loadTime = stopWatch.peek();
99 stopWatch.reset();
100 try
101 {
102 // Instead of constructing a resolver/constructor with each Loader,
103 // construct them once to remove noise when profiling.
104 auto resolver = Resolver.withDefaultResolvers;
105
106 auto constructTime = stopWatch.peek();
107
108 Node[] nodes;
109
110 void runLoaderBenchmark() //@safe
111 {
112 // Loading the file rewrites the loaded buffer, so if we don't reload from
113 // disk, we need to use a copy of the originally loaded file.
114 if(reload) { fileInMemory = std.file.read(file); }
115 else { fileWorkingCopy[] = fileInMemory[]; }
116 void[] fileToLoad = reload ? fileInMemory : fileWorkingCopy;
117
118 auto loader = Loader.fromBuffer(fileToLoad);
119 if(scanOnly)
120 {
121 loader.scanBench();
122 return;
123 }
124
125 loader.resolver = resolver;
126 nodes = loader.array;
127 }
128 void runDumpBenchmark() @safe
129 {
130 if(dump)
131 {
132 dumper().dump(File(file ~ ".dump", "w").lockingTextWriter, nodes);
133 }
134 }
135 void runGetBenchmark() @safe
136 {
137 if(get) foreach(ref node; nodes)
138 {
139 extract(node);
140 }
141 }
142 auto totalTime = benchmark!(runLoaderBenchmark, runDumpBenchmark, runGetBenchmark)(runs);
143 if (!quiet)
144 {
145 auto enabledOptions =
146 only(
147 get ? "Get" : "",
148 dump ? "Dump" : "",
149 reload ? "Reload" : "",
150 scanOnly ? "Scan Only": ""
151 ).filter!(x => x != "");
152 if (!enabledOptions.empty)
153 {
154 writefln!"Options enabled: %-(%s, %)"(enabledOptions);
155 }
156 if (verbose)
157 {
158 if (!reload)
159 {
160 writeln("Time to load file: ", loadTime);
161 }
162 writeln("Time to set up resolver: ", constructTime);
163 }
164 writeln("Runs: ", runs);
165 foreach(time, func, enabled; lockstep(totalTime[], only("Loader", "Dumper", "Get"), only(true, dump, get)))
166 {
167 if (enabled)
168 {
169 writeln("Average time spent on ", func, ": ", time / runs);
170 writeln("Total time spent on ", func, ": ", time);
171 }
172 }
173 }
174 }
175 catch(YAMLException e)
176 {
177 writeln("ERROR: ", e.msg);
178 }
179 }