1 //===- YAMLBench - Benchmark the YAMLParser implementation ----------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This program executes the YAMLParser on differntly sized YAML texts and 11 // outputs the run time. 12 // 13 //===----------------------------------------------------------------------===// 14 15 16 #include "llvm/ADT/SmallString.h" 17 #include "llvm/Support/Casting.h" 18 #include "llvm/Support/CommandLine.h" 19 #include "llvm/Support/MemoryBuffer.h" 20 #include "llvm/Support/SourceMgr.h" 21 #include "llvm/Support/Timer.h" 22 #include "llvm/Support/YAMLParser.h" 23 #include "llvm/Support/raw_ostream.h" 24 #include "llvm/Support/system_error.h" 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 struct indent { 56 unsigned distance; 57 indent(unsigned d) : distance(d) {} 58 }; 59 60 static raw_ostream &operator <<(raw_ostream &os, const indent &in) { 61 for (unsigned i = 0; i < in.distance; ++i) 62 os << " "; 63 return os; 64 } 65 66 static void dumpNode( yaml::Node *n 67 , unsigned Indent = 0 68 , bool SuppressFirstIndent = false) { 69 if (!n) 70 return; 71 if (!SuppressFirstIndent) 72 outs() << indent(Indent); 73 StringRef Anchor = n->getAnchor(); 74 if (!Anchor.empty()) 75 outs() << "&" << Anchor << " "; 76 if (yaml::ScalarNode *sn = dyn_cast<yaml::ScalarNode>(n)) { 77 SmallString<32> Storage; 78 StringRef Val = sn->getValue(Storage); 79 outs() << "!!str \"" << yaml::escape(Val) << "\""; 80 } else if (yaml::SequenceNode *sn = dyn_cast<yaml::SequenceNode>(n)) { 81 outs() << "!!seq [\n"; 82 ++Indent; 83 for (yaml::SequenceNode::iterator i = sn->begin(), e = sn->end(); 84 i != e; ++i) { 85 dumpNode(i, Indent); 86 outs() << ",\n"; 87 } 88 --Indent; 89 outs() << indent(Indent) << "]"; 90 } else if (yaml::MappingNode *mn = dyn_cast<yaml::MappingNode>(n)) { 91 outs() << "!!map {\n"; 92 ++Indent; 93 for (yaml::MappingNode::iterator i = mn->begin(), e = mn->end(); 94 i != e; ++i) { 95 outs() << indent(Indent) << "? "; 96 dumpNode(i->getKey(), Indent, true); 97 outs() << "\n"; 98 outs() << indent(Indent) << ": "; 99 dumpNode(i->getValue(), Indent, true); 100 outs() << ",\n"; 101 } 102 --Indent; 103 outs() << indent(Indent) << "}"; 104 } else if (yaml::AliasNode *an = dyn_cast<yaml::AliasNode>(n)){ 105 outs() << "*" << an->getName(); 106 } else if (dyn_cast<yaml::NullNode>(n)) { 107 outs() << "!!null null"; 108 } 109 } 110 111 static void dumpStream(yaml::Stream &stream) { 112 for (yaml::document_iterator di = stream.begin(), de = stream.end(); di != de; 113 ++di) { 114 outs() << "%YAML 1.2\n" 115 << "---\n"; 116 yaml::Node *n = di->getRoot(); 117 if (n) 118 dumpNode(n); 119 else 120 break; 121 outs() << "\n...\n"; 122 } 123 } 124 125 static void benchmark( llvm::TimerGroup &Group 126 , llvm::StringRef Name 127 , llvm::StringRef JSONText) { 128 llvm::Timer BaseLine((Name + ": Loop").str(), Group); 129 BaseLine.startTimer(); 130 char C = 0; 131 for (llvm::StringRef::iterator I = JSONText.begin(), 132 E = JSONText.end(); 133 I != E; ++I) { C += *I; } 134 BaseLine.stopTimer(); 135 volatile char DontOptimizeOut = C; (void)DontOptimizeOut; 136 137 llvm::Timer Tokenizing((Name + ": Tokenizing").str(), Group); 138 Tokenizing.startTimer(); 139 { 140 yaml::scanTokens(JSONText); 141 } 142 Tokenizing.stopTimer(); 143 144 llvm::Timer Parsing((Name + ": Parsing").str(), Group); 145 Parsing.startTimer(); 146 { 147 llvm::SourceMgr SM; 148 llvm::yaml::Stream stream(JSONText, SM); 149 stream.skip(); 150 } 151 Parsing.stopTimer(); 152 } 153 154 static std::string createJSONText(size_t MemoryMB, unsigned ValueSize) { 155 std::string JSONText; 156 llvm::raw_string_ostream Stream(JSONText); 157 Stream << "[\n"; 158 size_t MemoryBytes = MemoryMB * 1024 * 1024; 159 while (JSONText.size() < MemoryBytes) { 160 Stream << " {\n" 161 << " \"key1\": \"" << std::string(ValueSize, '*') << "\",\n" 162 << " \"key2\": \"" << std::string(ValueSize, '*') << "\",\n" 163 << " \"key3\": \"" << std::string(ValueSize, '*') << "\"\n" 164 << " }"; 165 Stream.flush(); 166 if (JSONText.size() < MemoryBytes) Stream << ","; 167 Stream << "\n"; 168 } 169 Stream << "]\n"; 170 Stream.flush(); 171 return JSONText; 172 } 173 174 int main(int argc, char **argv) { 175 llvm::cl::ParseCommandLineOptions(argc, argv); 176 if (Input.getNumOccurrences()) { 177 OwningPtr<MemoryBuffer> Buf; 178 if (MemoryBuffer::getFileOrSTDIN(Input, Buf)) 179 return 1; 180 181 llvm::SourceMgr sm; 182 if (DumpTokens) { 183 yaml::dumpTokens(Buf->getBuffer(), outs()); 184 } 185 186 if (DumpCanonical) { 187 yaml::Stream stream(Buf->getBuffer(), sm); 188 dumpStream(stream); 189 } 190 } 191 192 if (Verify) { 193 llvm::TimerGroup Group("YAML parser benchmark"); 194 benchmark(Group, "Fast", createJSONText(10, 500)); 195 } else if (!DumpCanonical && !DumpTokens) { 196 llvm::TimerGroup Group("YAML parser benchmark"); 197 benchmark(Group, "Small Values", createJSONText(MemoryLimitMB, 5)); 198 benchmark(Group, "Medium Values", createJSONText(MemoryLimitMB, 500)); 199 benchmark(Group, "Large Values", createJSONText(MemoryLimitMB, 50000)); 200 } 201 202 return 0; 203 } 204