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