1 //===- YAMLBench - Benchmark the YAMLParser implementation ----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This program executes the YAMLParser on differently sized YAML texts and 10 // outputs the run time. 11 // 12 //===----------------------------------------------------------------------===// 13 14 15 #include "llvm/ADT/SmallString.h" 16 #include "llvm/Support/Casting.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/MemoryBuffer.h" 19 #include "llvm/Support/SourceMgr.h" 20 #include "llvm/Support/Timer.h" 21 #include "llvm/Support/Process.h" 22 #include "llvm/Support/YAMLParser.h" 23 #include "llvm/Support/raw_ostream.h" 24 #include <system_error> 25 26 using namespace llvm; 27 28 static cl::opt<bool> 29 DumpTokens( "tokens" 30 , cl::desc("Print the tokenization of the file.") 31 , cl::init(false) 32 ); 33 34 static cl::opt<bool> 35 DumpCanonical( "canonical" 36 , cl::desc("Print the canonical YAML for this file.") 37 , cl::init(false) 38 ); 39 40 static cl::opt<std::string> 41 Input(cl::Positional, cl::desc("<input>")); 42 43 static cl::opt<bool> 44 Verify( "verify" 45 , cl::desc( 46 "Run a quick verification useful for regression testing") 47 , cl::init(false) 48 ); 49 50 static cl::opt<unsigned> 51 MemoryLimitMB("memory-limit", cl::desc( 52 "Do not use more megabytes of memory"), 53 cl::init(1000)); 54 55 cl::opt<cl::boolOrDefault> 56 UseColor("use-color", cl::desc("Emit colored output (default=autodetect)"), 57 cl::init(cl::BOU_UNSET)); 58 59 struct indent { 60 unsigned distance; 61 indent(unsigned d) : distance(d) {} 62 }; 63 64 static raw_ostream &operator <<(raw_ostream &os, const indent &in) { 65 for (unsigned i = 0; i < in.distance; ++i) 66 os << " "; 67 return os; 68 } 69 70 /// Pretty print a tag by replacing tag:yaml.org,2002: with !!. 71 static std::string prettyTag(yaml::Node *N) { 72 std::string Tag = N->getVerbatimTag(); 73 if (StringRef(Tag).startswith("tag:yaml.org,2002:")) { 74 std::string Ret = "!!"; 75 Ret += StringRef(Tag).substr(18); 76 return Ret; 77 } 78 std::string Ret = "!<"; 79 Ret += Tag; 80 Ret += ">"; 81 return Ret; 82 } 83 84 static void dumpNode( yaml::Node *n 85 , unsigned Indent = 0 86 , bool SuppressFirstIndent = false) { 87 if (!n) 88 return; 89 if (!SuppressFirstIndent) 90 outs() << indent(Indent); 91 StringRef Anchor = n->getAnchor(); 92 if (!Anchor.empty()) 93 outs() << "&" << Anchor << " "; 94 if (yaml::ScalarNode *sn = dyn_cast<yaml::ScalarNode>(n)) { 95 SmallString<32> Storage; 96 StringRef Val = sn->getValue(Storage); 97 outs() << prettyTag(n) << " \"" << yaml::escape(Val) << "\""; 98 } else if (yaml::BlockScalarNode *BN = dyn_cast<yaml::BlockScalarNode>(n)) { 99 outs() << prettyTag(n) << " \"" << yaml::escape(BN->getValue()) << "\""; 100 } else if (yaml::SequenceNode *sn = dyn_cast<yaml::SequenceNode>(n)) { 101 outs() << prettyTag(n) << " [\n"; 102 ++Indent; 103 for (yaml::SequenceNode::iterator i = sn->begin(), e = sn->end(); 104 i != e; ++i) { 105 dumpNode(i, Indent); 106 outs() << ",\n"; 107 } 108 --Indent; 109 outs() << indent(Indent) << "]"; 110 } else if (yaml::MappingNode *mn = dyn_cast<yaml::MappingNode>(n)) { 111 outs() << prettyTag(n) << " {\n"; 112 ++Indent; 113 for (yaml::MappingNode::iterator i = mn->begin(), e = mn->end(); 114 i != e; ++i) { 115 outs() << indent(Indent) << "? "; 116 dumpNode(i->getKey(), Indent, true); 117 outs() << "\n"; 118 outs() << indent(Indent) << ": "; 119 dumpNode(i->getValue(), Indent, true); 120 outs() << ",\n"; 121 } 122 --Indent; 123 outs() << indent(Indent) << "}"; 124 } else if (yaml::AliasNode *an = dyn_cast<yaml::AliasNode>(n)){ 125 outs() << "*" << an->getName(); 126 } else if (isa<yaml::NullNode>(n)) { 127 outs() << prettyTag(n) << " null"; 128 } 129 } 130 131 static void dumpStream(yaml::Stream &stream) { 132 for (yaml::document_iterator di = stream.begin(), de = stream.end(); di != de; 133 ++di) { 134 outs() << "%YAML 1.2\n" 135 << "---\n"; 136 yaml::Node *n = di->getRoot(); 137 if (n) 138 dumpNode(n); 139 else 140 break; 141 outs() << "\n...\n"; 142 } 143 } 144 145 static void benchmark(llvm::TimerGroup &Group, llvm::StringRef Name, 146 llvm::StringRef Description, llvm::StringRef JSONText) { 147 llvm::Timer BaseLine((Name + ".loop").str(), (Description + ": Loop").str(), 148 Group); 149 BaseLine.startTimer(); 150 char C = 0; 151 for (llvm::StringRef::iterator I = JSONText.begin(), 152 E = JSONText.end(); 153 I != E; ++I) { C += *I; } 154 BaseLine.stopTimer(); 155 volatile char DontOptimizeOut = C; (void)DontOptimizeOut; 156 157 llvm::Timer Tokenizing((Name + ".tokenizing").str(), 158 (Description + ": Tokenizing").str(), Group); 159 Tokenizing.startTimer(); 160 { 161 yaml::scanTokens(JSONText); 162 } 163 Tokenizing.stopTimer(); 164 165 llvm::Timer Parsing((Name + ".parsing").str(), 166 (Description + ": Parsing").str(), Group); 167 Parsing.startTimer(); 168 { 169 llvm::SourceMgr SM; 170 llvm::yaml::Stream stream(JSONText, SM); 171 stream.skip(); 172 } 173 Parsing.stopTimer(); 174 } 175 176 static std::string createJSONText(size_t MemoryMB, unsigned ValueSize) { 177 std::string JSONText; 178 llvm::raw_string_ostream Stream(JSONText); 179 Stream << "[\n"; 180 size_t MemoryBytes = MemoryMB * 1024 * 1024; 181 while (JSONText.size() < MemoryBytes) { 182 Stream << " {\n" 183 << " \"key1\": \"" << std::string(ValueSize, '*') << "\",\n" 184 << " \"key2\": \"" << std::string(ValueSize, '*') << "\",\n" 185 << " \"key3\": \"" << std::string(ValueSize, '*') << "\"\n" 186 << " }"; 187 if (JSONText.size() < MemoryBytes) Stream << ","; 188 Stream << "\n"; 189 } 190 Stream << "]\n"; 191 return JSONText; 192 } 193 194 int main(int argc, char **argv) { 195 llvm::cl::ParseCommandLineOptions(argc, argv); 196 bool ShowColors = UseColor == cl::BOU_UNSET 197 ? sys::Process::StandardOutHasColors() 198 : UseColor == cl::BOU_TRUE; 199 if (Input.getNumOccurrences()) { 200 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 201 MemoryBuffer::getFileOrSTDIN(Input); 202 if (!BufOrErr) 203 return 1; 204 MemoryBuffer &Buf = *BufOrErr.get(); 205 206 llvm::SourceMgr sm; 207 if (DumpTokens) { 208 yaml::dumpTokens(Buf.getBuffer(), outs()); 209 } 210 211 if (DumpCanonical) { 212 yaml::Stream stream(Buf.getBuffer(), sm, ShowColors); 213 dumpStream(stream); 214 if (stream.failed()) 215 return 1; 216 } 217 } 218 219 if (Verify) { 220 llvm::TimerGroup Group("yaml", "YAML parser benchmark"); 221 benchmark(Group, "Fast", "Fast", createJSONText(10, 500)); 222 } else if (!DumpCanonical && !DumpTokens) { 223 llvm::TimerGroup Group("yaml", "YAML parser benchmark"); 224 benchmark(Group, "Small", "Small Values", createJSONText(MemoryLimitMB, 5)); 225 benchmark(Group, "Medium", "Medium Values", 226 createJSONText(MemoryLimitMB, 500)); 227 benchmark(Group, "Large", "Large Values", 228 createJSONText(MemoryLimitMB, 50000)); 229 } 230 231 return 0; 232 } 233