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
26using namespace llvm;
27
28static cl::opt<bool>
29 DumpTokens( "tokens"
30 , cl::desc("Print the tokenization of the file.")
31 , cl::init(Val: false)
32 );
33
34static cl::opt<bool>
35 DumpCanonical( "canonical"
36 , cl::desc("Print the canonical YAML for this file.")
37 , cl::init(Val: false)
38 );
39
40static cl::opt<std::string>
41 Input(cl::Positional, cl::desc("<input>"));
42
43static cl::opt<bool>
44 Verify( "verify"
45 , cl::desc(
46 "Run a quick verification useful for regression testing")
47 , cl::init(Val: false)
48 );
49
50static cl::opt<unsigned>
51 MemoryLimitMB("memory-limit", cl::desc(
52 "Do not use more megabytes of memory"),
53 cl::init(Val: 1000));
54
55static cl::opt<cl::boolOrDefault>
56 UseColor("use-color", cl::desc("Emit colored output (default=autodetect)"),
57 cl::init(Val: cl::BOU_UNSET));
58
59/// Pretty print a tag by replacing tag:yaml.org,2002: with !!.
60static std::string prettyTag(yaml::Node *N) {
61 std::string Tag = N->getVerbatimTag();
62 if (StringRef(Tag).starts_with(Prefix: "tag:yaml.org,2002:")) {
63 std::string Ret = "!!";
64 Ret += StringRef(Tag).substr(Start: 18);
65 return Ret;
66 }
67 std::string Ret = "!<";
68 Ret += Tag;
69 Ret += ">";
70 return Ret;
71}
72
73static 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>(Val: n)) {
84 SmallString<32> Storage;
85 StringRef Val = sn->getValue(Storage);
86 outs() << prettyTag(N: n) << " \"" << yaml::escape(Input: Val) << "\"";
87 } else if (yaml::BlockScalarNode *BN = dyn_cast<yaml::BlockScalarNode>(Val: n)) {
88 outs() << prettyTag(N: n) << " \"" << yaml::escape(Input: BN->getValue()) << "\"";
89 } else if (yaml::SequenceNode *sn = dyn_cast<yaml::SequenceNode>(Val: n)) {
90 outs() << prettyTag(N: n) << " [\n";
91 ++Indent;
92 for (yaml::SequenceNode::iterator i = sn->begin(), e = sn->end();
93 i != e; ++i) {
94 dumpNode(n: i, Indent);
95 outs() << ",\n";
96 }
97 --Indent;
98 outs() << indent(Indent) << "]";
99 } else if (yaml::MappingNode *mn = dyn_cast<yaml::MappingNode>(Val: n)) {
100 outs() << prettyTag(N: 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(n: i->getKey(), Indent, SuppressFirstIndent: true);
106 outs() << "\n";
107 outs() << indent(Indent) << ": ";
108 dumpNode(n: i->getValue(), Indent, SuppressFirstIndent: true);
109 outs() << ",\n";
110 }
111 --Indent;
112 outs() << indent(Indent) << "}";
113 } else if (yaml::AliasNode *an = dyn_cast<yaml::AliasNode>(Val: n)){
114 outs() << "*" << an->getName();
115 } else if (isa<yaml::NullNode>(Val: n)) {
116 outs() << prettyTag(N: n) << " null";
117 }
118}
119
120static 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
134static 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(Input: 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
165static 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
183int 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(Filename: Input);
191 if (!BufOrErr)
192 return 1;
193 MemoryBuffer &Buf = *BufOrErr.get();
194
195 llvm::SourceMgr sm;
196 if (DumpTokens) {
197 yaml::dumpTokens(Input: 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, Name: "Fast", Description: "Fast", JSONText: createJSONText(MemoryMB: 10, ValueSize: 500));
211 } else if (!DumpCanonical && !DumpTokens) {
212 llvm::TimerGroup Group("yaml", "YAML parser benchmark");
213 benchmark(Group, Name: "Small", Description: "Small Values", JSONText: createJSONText(MemoryMB: MemoryLimitMB, ValueSize: 5));
214 benchmark(Group, Name: "Medium", Description: "Medium Values",
215 JSONText: createJSONText(MemoryMB: MemoryLimitMB, ValueSize: 500));
216 benchmark(Group, Name: "Large", Description: "Large Values",
217 JSONText: createJSONText(MemoryMB: MemoryLimitMB, ValueSize: 50000));
218 }
219
220 return 0;
221}
222