0605654ad2f04715d6a6dc8b98fea3b7d88c4012
[c11concurrency-benchmarks.git] / silo / benchmarks / dbtest.cc
1 #include <iostream>
2 #include <fstream>
3 #include <sstream>
4 #include <vector>
5 #include <utility>
6 #include <string>
7 #include <set>
8
9 #include <getopt.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/sysinfo.h>
13
14 #include "../allocator.h"
15 #include "../stats_server.h"
16 #include "bench.h"
17 #include "bdb_wrapper.h"
18 #include "ndb_wrapper.h"
19 #include "ndb_wrapper_impl.h"
20 #include "kvdb_wrapper.h"
21 #include "kvdb_wrapper_impl.h"
22 #if !NO_MYSQL
23 #include "mysql_wrapper.h"
24 #endif
25
26 using namespace std;
27 using namespace util;
28
29 static vector<string>
30 split_ws(const string &s)
31 {
32   vector<string> r;
33   istringstream iss(s);
34   copy(istream_iterator<string>(iss),
35        istream_iterator<string>(),
36        back_inserter<vector<string>>(r));
37   return r;
38 }
39
40 static size_t
41 parse_memory_spec(const string &s)
42 {
43   string x(s);
44   size_t mult = 1;
45   if (x.back() == 'G') {
46     mult = static_cast<size_t>(1) << 30;
47     x.pop_back();
48   } else if (x.back() == 'M') {
49     mult = static_cast<size_t>(1) << 20;
50     x.pop_back();
51   } else if (x.back() == 'K') {
52     mult = static_cast<size_t>(1) << 10;
53     x.pop_back();
54   }
55   return strtoul(x.c_str(), nullptr, 10) * mult;
56 }
57
58 int
59 main(int argc, char **argv)
60 {
61   abstract_db *db = NULL;
62   void (*test_fn)(abstract_db *, int argc, char **argv) = NULL;
63   string bench_type = "ycsb";
64   string db_type = "ndb-proto2";
65   char *curdir = get_current_dir_name();
66   string basedir = curdir;
67   string bench_opts;
68   size_t numa_memory = 0;
69   free(curdir);
70   int saw_run_spec = 0;
71   int nofsync = 0;
72   int do_compress = 0;
73   int fake_writes = 0;
74   int disable_gc = 0;
75   int disable_snapshots = 0;
76   vector<string> logfiles;
77   vector<vector<unsigned>> assignments;
78   string stats_server_sockfile;
79   while (1) {
80     static struct option long_options[] =
81     {
82       {"verbose"                    , no_argument       , &verbose                   , 1}   ,
83       {"parallel-loading"           , no_argument       , &enable_parallel_loading   , 1}   ,
84       {"pin-cpus"                   , no_argument       , &pin_cpus                  , 1}   ,
85       {"slow-exit"                  , no_argument       , &slow_exit                 , 1}   ,
86       {"retry-aborted-transactions" , no_argument       , &retry_aborted_transaction , 1}   ,
87       {"backoff-aborted-transactions" , no_argument     , &backoff_aborted_transaction , 1}   ,
88       {"bench"                      , required_argument , 0                          , 'b'} ,
89       {"scale-factor"               , required_argument , 0                          , 's'} ,
90       {"num-threads"                , required_argument , 0                          , 't'} ,
91       {"db-type"                    , required_argument , 0                          , 'd'} ,
92       {"basedir"                    , required_argument , 0                          , 'B'} ,
93       {"txn-flags"                  , required_argument , 0                          , 'f'} ,
94       {"runtime"                    , required_argument , 0                          , 'r'} ,
95       {"ops-per-worker"             , required_argument , 0                          , 'n'} ,
96       {"bench-opts"                 , required_argument , 0                          , 'o'} ,
97       {"numa-memory"                , required_argument , 0                          , 'm'} , // implies --pin-cpus
98       {"logfile"                    , required_argument , 0                          , 'l'} ,
99       {"assignment"                 , required_argument , 0                          , 'a'} ,
100       {"log-nofsync"                , no_argument       , &nofsync                   , 1}   ,
101       {"log-compress"               , no_argument       , &do_compress               , 1}   ,
102       {"log-fake-writes"            , no_argument       , &fake_writes               , 1}   ,
103       {"disable-gc"                 , no_argument       , &disable_gc                , 1}   ,
104       {"disable-snapshots"          , no_argument       , &disable_snapshots         , 1}   ,
105       {"stats-server-sockfile"      , required_argument , 0                          , 'x'} ,
106       {"no-reset-counters"          , no_argument       , &no_reset_counters         , 1}   ,
107       {0, 0, 0, 0}
108     };
109     int option_index = 0;
110     int c = getopt_long(argc, argv, "b:s:t:d:B:f:r:n:o:m:l:a:x:", long_options, &option_index);
111     if (c == -1)
112       break;
113
114     switch (c) {
115     case 0:
116       if (long_options[option_index].flag != 0)
117         break;
118       abort();
119       break;
120
121     case 'b':
122       bench_type = optarg;
123       break;
124
125     case 's':
126       scale_factor = strtod(optarg, NULL);
127       ALWAYS_ASSERT(scale_factor > 0.0);
128       break;
129
130     case 't':
131       nthreads = strtoul(optarg, NULL, 10);
132       ALWAYS_ASSERT(nthreads > 0);
133       break;
134
135     case 'd':
136       db_type = optarg;
137       break;
138
139     case 'B':
140       basedir = optarg;
141       break;
142
143     case 'f':
144       txn_flags = strtoul(optarg, NULL, 10);
145       break;
146
147     case 'r':
148       ALWAYS_ASSERT(!saw_run_spec);
149       saw_run_spec = 1;
150       runtime = strtoul(optarg, NULL, 10);
151       ALWAYS_ASSERT(runtime > 0);
152       run_mode = RUNMODE_TIME;
153       break;
154
155     case 'n':
156       ALWAYS_ASSERT(!saw_run_spec);
157       saw_run_spec = 1;
158       ops_per_worker = strtoul(optarg, NULL, 10);
159       ALWAYS_ASSERT(ops_per_worker > 0);
160       run_mode = RUNMODE_OPS;
161
162     case 'o':
163       bench_opts = optarg;
164       break;
165
166     case 'm':
167       {
168         pin_cpus = 1;
169         const size_t m = parse_memory_spec(optarg);
170         ALWAYS_ASSERT(m > 0);
171         numa_memory = m;
172       }
173       break;
174
175     case 'l':
176       logfiles.emplace_back(optarg);
177       break;
178
179     case 'a':
180       assignments.emplace_back(
181           ParseCSVString<unsigned, RangeAwareParser<unsigned>>(optarg));
182       break;
183
184     case 'x':
185       stats_server_sockfile = optarg;
186       break;
187
188     case '?':
189       /* getopt_long already printed an error message. */
190       exit(1);
191
192     default:
193       abort();
194     }
195   }
196
197   if (bench_type == "ycsb")
198     test_fn = ycsb_do_test;
199   else if (bench_type == "tpcc")
200     test_fn = tpcc_do_test;
201   else if (bench_type == "queue")
202     test_fn = queue_do_test;
203   else if (bench_type == "encstress")
204     test_fn = encstress_do_test;
205   else if (bench_type == "bid")
206     test_fn = bid_do_test;
207   else
208     ALWAYS_ASSERT(false);
209
210   if (do_compress && logfiles.empty()) {
211     cerr << "[ERROR] --log-compress specified without logging enabled" << endl;
212     return 1;
213   }
214
215   if (fake_writes && logfiles.empty()) {
216     cerr << "[ERROR] --log-fake-writes specified without logging enabled" << endl;
217     return 1;
218   }
219
220   if (nofsync && logfiles.empty()) {
221     cerr << "[ERROR] --log-nofsync specified without logging enabled" << endl;
222     return 1;
223   }
224
225   if (fake_writes && nofsync) {
226     cerr << "[WARNING] --log-nofsync has no effect with --log-fake-writes enabled" << endl;
227   }
228
229 #ifndef ENABLE_EVENT_COUNTERS
230   if (!stats_server_sockfile.empty()) {
231     cerr << "[WARNING] --stats-server-sockfile with no event counters enabled is useless" << endl;
232   }
233 #endif
234
235   // initialize the numa allocator
236   if (numa_memory > 0) {
237     const size_t maxpercpu = util::iceil(
238         numa_memory / nthreads, ::allocator::GetHugepageSize());
239     numa_memory = maxpercpu * nthreads;
240     ::allocator::Initialize(nthreads, maxpercpu);
241   }
242
243   const set<string> can_persist({"ndb-proto2"});
244   if (!logfiles.empty() && !can_persist.count(db_type)) {
245     cerr << "[ERROR] benchmark " << db_type
246          << " does not have persistence implemented" << endl;
247     return 1;
248   }
249
250 #ifdef PROTO2_CAN_DISABLE_GC
251   const set<string> has_gc({"ndb-proto1", "ndb-proto2"});
252   if (disable_gc && !has_gc.count(db_type)) {
253     cerr << "[ERROR] benchmark " << db_type
254          << " does not have gc to disable" << endl;
255     return 1;
256   }
257 #else
258   if (disable_gc) {
259     cerr << "[ERROR] macro PROTO2_CAN_DISABLE_GC was not set, cannot disable gc" << endl;
260     return 1;
261   }
262 #endif
263
264 #ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
265   const set<string> has_snapshots({"ndb-proto2"});
266   if (disable_snapshots && !has_snapshots.count(db_type)) {
267     cerr << "[ERROR] benchmark " << db_type
268          << " does not have snapshots to disable" << endl;
269     return 1;
270   }
271 #else
272   if (disable_snapshots) {
273     cerr << "[ERROR] macro PROTO2_CAN_DISABLE_SNAPSHOTS was not set, cannot disable snapshots" << endl;
274     return 1;
275   }
276 #endif
277
278   if (db_type == "bdb") {
279     const string cmd = "rm -rf " + basedir + "/db/*";
280     // XXX(stephentu): laziness
281     int ret UNUSED = system(cmd.c_str());
282     db = new bdb_wrapper("db", bench_type + ".db");
283   } else if (db_type == "ndb-proto1") {
284     // XXX: hacky simulation of proto1
285     db = new ndb_wrapper<transaction_proto2>(
286         logfiles, assignments, !nofsync, do_compress, fake_writes);
287     transaction_proto2_static::set_hack_status(true);
288     ALWAYS_ASSERT(transaction_proto2_static::get_hack_status());
289 #ifdef PROTO2_CAN_DISABLE_GC
290     if (!disable_gc)
291       transaction_proto2_static::InitGC();
292 #endif
293   } else if (db_type == "ndb-proto2") {
294     db = new ndb_wrapper<transaction_proto2>(
295         logfiles, assignments, !nofsync, do_compress, fake_writes);
296     ALWAYS_ASSERT(!transaction_proto2_static::get_hack_status());
297 #ifdef PROTO2_CAN_DISABLE_GC
298     if (!disable_gc)
299       transaction_proto2_static::InitGC();
300 #endif
301 #ifdef PROTO2_CAN_DISABLE_SNAPSHOTS
302     if (disable_snapshots)
303       transaction_proto2_static::DisableSnapshots();
304 #endif
305   } else if (db_type == "kvdb") {
306     db = new kvdb_wrapper<true>;
307   } else if (db_type == "kvdb-st") {
308     db = new kvdb_wrapper<false>;
309 #if !NO_MYSQL
310   } else if (db_type == "mysql") {
311     string dbdir = basedir + "/mysql-db";
312     db = new mysql_wrapper(dbdir, bench_type);
313 #endif
314   } else
315     ALWAYS_ASSERT(false);
316
317 #ifdef DEBUG
318   cerr << "WARNING: benchmark built in DEBUG mode!!!" << endl;
319 #endif
320
321 #ifdef CHECK_INVARIANTS
322   cerr << "WARNING: invariant checking is enabled - should disable for benchmark" << endl;
323 #ifdef PARANOID_CHECKING
324   cerr << "  *** Paranoid checking is enabled ***" << endl;
325 #endif
326 #endif
327
328   if (verbose) {
329     const unsigned long ncpus = coreid::num_cpus_online();
330     cerr << "Database Benchmark:"                           << endl;
331     cerr << "  pid: " << getpid()                           << endl;
332     cerr << "settings:"                                     << endl;
333     cerr << "  par-loading : " << enable_parallel_loading   << endl;
334     cerr << "  pin-cpus    : " << pin_cpus                  << endl;
335     cerr << "  slow-exit   : " << slow_exit                 << endl;
336     cerr << "  retry-txns  : " << retry_aborted_transaction << endl;
337     cerr << "  backoff-txns: " << backoff_aborted_transaction << endl;
338     cerr << "  bench       : " << bench_type                << endl;
339     cerr << "  scale       : " << scale_factor              << endl;
340     cerr << "  num-cpus    : " << ncpus                     << endl;
341     cerr << "  num-threads : " << nthreads                  << endl;
342     cerr << "  db-type     : " << db_type                   << endl;
343     cerr << "  basedir     : " << basedir                   << endl;
344     cerr << "  txn-flags   : " << hexify(txn_flags)         << endl;
345     if (run_mode == RUNMODE_TIME)
346       cerr << "  runtime     : " << runtime                 << endl;
347     else
348       cerr << "  ops/worker  : " << ops_per_worker          << endl;
349 #ifdef USE_VARINT_ENCODING
350     cerr << "  var-encode  : yes"                           << endl;
351 #else
352     cerr << "  var-encode  : no"                            << endl;
353 #endif
354
355 #ifdef USE_JEMALLOC
356     cerr << "  allocator   : jemalloc"                      << endl;
357 #elif defined USE_TCMALLOC
358     cerr << "  allocator   : tcmalloc"                      << endl;
359 #elif defined USE_FLOW
360     cerr << "  allocator   : flow"                          << endl;
361 #else
362     cerr << "  allocator   : libc"                          << endl;
363 #endif
364     if (numa_memory > 0) {
365       cerr << "  numa-memory : " << numa_memory             << endl;
366     } else {
367       cerr << "  numa-memory : disabled"                    << endl;
368     }
369     cerr << "  logfiles : " << logfiles                     << endl;
370     cerr << "  assignments : " << assignments               << endl;
371     cerr << "  disable-gc : " << disable_gc                 << endl;
372     cerr << "  disable-snapshots : " << disable_snapshots   << endl;
373     cerr << "  stats-server-sockfile: " << stats_server_sockfile << endl;
374
375     cerr << "system properties:" << endl;
376     cerr << "  btree_internal_node_size: " << concurrent_btree::InternalNodeSize() << endl;
377     cerr << "  btree_leaf_node_size    : " << concurrent_btree::LeafNodeSize() << endl;
378
379 #ifdef TUPLE_PREFETCH
380     cerr << "  tuple_prefetch          : yes" << endl;
381 #else
382     cerr << "  tuple_prefetch          : no" << endl;
383 #endif
384
385 #ifdef BTREE_NODE_PREFETCH
386     cerr << "  btree_node_prefetch     : yes" << endl;
387 #else
388     cerr << "  btree_node_prefetch     : no" << endl;
389 #endif
390
391   }
392
393   if (!stats_server_sockfile.empty()) {
394     stats_server *srvr = new stats_server(stats_server_sockfile);
395     thread(&stats_server::serve_forever, srvr).detach();
396   }
397
398   vector<string> bench_toks = split_ws(bench_opts);
399   int argc = 1 + bench_toks.size();
400   char *argv[argc];
401   argv[0] = (char *) bench_type.c_str();
402   for (size_t i = 1; i <= bench_toks.size(); i++)
403     argv[i] = (char *) bench_toks[i - 1].c_str();
404   test_fn(db, argc, argv);
405   delete db;
406   return 0;
407 }