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 }