Home | History | Annotate | Download | only in yaml-bench
      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 
     27 using namespace llvm;
     28 
     29 static cl::opt<bool>
     30   DumpTokens( "tokens"
     31             , cl::desc("Print the tokenization of the file.")
     32             , cl::init(false)
     33             );
     34 
     35 static cl::opt<bool>
     36   DumpCanonical( "canonical"
     37                , cl::desc("Print the canonical YAML for this file.")
     38                , cl::init(false)
     39                );
     40 
     41 static cl::opt<std::string>
     42  Input(cl::Positional, cl::desc("<input>"));
     43 
     44 static cl::opt<bool>
     45   Verify( "verify"
     46         , cl::desc(
     47             "Run a quick verification useful for regression testing")
     48         , cl::init(false)
     49         );
     50 
     51 static cl::opt<unsigned>
     52   MemoryLimitMB("memory-limit", cl::desc(
     53                   "Do not use more megabytes of memory"),
     54                 cl::init(1000));
     55 
     56 cl::opt<cl::boolOrDefault>
     57     UseColor("use-color", cl::desc("Emit colored output (default=autodetect)"),
     58              cl::init(cl::BOU_UNSET));
     59 
     60 struct indent {
     61   unsigned distance;
     62   indent(unsigned d) : distance(d) {}
     63 };
     64 
     65 static 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 !!.
     72 static 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 
     85 static 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 
    132 static 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 
    146 static 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 
    175 static 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 
    195 int 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