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