• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
indentindent61   indent(unsigned d) : distance(d) {}
62 };
63 
operator <<(raw_ostream & os,const indent & in)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 !!.
prettyTag(yaml::Node * N)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 
dumpNode(yaml::Node * n,unsigned Indent=0,bool SuppressFirstIndent=false)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 
dumpStream(yaml::Stream & stream)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 
benchmark(llvm::TimerGroup & Group,llvm::StringRef Name,llvm::StringRef Description,llvm::StringRef JSONText)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 
createJSONText(size_t MemoryMB,unsigned ValueSize)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     Stream.flush();
188     if (JSONText.size() < MemoryBytes) Stream << ",";
189     Stream << "\n";
190   }
191   Stream << "]\n";
192   Stream.flush();
193   return JSONText;
194 }
195 
main(int argc,char ** argv)196 int main(int argc, char **argv) {
197   llvm::cl::ParseCommandLineOptions(argc, argv);
198   bool ShowColors = UseColor == cl::BOU_UNSET
199                         ? sys::Process::StandardOutHasColors()
200                         : UseColor == cl::BOU_TRUE;
201   if (Input.getNumOccurrences()) {
202     ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
203         MemoryBuffer::getFileOrSTDIN(Input);
204     if (!BufOrErr)
205       return 1;
206     MemoryBuffer &Buf = *BufOrErr.get();
207 
208     llvm::SourceMgr sm;
209     if (DumpTokens) {
210       yaml::dumpTokens(Buf.getBuffer(), outs());
211     }
212 
213     if (DumpCanonical) {
214       yaml::Stream stream(Buf.getBuffer(), sm, ShowColors);
215       dumpStream(stream);
216       if (stream.failed())
217         return 1;
218     }
219   }
220 
221   if (Verify) {
222     llvm::TimerGroup Group("yaml", "YAML parser benchmark");
223     benchmark(Group, "Fast", "Fast", createJSONText(10, 500));
224   } else if (!DumpCanonical && !DumpTokens) {
225     llvm::TimerGroup Group("yaml", "YAML parser benchmark");
226     benchmark(Group, "Small", "Small Values", createJSONText(MemoryLimitMB, 5));
227     benchmark(Group, "Medium", "Medium Values",
228               createJSONText(MemoryLimitMB, 500));
229     benchmark(Group, "Large", "Large Values",
230               createJSONText(MemoryLimitMB, 50000));
231   }
232 
233   return 0;
234 }
235