xref: /llvm-project/llvm/utils/yaml-bench/YAMLBench.cpp (revision 2731be7ac505f9ef2e90b77b84ef0fbe411bf9f5)
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 /// Pretty print a tag by replacing tag:yaml.org,2002: with !!.
60 static std::string prettyTag(yaml::Node *N) {
61   std::string Tag = N->getVerbatimTag();
62   if (StringRef(Tag).starts_with("tag:yaml.org,2002:")) {
63     std::string Ret = "!!";
64     Ret += StringRef(Tag).substr(18);
65     return Ret;
66   }
67   std::string Ret = "!<";
68   Ret += Tag;
69   Ret += ">";
70   return Ret;
71 }
72 
73 static void dumpNode( yaml::Node *n
74                     , unsigned Indent = 0
75                     , bool SuppressFirstIndent = false) {
76   if (!n)
77     return;
78   if (!SuppressFirstIndent)
79     outs() << indent(Indent);
80   StringRef Anchor = n->getAnchor();
81   if (!Anchor.empty())
82     outs() << "&" << Anchor << " ";
83   if (yaml::ScalarNode *sn = dyn_cast<yaml::ScalarNode>(n)) {
84     SmallString<32> Storage;
85     StringRef Val = sn->getValue(Storage);
86     outs() << prettyTag(n) << " \"" << yaml::escape(Val) << "\"";
87   } else if (yaml::BlockScalarNode *BN = dyn_cast<yaml::BlockScalarNode>(n)) {
88     outs() << prettyTag(n) << " \"" << yaml::escape(BN->getValue()) << "\"";
89   } else if (yaml::SequenceNode *sn = dyn_cast<yaml::SequenceNode>(n)) {
90     outs() << prettyTag(n) << " [\n";
91     ++Indent;
92     for (yaml::SequenceNode::iterator i = sn->begin(), e = sn->end();
93                                       i != e; ++i) {
94       dumpNode(i, Indent);
95       outs() << ",\n";
96     }
97     --Indent;
98     outs() << indent(Indent) << "]";
99   } else if (yaml::MappingNode *mn = dyn_cast<yaml::MappingNode>(n)) {
100     outs() << prettyTag(n) << " {\n";
101     ++Indent;
102     for (yaml::MappingNode::iterator i = mn->begin(), e = mn->end();
103                                      i != e; ++i) {
104       outs() << indent(Indent) << "? ";
105       dumpNode(i->getKey(), Indent, true);
106       outs() << "\n";
107       outs() << indent(Indent) << ": ";
108       dumpNode(i->getValue(), Indent, true);
109       outs() << ",\n";
110     }
111     --Indent;
112     outs() << indent(Indent) << "}";
113   } else if (yaml::AliasNode *an = dyn_cast<yaml::AliasNode>(n)){
114     outs() << "*" << an->getName();
115   } else if (isa<yaml::NullNode>(n)) {
116     outs() << prettyTag(n) << " null";
117   }
118 }
119 
120 static void dumpStream(yaml::Stream &stream) {
121   for (yaml::document_iterator di = stream.begin(), de = stream.end(); di != de;
122        ++di) {
123     outs() << "%YAML 1.2\n"
124            << "---\n";
125     yaml::Node *n = di->getRoot();
126     if (n)
127       dumpNode(n);
128     else
129       break;
130     outs() << "\n...\n";
131   }
132 }
133 
134 static void benchmark(llvm::TimerGroup &Group, llvm::StringRef Name,
135                       llvm::StringRef Description, llvm::StringRef JSONText) {
136   llvm::Timer BaseLine((Name + ".loop").str(), (Description + ": Loop").str(),
137                        Group);
138   BaseLine.startTimer();
139   char C = 0;
140   for (llvm::StringRef::iterator I = JSONText.begin(),
141                                  E = JSONText.end();
142        I != E; ++I) { C += *I; }
143   BaseLine.stopTimer();
144   volatile char DontOptimizeOut = C; (void)DontOptimizeOut;
145 
146   llvm::Timer Tokenizing((Name + ".tokenizing").str(),
147                          (Description + ": Tokenizing").str(), Group);
148   Tokenizing.startTimer();
149   {
150     yaml::scanTokens(JSONText);
151   }
152   Tokenizing.stopTimer();
153 
154   llvm::Timer Parsing((Name + ".parsing").str(),
155                       (Description + ": Parsing").str(), Group);
156   Parsing.startTimer();
157   {
158     llvm::SourceMgr SM;
159     llvm::yaml::Stream stream(JSONText, SM);
160     stream.skip();
161   }
162   Parsing.stopTimer();
163 }
164 
165 static std::string createJSONText(size_t MemoryMB, unsigned ValueSize) {
166   std::string JSONText;
167   llvm::raw_string_ostream Stream(JSONText);
168   Stream << "[\n";
169   size_t MemoryBytes = MemoryMB * 1024 * 1024;
170   while (JSONText.size() < MemoryBytes) {
171     Stream << " {\n"
172            << "  \"key1\": \"" << std::string(ValueSize, '*') << "\",\n"
173            << "  \"key2\": \"" << std::string(ValueSize, '*') << "\",\n"
174            << "  \"key3\": \"" << std::string(ValueSize, '*') << "\"\n"
175            << " }";
176     if (JSONText.size() < MemoryBytes) Stream << ",";
177     Stream << "\n";
178   }
179   Stream << "]\n";
180   return JSONText;
181 }
182 
183 int main(int argc, char **argv) {
184   llvm::cl::ParseCommandLineOptions(argc, argv);
185   bool ShowColors = UseColor == cl::BOU_UNSET
186                         ? sys::Process::StandardOutHasColors()
187                         : UseColor == cl::BOU_TRUE;
188   if (Input.getNumOccurrences()) {
189     ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
190         MemoryBuffer::getFileOrSTDIN(Input);
191     if (!BufOrErr)
192       return 1;
193     MemoryBuffer &Buf = *BufOrErr.get();
194 
195     llvm::SourceMgr sm;
196     if (DumpTokens) {
197       yaml::dumpTokens(Buf.getBuffer(), outs());
198     }
199 
200     if (DumpCanonical) {
201       yaml::Stream stream(Buf.getBuffer(), sm, ShowColors);
202       dumpStream(stream);
203       if (stream.failed())
204         return 1;
205     }
206   }
207 
208   if (Verify) {
209     llvm::TimerGroup Group("yaml", "YAML parser benchmark");
210     benchmark(Group, "Fast", "Fast", createJSONText(10, 500));
211   } else if (!DumpCanonical && !DumpTokens) {
212     llvm::TimerGroup Group("yaml", "YAML parser benchmark");
213     benchmark(Group, "Small", "Small Values", createJSONText(MemoryLimitMB, 5));
214     benchmark(Group, "Medium", "Medium Values",
215               createJSONText(MemoryLimitMB, 500));
216     benchmark(Group, "Large", "Large Values",
217               createJSONText(MemoryLimitMB, 50000));
218   }
219 
220   return 0;
221 }
222