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