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