1 #include "event_counter.h"
2 
3 #include <cassert>
4 #include <cctype>
5 #ifndef _MSC_VER
6 #include <dirent.h>
7 #endif
8 #include <unistd.h>
9 #include <cinttypes>
10 
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 
15 #include <algorithm>
16 #include <chrono>
17 #include <cstring>
18 #include <fstream>
19 #include <iomanip>
20 #include <iostream>
21 #include <map>
22 #include <set>
23 #include <sstream>
24 #include <string>
25 #include <vector>
26 
27 #include "linux-perf-events.h"
28 #ifdef __linux__
29 #include <libgen.h>
30 #endif
31 
32 #include "simdjson.h"
33 
34 #include <functional>
35 
36 #include "benchmarker.h"
37 
38 using namespace simdjson;
39 using std::cerr;
40 using std::cout;
41 using std::endl;
42 using std::string;
43 using std::to_string;
44 using std::vector;
45 using std::ostream;
46 using std::ofstream;
47 using std::exception;
48 
49 // Stash the exe_name in main() for functions to use
50 char* exe_name;
51 
print_usage(ostream & out)52 void print_usage(ostream& out) {
53   out << "Usage: " << exe_name << " [-vt] [-n #] [-s STAGE] [-a ARCH] <jsonfile> ..." << endl;
54   out << endl;
55   out << "Runs the parser against the given json files in a loop, measuring speed and other statistics." << endl;
56   out << endl;
57   out << "Options:" << endl;
58   out << endl;
59   out << "-n #         - Number of iterations per file. Default: 200" << endl;
60   out << "-i #         - Number of times to iterate a single file before moving to the next. Default: 20" << endl;
61   out << "-t           - Tabbed data output" << endl;
62   out << "-v           - Verbose output." << endl;
63   out << "-s stage1    - Stop after find_structural_bits." << endl;
64   out << "-s all       - Run all stages." << endl;
65   out << "-C           - Leave the buffers cold (includes page allocation and related OS tasks during parsing, speed tied to OS performance)" << endl;
66   out << "-H           - Make the buffers hot (reduce page allocation and related OS tasks during parsing) [default]" << endl;
67   out << "-a IMPL      - Use the given parser implementation. By default, detects the most advanced" << endl;
68   out << "               implementation supported on the host machine." << endl;
69   for (auto impl : simdjson::available_implementations) {
70     if(impl->supported_by_runtime_system()) {
71       out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << endl;
72     }
73   }
74 }
75 
exit_usage(string message)76 void exit_usage(string message) {
77   cerr << message << endl;
78   cerr << endl;
79   print_usage(cerr);
80   exit(EXIT_FAILURE);
81 }
82 
83 struct option_struct {
84   vector<char*> files{};
85   bool stage1_only = false;
86 
87   int32_t iterations = 200;
88   int32_t iteration_step = -1;
89 
90   bool verbose = false;
91   bool tabbed_output = false;
92   /**
93    * Benchmarking on a cold parser instance means that the parsing may include
94    * memory allocation at the OS level. This may lead to apparently odd results
95    * such that higher speed under the Windows Subsystem for Linux than under the
96    * regular Windows, for the same machine. It is arguably misleading to benchmark
97    * how the OS allocates memory, when we really want to just benchmark simdjson.
98    */
99   bool hotbuffers = true;
100 
option_structoption_struct101   option_struct(int argc, char **argv) {
102     int c;
103 
104     while ((c = getopt(argc, argv, "vtn:i:a:s:HC")) != -1) {
105       switch (c) {
106       case 'n':
107         iterations = atoi(optarg);
108         break;
109       case 'i':
110         iteration_step = atoi(optarg);
111         break;
112       case 't':
113         tabbed_output = true;
114         break;
115       case 'v':
116         verbose = true;
117         break;
118       case 'a': {
119         const implementation *impl = simdjson::available_implementations[optarg];
120         if ((!impl) || (!impl->supported_by_runtime_system())) {
121           std::string exit_message = string("Unsupported option value -a ") + optarg + ": expected -a  with one of ";
122           for (auto imple : simdjson::available_implementations) {
123             if(imple->supported_by_runtime_system()) {
124               exit_message += imple->name();
125               exit_message += " ";
126             }
127           }
128           exit_usage(exit_message);
129         }
130         simdjson::active_implementation = impl;
131         break;
132       }
133       case 'C':
134         hotbuffers = false;
135         break;
136       case 'H':
137         hotbuffers = true;
138         break;
139       case 's':
140         if (!strcmp(optarg, "stage1")) {
141           stage1_only = true;
142         } else if (!strcmp(optarg, "all")) {
143           stage1_only = false;
144         } else {
145           exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
146         }
147         break;
148       default:
149         // reaching here means an argument was given to getopt() which did not have a case label
150         exit_usage("Unexpected argument - missing case for option "+
151                     std::string(1,static_cast<char>(c))+
152                     " (programming error)");
153       }
154     }
155 
156     if (iteration_step == -1) {
157       iteration_step = iterations / 50;
158       if (iteration_step < 200) { iteration_step = 200; }
159       if (iteration_step > iterations) { iteration_step = iterations; }
160     }
161 
162     // All remaining arguments are considered to be files
163     for (int i=optind; i<argc; i++) {
164       files.push_back(argv[i]);
165     }
166     if (files.empty()) {
167       exit_usage("No files specified");
168     }
169   }
170 };
171 
main(int argc,char * argv[])172 int main(int argc, char *argv[]) {
173   // Read options
174   exe_name = argv[0];
175   option_struct options(argc, argv);
176   if (options.verbose) {
177     verbose_stream = &cout;
178     verbose() << "Implementation: " << simdjson::active_implementation->name() << endl;
179   }
180 
181   // Start collecting events. We put this early so if it prints an error message, it's the
182   // first thing printed.
183   event_collector collector;
184 
185   // Print preamble
186   if (!options.tabbed_output) {
187     printf("number of iterations %u \n", options.iterations);
188   }
189 
190   // Set up benchmarkers by reading all files
191   vector<benchmarker*> benchmarkers;
192   for (size_t i=0; i<options.files.size(); i++) {
193     benchmarkers.push_back(new benchmarker(options.files[i], collector));
194   }
195 
196   // Run the benchmarks
197   progress_bar progress(options.iterations, 50);
198   // Put the if (options.stage1_only) *outside* the loop so that run_iterations will be optimized
199   if (options.stage1_only) {
200     for (int iteration = 0; iteration < options.iterations; iteration += options.iteration_step) {
201       if (!options.verbose) { progress.print(iteration); }
202       // Benchmark each file once per iteration
203       for (size_t f=0; f<options.files.size(); f++) {
204         verbose() << "[verbose] " << benchmarkers[f]->filename << " iterations #" << iteration << "-" << (iteration+options.iteration_step-1) << endl;
205         benchmarkers[f]->run_iterations(options.iteration_step, true, options.hotbuffers);
206       }
207     }
208   } else {
209     for (int iteration = 0; iteration < options.iterations; iteration += options.iteration_step) {
210       if (!options.verbose) { progress.print(iteration); }
211       // Benchmark each file once per iteration
212       for (size_t f=0; f<options.files.size(); f++) {
213         verbose() << "[verbose] " << benchmarkers[f]->filename << " iterations #" << iteration << "-" << (iteration+options.iteration_step-1) << endl;
214         benchmarkers[f]->run_iterations(options.iteration_step, false, options.hotbuffers);
215       }
216     }
217   }
218   if (!options.verbose) { progress.erase(); }
219 
220   for (size_t i=0; i<options.files.size(); i++) {
221     benchmarkers[i]->print(options.tabbed_output);
222     delete benchmarkers[i];
223   }
224 
225   return EXIT_SUCCESS;
226 }
227