• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "iwkv.h"
2 #include "iwutils.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <ctype.h>
7 
8 char *g_program;
9 
10 uint32_t _execsize();
11 
12 typedef struct _BMCTX BMCTX;
13 
14 typedef bool (bench_method(BMCTX *bmctx));
15 
16 #define RND_DATA_SZ (10*1048576)
17 char RND_DATA[RND_DATA_SZ];
18 
19 struct BM {
20   bool initiated;
21   int ksz;
22   int argc;
23   char **argv;
24   char *param_db;
25   int param_num;
26   int param_num_reads;
27   int param_value_size;
28   uint32_t param_seed;
29   char *param_benchmarks;
30   void (*val_free)(void *ptr);
31   void (*env_setup)(void);
32   void *(*db_open)(BMCTX *ctx);
33   bool (*db_close)(BMCTX *ctx);
34   bool (*db_put)(BMCTX *ctx, const IWKV_val *key, const IWKV_val *val, bool sync);
35   bool (*db_get)(BMCTX *ctx, const IWKV_val *key, IWKV_val *val, bool *found);
36   bool (*db_cursor_to_key)(BMCTX *ctx, const IWKV_val *key, IWKV_val *val, bool *found);
37   bool (*db_del)(BMCTX *ctx, const IWKV_val *key, bool sync);
38   bool (*db_read_seq)(BMCTX *ctx, bool reverse);
39   uint64_t (*db_size_bytes)(BMCTX *ctx);
40 } bm = {0};
41 
42 struct _BMCTX {
43   bool success;
44   bool freshdb;
45   bool logdbsize;
46   char *name;
47   int num;
48   int value_size;
49   uint64_t start_ms;
50   uint64_t end_ms;
51   void *db;
52   void *extra;
53   bench_method *method;
54   int rnd_data_pos;
55 };
56 
_val_dispose(IWKV_val * val)57 static void _val_dispose(IWKV_val *val) {
58   if (bm.val_free) {
59     if (val->data) {
60       bm.val_free(val->data);
61       val->size = 0;
62     }
63   } else {
64     iwkv_val_dispose(val);
65   }
66 }
67 
_bmctx_dispose(BMCTX * ctx)68 static void _bmctx_dispose(BMCTX *ctx) {
69   free(ctx->name);
70   free(ctx);
71 }
72 
_bmctx_rndbuf_nextptr(BMCTX * ctx,int len)73 static const char *_bmctx_rndbuf_nextptr(BMCTX *ctx, int len) {
74   if (len > RND_DATA_SZ) {
75     fprintf(stderr, "record value length exceeds maximum allowed: %d\n", RND_DATA_SZ);
76     exit(1);
77   }
78   if (ctx->rnd_data_pos + len > RND_DATA_SZ) {
79     ctx->rnd_data_pos = 0;
80   }
81   const char *ret = RND_DATA + ctx->rnd_data_pos;
82   ctx->rnd_data_pos += len;
83   return ret;
84 }
85 
_bm_check()86 static bool _bm_check() {
87   if (!bm.env_setup) {
88     fprintf(stderr, "env_setup function is not set\n");
89     return false;
90   }
91   if (!bm.db_open) {
92     fprintf(stderr, "db_open function is not set\n");
93     return false;
94   }
95   if (!bm.db_close) {
96     fprintf(stderr, "db_close function is not set\n");
97     return false;
98   }
99   if (!bm.db_put) {
100     fprintf(stderr, "db_put function is not set\n");
101     return false;
102   }
103   if (!bm.db_get) {
104     fprintf(stderr, "db_get function is not set\n");
105     return false;
106   }
107   if (!bm.db_del) {
108     fprintf(stderr, "db_del function is not set\n");
109     return false;
110   }
111   if (!bm.db_read_seq) {
112     fprintf(stderr, "db_read_seq function is not set\n");
113     return false;
114   }
115   if (!bm.db_cursor_to_key) {
116     fprintf(stderr, "db_cursor_to_key function is not set\n");
117     return false;
118   }
119   return true;
120 }
121 
_bm_help()122 static void _bm_help() {
123   fprintf(stderr, "\n");
124   fprintf(stderr, "Usage %s [options]\n\n", g_program);
125   fprintf(stderr, "Options:\n");
126   fprintf(stderr, "  -h Show this help\n");
127   fprintf(stderr, "  -w Write Ahead Log (WAL) enabled\n");
128   fprintf(stderr, "  -n <num> Number of stored records\n");
129   fprintf(stderr,
130           "  -r <num> Number of records to read (equals to number of stored records if not specified, not for readseq,readreverse)\n");
131   fprintf(stderr, "  -vz <size> Size of a single record value in bytes\n");
132   fprintf(stderr, "  -kz <16|192|1024> Size of record key. Either 16 (default) or 192 or 1024 bytes\n");
133   fprintf(stderr, "  -b <comma separated benchmarks to run>\n\n");
134   fprintf(stderr, "  -db <file/dir> Database file/directory\n\n");
135   fprintf(stderr, "  -rs <random seed> Random seed used for iwu random generator\n\n");
136   fprintf(stderr, "Available benchmarks:\n");
137   fprintf(stderr, "  fillseq        write N fixed length values in sequential key order\n");
138   fprintf(stderr, "  fillseq2       write N random length values in sequential key order\n");
139   fprintf(stderr, "  fillrandom     write N fixed length values in random key order\n");
140   fprintf(stderr, "  fillrandom2    write N random length values in random key order\n");
141   fprintf(stderr, "  overwrite      overwrite N values in random key order\n");
142   fprintf(stderr, "  fillsync       write N/100 values in random key order in sync mode\n");
143   fprintf(stderr, "  fill100K       write N/100 100K values in random order\n");
144   fprintf(stderr, "  deleteseq      delete N keys in sequential order\n");
145   fprintf(stderr, "  deleterandom   delete N keys in random order\n");
146   fprintf(stderr, "  readseq        read all records sequentially\n");
147   fprintf(stderr, "  readreverse    read all records sequentially in reverse order\n");
148   fprintf(stderr, "  readrandom     read N times in random order\n");
149   fprintf(stderr, "  readmissing    read N missing keys in random order\n");
150   fprintf(stderr, "  readhot        read N times in random order from 1%% section of DB\n");
151   fprintf(stderr, "  seekrandom     N random seeks\n");
152   fprintf(stderr, "\n");
153 }
154 
_bm_init(int argc,char * argv[])155 static bool _bm_init(int argc, char *argv[]) {
156   if (bm.initiated) {
157     fprintf(stderr, "Benchmark already initialized\n");
158     return false;
159   }
160   if (iw_init()) {
161     return false;
162   }
163   bm.argc = argc;
164   bm.argv = argv;
165   bm.param_num = 2000000; // 2M records
166   bm.param_num_reads = -1; // Same as param_num
167   bm.param_value_size = 400;
168   bm.param_benchmarks =  "fillrandom2,"
169                          "readrandom,"
170                          "deleterandom,"
171                          "fillseq2,"
172                          "readrandom,"
173                          "deleteseq,"
174                          "fillseq,"
175                          "overwrite,"
176                          "readrandom,"
177                          "readseq,"
178                          "readreverse,"
179                          "readhot,"
180                          "readmissing,"
181                          "deleteseq,"
182                          "fill100K";
183 #ifndef NDEBUG
184   fprintf(stderr, "WARNING: Assertions are enabled, benchmarks can be slow\n");
185 #endif
186   for (int i = 1; i < argc; ++i) {
187     if (!strcmp(argv[i], "-h")) {
188       _bm_help();
189       return false;
190     }
191     if (!strcmp(argv[i], "-n")) {
192       if (++i >= argc) {
193         fprintf(stderr, "'-n <num records>' option has no value\n");
194         return false;
195       }
196       bm.param_num = atoi(argv[i]);
197       if (bm.param_num < 1) {
198         fprintf(stderr, "'-n <num records>' invalid option value\n");
199         return false;
200       }
201     } else if (!strcmp(argv[i], "-r")) {
202       if (++i >= argc) {
203         fprintf(stderr, "'-r <num read records>' option has no value\n");
204         return false;
205       }
206       bm.param_num_reads = atoi(argv[i]);
207     } else if (!strcmp(argv[i], "-vz")) {
208       if (++i >= argc) {
209         fprintf(stderr, "'-vz <value size>' option has not value\n");
210         return false;
211       }
212       bm.param_value_size = atoi(argv[i]);
213       if (bm.param_value_size <= 0) {
214         fprintf(stderr, "'-vz <value size>' invalid option value\n");
215         return false;
216       }
217     } else if (!strcmp(argv[i], "-b")) {
218       if (++i >= argc) {
219         fprintf(stderr, "'-b <benchmarks>' options has no value\n");
220         return false;
221       }
222       bm.param_benchmarks = argv[i];
223     } else if (!strcmp(argv[i], "-db")) {
224       if (++i >= argc) {
225         fprintf(stderr, "'-db <db file>' options has no value\n");
226         return false;
227       }
228       bm.param_db = argv[i];
229     } else if (!strcmp(argv[i], "-rs")) {
230       if (++i >= argc) {
231         fprintf(stderr, "'-rs <random seed>' options has no value\n");
232         return false;
233       }
234       bm.param_seed = atoll(argv[i]);
235     } else if (!strcmp(argv[i], "-kz")) {
236       if (++i >= argc) {
237         fprintf(stderr, "'-kz 16|192|1024' options has no value\n");
238         return false;
239       }
240       int ksz = atoi(argv[i]);
241       if (ksz == 192) {
242         bm.ksz = 192;
243       } else if (ksz == 1024) {
244         bm.ksz = 1024;
245       } else if (ksz != 16) {
246         fprintf(stderr, "'-kz 16|192|1024' options has invalid value %d\n", ksz);
247         return false;
248       }
249     }
250   }
251   if (bm.ksz == 0) {
252     bm.ksz = 16;
253   }
254   if (bm.param_num_reads < 0) {
255     bm.param_num_reads = bm.param_num;
256   }
257   if (!_bm_check()) {
258     fprintf(stderr, "Benchmark `bm` structure is not configured properly\n");
259     return false;
260   }
261   if (!bm.param_seed) {
262     uint64_t ts;
263     iwp_current_time_ms(&ts, false);
264     ts = IW_SWAB64(ts);
265     ts >>= 32;
266     bm.param_seed = ts;
267   }
268   iwu_rand_seed(bm.param_seed);
269   srandom(bm.param_seed);
270   fprintf(stderr,
271           "\n exec size: %u"
272           "\n random seed: %u"
273           "\n num records: %d\n read num records: %d\n value size: %d\n benchmarks: %s"
274           "\n key size: %d \n\n",
275           _execsize(),
276           bm.param_seed, bm.param_num, bm.param_num_reads, bm.param_value_size, bm.param_benchmarks,
277           bm.ksz);
278 
279   // Fill up random data array
280   for (int i = 0; i < RND_DATA_SZ; ++i) {
281     RND_DATA[i] = ' ' + iwu_rand_range(95); // ascii space ... ~
282   }
283   return true;
284 }
285 
_logdbsize(BMCTX * ctx)286 static void _logdbsize(BMCTX *ctx) {
287   if (bm.db_size_bytes) {
288     uint64_t dbz = bm.db_size_bytes(ctx);
289     fprintf(stderr, " db size: %lu (%lu MB)\n", dbz, (dbz / 1024 / 1024));
290   }
291 }
292 
_execsize()293 uint32_t _execsize() {
294   const char *path = g_program;
295   IWP_FILE_STAT fst;
296   iwrc rc = iwp_fstat(path, &fst);
297   if (rc) {
298     iwlog_ecode_error3(rc);
299     return 0;
300   }
301   return fst.size;
302 }
303 
_bm_run(BMCTX * ctx)304 static void _bm_run(BMCTX *ctx) {
305   assert(ctx->method);
306   uint64_t llv;
307   ctx->db = bm.db_open(ctx);
308   if (!ctx->db) {
309     ctx->success = false;
310     return;
311   }
312   iwp_current_time_ms(&llv, false);
313   ctx->start_ms = llv;
314   ctx->success = ctx->method(ctx);
315   if (!bm.db_close(ctx)) {
316     ctx->success = false;
317   }
318   iwp_current_time_ms(&llv, false);
319   ctx->end_ms = llv;
320 }
321 
322 
323 #define FILLKEY() snprintf(key.data, sizeof(kbuf), bm.ksz == 16 ? "%016d" : bm.ksz == 192 ? "%0192d" : "%01024d", k)
324 
325 
_do_write(BMCTX * ctx,bool seq,bool sync,bool rvlen)326 static bool _do_write(BMCTX *ctx, bool seq, bool sync, bool rvlen) {
327   char kbuf[1025];
328   IWKV_val key, val;
329   key.data = kbuf;
330   int value_size = ctx->value_size;
331   for (int i = 0; i < ctx->num; ++i) {
332     if (rvlen) {
333       value_size = iwu_rand_range(ctx->value_size + 1);
334       if (value_size == 0) {
335         value_size = 1;
336       }
337     }
338     const int k = seq ? i : iwu_rand_range(bm.param_num);
339     FILLKEY();
340 
341     key.size = strlen(key.data);
342     val.data = (void *) _bmctx_rndbuf_nextptr(ctx, value_size);
343     val.size = value_size;
344     if (!bm.db_put(ctx, &key, &val, sync)) {
345       return false;
346     }
347   }
348   return true;
349 }
350 
_do_delete(BMCTX * ctx,bool sync,bool seq)351 static bool _do_delete(BMCTX *ctx, bool sync, bool seq) {
352   char kbuf[1025];
353   IWKV_val key;
354   key.data = kbuf;
355   for (int i = 0; i < ctx->num; ++i) {
356     const int k = seq ? i : iwu_rand_range(bm.param_num);
357     FILLKEY();
358     key.size = strlen(key.data);
359     if (!bm.db_del(ctx, &key, sync)) {
360       return false;
361     }
362   }
363   return true;
364 }
365 
_do_read_seq(BMCTX * ctx)366 static bool _do_read_seq(BMCTX *ctx) {
367   return bm.db_read_seq(ctx, false);
368 }
369 
_do_read_reverse(BMCTX * ctx)370 static bool _do_read_reverse(BMCTX *ctx) {
371   return bm.db_read_seq(ctx, true);
372 }
373 
_do_read_random(BMCTX * ctx)374 static bool _do_read_random(BMCTX *ctx) {
375   char kbuf[1025];
376   IWKV_val key, val;
377   bool found;
378   key.data = kbuf;
379   for (int i = 0; i < bm.param_num_reads; ++i) {
380     const int k = iwu_rand_range(bm.param_num);
381     FILLKEY();
382     key.size = strlen(key.data);
383     val.data = 0;
384     val.size = 0;
385     if (!bm.db_get(ctx, &key, &val, &found)) {
386       _val_dispose(&val);
387       return false;
388     }
389     _val_dispose(&val);
390   }
391   return true;
392 }
393 
_do_read_missing(BMCTX * ctx)394 static bool _do_read_missing(BMCTX *ctx) {
395   char kbuf[1025];
396   IWKV_val key, val;
397   bool found;
398   key.data = kbuf;
399   for (int i = 0; i < bm.param_num_reads; ++i) {
400     const int k = iwu_rand_range(bm.param_num);
401     snprintf(key.data, sizeof(kbuf),
402              bm.ksz == 16 ? "%016d." : bm.ksz == 192 ? "%0192d." : "%01024d.", k);
403     key.size = strlen(key.data);
404     val.data = 0;
405     val.size = 0;
406     if (!bm.db_get(ctx, &key, &val, &found)) {
407       _val_dispose(&val);
408       return false;
409     }
410     _val_dispose(&val);
411     if (found) {
412       return false;
413     }
414   }
415   return true;
416 }
417 
_do_read_hot(BMCTX * ctx)418 static bool _do_read_hot(BMCTX *ctx) {
419   char kbuf[1025];
420   IWKV_val key, val;
421   bool found;
422   key.data = kbuf;
423   const int range = (bm.param_num + 99) / 100;
424   for (int i = 0; i < bm.param_num_reads; ++i) {
425     const int k = iwu_rand_range(range);
426     FILLKEY();
427     key.size = strlen(key.data);
428     val.data = 0;
429     val.size = 0;
430     if (!bm.db_get(ctx, &key, &val, &found)) {
431       _val_dispose(&val);
432       return false;
433     }
434     _val_dispose(&val);
435   }
436   return true;
437 }
438 
_do_seek_random(BMCTX * ctx)439 static bool _do_seek_random(BMCTX *ctx) {
440   char kbuf[1025];
441   IWKV_val key, val;
442   bool found;
443   key.data = kbuf;
444   for (int i = 0; i < bm.param_num_reads; ++i) {
445     const int k = iwu_rand_range(bm.param_num);
446     FILLKEY();
447     key.size = strlen(key.data);
448     val.data = 0;
449     val.size = 0;
450     if (!bm.db_cursor_to_key(ctx, &key, &val, &found)) {
451       _val_dispose(&val);
452       return false;
453     }
454     _val_dispose(&val);
455   }
456   return true;
457 }
458 
_bm_fillseq(BMCTX * ctx)459 static bool _bm_fillseq(BMCTX *ctx) {
460   if (!ctx->freshdb) return false;
461   return _do_write(ctx, true, false, false);
462 }
463 
_bm_fillseq2(BMCTX * ctx)464 static bool _bm_fillseq2(BMCTX *ctx) {
465   if (!ctx->freshdb) return false;
466   return _do_write(ctx, true, false, true);
467 }
468 
_bm_fillrandom(BMCTX * ctx)469 static bool _bm_fillrandom(BMCTX *ctx) {
470   if (!ctx->freshdb) return false;
471   return _do_write(ctx, false, false, false);
472 }
473 
_bm_fillrandom2(BMCTX * ctx)474 static bool _bm_fillrandom2(BMCTX *ctx) {
475   if (!ctx->freshdb) return false;
476   return _do_write(ctx, false, false, true);
477 }
478 
_bm_overwrite(BMCTX * ctx)479 static bool _bm_overwrite(BMCTX *ctx) {
480   if (ctx->freshdb) return false;
481   return _do_write(ctx, false, false, false);
482 }
483 
_bm_fillsync(BMCTX * ctx)484 static bool _bm_fillsync(BMCTX *ctx) {
485   if (!ctx->freshdb) return false;
486   ctx->num /= 100;
487   if (ctx->num < 1) ctx->num = 1;
488   fprintf(stderr, "\tfillsync num records: %d\n", ctx->num);
489   return _do_write(ctx, false, true, false);
490 }
491 
_bm_fill100K(BMCTX * ctx)492 static bool _bm_fill100K(BMCTX *ctx) {
493   if (!ctx->freshdb) return false;
494   ctx->num /= 100;
495   if (ctx->num < 1) ctx->num = 1;
496   fprintf(stderr, "\tfill100K num records: %d\n", ctx->num);
497   ctx->value_size = 100 * 1000;
498   return _do_write(ctx, false, false, false);
499 }
500 
_bm_deleteseq(BMCTX * ctx)501 static bool _bm_deleteseq(BMCTX *ctx) {
502   if (ctx->freshdb) return false;
503   return _do_delete(ctx, false, true);
504 }
505 
_bm_deleterandom(BMCTX * ctx)506 static bool _bm_deleterandom(BMCTX *ctx) {
507   if (ctx->freshdb) return false;
508   return _do_delete(ctx, false, false);
509 }
510 
_bm_readseq(BMCTX * ctx)511 static bool _bm_readseq(BMCTX *ctx) {
512   if (ctx->freshdb) return false;
513   return _do_read_seq(ctx);
514 }
515 
_bm_readreverse(BMCTX * ctx)516 static bool _bm_readreverse(BMCTX *ctx) {
517   if (ctx->freshdb) return false;
518   return _do_read_reverse(ctx);
519 }
520 
_bm_readrandom(BMCTX * ctx)521 static bool _bm_readrandom(BMCTX *ctx) {
522   if (ctx->freshdb) return false;
523   return _do_read_random(ctx);
524 }
525 
_bm_readmissing(BMCTX * ctx)526 static bool _bm_readmissing(BMCTX *ctx) {
527   if (ctx->freshdb) return false;
528   return _do_read_missing(ctx);
529 }
530 
_bm_readhot(BMCTX * ctx)531 static bool _bm_readhot(BMCTX *ctx) {
532   if (ctx->freshdb) return false;
533   return _do_read_hot(ctx);
534 }
535 
_bm_seekrandom(BMCTX * ctx)536 static bool _bm_seekrandom(BMCTX *ctx) {
537   if (ctx->freshdb) return false;
538   return _do_seek_random(ctx);
539 }
540 
_bmctx_create(const char * name)541 static BMCTX *_bmctx_create(const char *name) {
542   bench_method *method = 0;
543   bool logdbsize = false;
544   bool freshdb = false;
545   if (!strcmp(name, "fillseq")) {
546     freshdb = true;
547     logdbsize = true;
548     method = _bm_fillseq;
549   } else if (!strcmp(name, "fillseq2")) {
550     freshdb = true;
551     logdbsize = true;
552     method = _bm_fillseq2;
553   } else if (!strcmp(name, "fillrandom")) {
554     freshdb = true;
555     logdbsize = true;
556     method = _bm_fillrandom;
557   } else if (!strcmp(name, "fillrandom2")) {
558     freshdb = true;
559     logdbsize = true;
560     method = _bm_fillrandom2;
561   } else if (!strcmp(name, "overwrite")) {
562     logdbsize = true;
563     method = _bm_overwrite;
564   } else if (!strcmp(name, "fillsync")) {
565     freshdb = true;
566     logdbsize = true;
567     method = _bm_fillsync;
568   } else if (!strcmp(name, "fill100K")) {
569     freshdb = true;
570     logdbsize = true;
571     method = _bm_fill100K;
572   } else if (!strcmp(name, "deleteseq")) {
573     logdbsize = true;
574     method = _bm_deleteseq;
575   } else if (!strcmp(name, "deleterandom")) {
576     logdbsize = true;
577     method = _bm_deleterandom;
578   } else if (!strcmp(name, "readseq")) {
579     method = _bm_readseq;
580   } else if (!strcmp(name, "readreverse")) {
581     method = _bm_readreverse;
582   } else if (!strcmp(name, "readrandom")) {
583     method = _bm_readrandom;
584   } else if (!strcmp(name, "readmissing")) {
585     method = _bm_readmissing;
586   } else if (!strcmp(name, "readhot")) {
587     method = _bm_readhot;
588   } else if (!strcmp(name, "seekrandom")) {
589     method = _bm_seekrandom;
590   } else {
591     fprintf(stderr, "Unknown benchmark: '%s'\n", name);
592     return 0;
593   }
594   BMCTX *bmctx = calloc(1, sizeof(*bmctx));
595   bmctx->name = strdup(name);
596   bmctx->method = method;
597   bmctx->num = bm.param_num;
598   bmctx->value_size = bm.param_value_size;
599   bmctx->freshdb = freshdb;
600   bmctx->logdbsize = logdbsize;
601   return bmctx;
602 }
603 
bm_bench_run(int argc,char * argv[])604 static bool bm_bench_run(int argc, char *argv[]) {
605   if (!_bm_init(argc, argv)) {
606     fprintf(stderr, "Benchmark cannot be initialized\n");
607     exit(1);
608   }
609   bm.env_setup();
610   int c = 0;
611   const char *ptr = bm.param_benchmarks;
612   char bname[100];
613   bool bmres = true;
614   while (bmres) {
615     if (*ptr == ',' || *ptr == '\0' || c >= 99) {
616       bname[c] = '\0';
617       BMCTX *ctx = _bmctx_create(bname);
618       c = 0;
619       if (ctx) {
620         fprintf(stderr, " benchmark: %s\n", bname);
621         _bm_run(ctx);
622         bmres = ctx->success;
623         if (!bmres) {
624           fprintf(stderr, "Failed to run benchmark: %s\n", bname);
625         } else {
626           fprintf(stderr, " done: %s in %lu\n", bname, (ctx->end_ms - ctx->start_ms));
627         }
628         if (ctx->logdbsize) {
629           _logdbsize(ctx);
630         }
631         _bmctx_dispose(ctx);
632       }
633       if (*ptr == '\0' || !bmres) {
634         break;
635       }
636     } else if (!isspace(*ptr)) {
637       bname[c++] = *ptr;
638     }
639     ptr += 1;
640   }
641   return bmres;
642 }
643