inital commit
[c11concurrency-benchmarks.git] / mabain / db_comparisons / db_comp.cpp
1 #include <iostream>
2 #include <string>
3 #include <cstring>
4 #include <fstream>
5 #include <sys/time.h>
6 #include <assert.h>
7 #include <unistd.h>
8 #include <sys/syscall.h>
9 #include <openssl/sha.h>
10
11 #ifdef LEVEL_DB
12 #include <leveldb/db.h>
13 #elif KYOTO_CABINET
14 #include <kcpolydb.h>
15 #include <kchashdb.h>
16 #elif LMDB
17 extern "C" {
18 #include <lmdb.h>
19 }
20 #elif MABAIN
21 #include <mabain/db.h>
22 #endif
23
24 #ifdef LEVEL_DB
25 leveldb::DB *db = NULL;
26 #elif KYOTO_CABINET
27 kyotocabinet::HashDB *db = NULL;
28 #elif LMDB
29 MDB_env *env = NULL;
30 MDB_dbi db;
31 MDB_txn *txn = NULL;
32 #elif MABAIN
33 mabain::DB *db = NULL;
34 #endif
35
36 #define ONE_MILLION 1000000
37
38 static const char *db_dir   = "/var/tmp/db_test/";
39 static int num_kv           = 1 * ONE_MILLION;
40 static int n_reader         = 7;
41 static int key_type         = 0;
42 static bool sync_on_write   = false;
43 static unsigned long long memcap = 1024ULL*1024*1024;
44
45 static void get_sha256_str(int key, char *sha256_str)
46 {
47     unsigned char hash[SHA256_DIGEST_LENGTH];
48     SHA256_CTX sha256;
49     SHA256_Init(&sha256);
50     SHA256_Update(&sha256, (unsigned char*)&key, 4);
51     SHA256_Final(hash, &sha256);
52     int i = 0;
53     for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
54     {
55         sprintf(sha256_str + (i * 2), "%02x", hash[i]);
56     }
57     sha256_str[64] = 0;
58 }
59
60 static void get_sha1_str(int key, char *sha1_str)
61 {
62     unsigned char hash[SHA_DIGEST_LENGTH];
63     SHA_CTX sha1;
64     SHA1_Init(&sha1);
65     SHA1_Update(&sha1, (unsigned char*)&key, 4);
66     SHA1_Final(hash, &sha1);
67     int i = 0;
68     for(i = 0; i < SHA_DIGEST_LENGTH; i++)
69     {
70         sprintf(sha1_str + (i * 2), "%02x", hash[i]);
71     }
72     sha1_str[32] = 0;
73 }
74
75 static void print_cpu_info()
76 {
77     std::ifstream cpu_info("/proc/cpuinfo", std::fstream::in);
78     if(!cpu_info.is_open()) {
79         return;
80     }
81
82     std::string line;
83     std::string model_name;
84     std::string vendor_id;
85     std::string cpu_freq;
86     int n_processor = 0;
87     while (std::getline(cpu_info, line))
88     {
89         if(line.find("model name") != std::string::npos)
90         {
91             model_name = line;
92             n_processor++;
93         }
94         else if(line.find("vendor_id") != std::string::npos)
95         {
96             vendor_id = line;
97         }
98         else if(line.find("cpu MHz") != std::string::npos)
99         {
100             cpu_freq = line;
101         }
102     }
103     cpu_info.close();
104
105     std::cout << "number of CPUs\t: " << n_processor << "\n";
106     std::cout << vendor_id << "\n";
107     std::cout << model_name << "\n";
108     std::cout << cpu_freq << "\n";
109 }
110
111 static void InitTestDir()
112 {
113     std::string cmd;
114
115     cmd = std::string("mkdir -p ") + db_dir;
116     if(system(cmd.c_str()) != 0) {
117     }
118
119 #ifdef LEVEL_DB
120     std::cout << "===== using leveldb for testing\n";
121     std::string db_dir_tmp = std::string(db_dir) + "/leveldb/";
122 #elif KYOTO_CABINET
123     std::cout << "===== using kyotocabinet for testing\n";
124     std::string db_dir_tmp = std::string(db_dir) + "/kyotocabinet/";
125 #elif LMDB
126     std::cout << "===== using lmdb for testing\n";
127     std::string db_dir_tmp = std::string(db_dir) + "/lmdb/";
128 #elif MABAIN
129     std::cout << "===== using mabain for testing\n";
130     std::string db_dir_tmp = std::string(db_dir) + "/mabain/";
131 #endif
132     cmd = std::string("mkdir -p ") + db_dir_tmp;
133     if(system(cmd.c_str()) != 0) {
134     }
135     cmd = std::string("rm -rf ") + db_dir_tmp + "*";
136     if(system(cmd.c_str()) != 0) {
137     }
138
139 }
140
141 static void InitDB(bool writer_mode = true)
142 {
143 #ifdef LEVEL_DB
144     std::string db_dir_tmp = std::string(db_dir) + "/leveldb/";
145     leveldb::Options options;
146     options.create_if_missing = true;
147     leveldb::Status status = leveldb::DB::Open(options, db_dir_tmp, &db);
148     assert(status.ok());
149 #elif KYOTO_CABINET
150     std::string db_file = std::string(db_dir) + "/kyotocabinet/dbbench_hashDB.kch";
151     db = new kyotocabinet::HashDB();
152     int open_options = kyotocabinet::PolyDB::OWRITER |
153                        kyotocabinet::PolyDB::OCREATE;
154     db->tune_map(memcap);
155     if(!db->open(db_file, open_options)) {
156       fprintf(stderr, "open error: %s\n", db->error().name());
157     }
158 #elif LMDB
159     std::string db_dir_tmp = std::string(db_dir) + "/lmdb";
160     mdb_env_create(&env);
161     mdb_env_set_mapsize(env, memcap);
162     mdb_env_open(env, db_dir_tmp.c_str(), 0, 0664);
163     mdb_txn_begin(env, NULL, 0, &txn);
164     mdb_open(txn, NULL, 0, &db);
165     mdb_txn_commit(txn);
166 #elif MABAIN
167     std::string db_dir_tmp = std::string(db_dir) + "/mabain/";
168     int options = mabain::CONSTS::WriterOptions() | mabain::CONSTS::ASYNC_WRITER_MODE;
169     if(sync_on_write) {
170         options |= mabain::CONSTS::SYNC_ON_WRITE;
171     }
172     if(!writer_mode)
173         options = mabain::CONSTS::ReaderOptions();
174     db = new mabain::DB(db_dir_tmp.c_str(), options, (unsigned long long)(0.6666667*memcap),
175                                                      (unsigned long long)(0.3333333*memcap));
176     assert(db->is_open());
177 #endif
178 }
179
180 static void Add(int n)
181 {
182     timeval start, stop;
183     char kv[65];
184
185     gettimeofday(&start,NULL);
186 #if LMDB
187     if(!sync_on_write)
188         mdb_txn_begin(env, NULL, 0, &txn);
189 #endif
190     for(int i = 0; i < n; i++) {
191         std::string key, val;
192         if(key_type == 0) {
193             key = std::to_string(i);
194             val = std::to_string(i);
195         } else {
196             if(key_type == 1) {
197                 get_sha1_str(i, kv);
198             } else {
199                 get_sha256_str(i, kv);
200             }
201             key = kv;
202             val = kv;
203         }
204
205 #ifdef LEVEL_DB
206         leveldb::WriteOptions opts = leveldb::WriteOptions();
207         opts.sync = sync_on_write;
208         db->Put(opts, key, val);
209 #elif KYOTO_CABINET
210         db->set(key.c_str(), val.c_str());
211 #elif LMDB
212         MDB_val lmdb_key, lmdb_val;
213         lmdb_key.mv_size = key.size();
214         lmdb_key.mv_data = (void*) key.data();
215         lmdb_val.mv_size = val.size();
216         lmdb_val.mv_data = (void*) val.data();
217         MDB_cursor *mc;
218         if(sync_on_write) {
219             mdb_txn_begin(env, NULL, 0, &txn);
220             mdb_cursor_open(txn, db, &mc);
221             mdb_cursor_put(mc, &lmdb_key, &lmdb_val, 0);
222             mdb_cursor_close(mc);
223             mdb_txn_commit(txn);
224         } else {
225             mdb_cursor_open(txn, db, &mc);
226             mdb_cursor_put(mc, &lmdb_key, &lmdb_val, 0);
227             mdb_cursor_close(mc);
228         }
229 #elif MABAIN
230         db->Add(key, val);
231 #endif
232
233         if((i+1) % ONE_MILLION == 0) {
234             std::cout << "inserted: " << (i+1) << " key-value pairs\n";
235         }
236     }    
237 #if LMDB
238     if(!sync_on_write)
239         mdb_txn_commit(txn);
240 #endif
241
242     gettimeofday(&stop,NULL);
243
244     uint64_t timediff = (stop.tv_sec - start.tv_sec)*1000000 + (stop.tv_usec - start.tv_usec);
245     std::cout << "===== " << timediff*1.0/n << " micro seconds per insertion\n";
246 }
247
248 static void Lookup(int n)
249 {
250     timeval start, stop;
251     int nfound = 0;
252     char kv[65];
253 #ifdef LMDB
254     MDB_val lmdb_key, lmdb_value;
255     MDB_cursor *cursor;
256     mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
257     mdb_cursor_open(txn, db, &cursor);
258 #endif
259
260     gettimeofday(&start,NULL);
261     for(int i = 0; i < n; i++) {
262         std::string key;
263         if(key_type == 0) {
264             key = std::to_string(i);
265         } else {
266             if(key_type == 1) {
267                 get_sha1_str(i, kv);
268             } else {
269                 get_sha256_str(i, kv);
270             }
271             key = kv;
272         }
273
274 #ifdef LEVEL_DB
275         std::string value;
276         leveldb::Status s = db->Get(leveldb::ReadOptions(), key, &value);
277         if(s.ok()) nfound++;
278 #elif KYOTO_CABINET
279         std::string value;
280         if(db->get(key, &value)) nfound++;
281 #elif LMDB
282         lmdb_key.mv_data = (void*) key.data();
283         lmdb_key.mv_size = key.size();
284         if(mdb_cursor_get(cursor, &lmdb_key, &lmdb_value, MDB_SET) == 0)
285             nfound++;
286         //std::cout<<key<<":"<<std::string((char*)lmdb_value.mv_data, lmdb_value.mv_size)<<"\n";
287 #elif MABAIN
288         mabain::MBData mbd;
289         int rval = db->Find(key, mbd);
290         //std::cout<<key<<":"<<std::string((char*)mbd.buff, mbd.data_len)<<"\n";
291         if(rval == 0) nfound++;
292 #endif
293
294         if((i+1) % ONE_MILLION == 0) {
295             std::cout << "looked up: " << (i+1) << " keys\n";
296         }
297     }
298
299 #ifdef LMDB
300     mdb_cursor_close(cursor);
301     mdb_txn_abort(txn);
302 #endif
303     gettimeofday(&stop,NULL);
304
305     std::cout << "found " << nfound << " key-value pairs\n";
306     uint64_t timediff = (stop.tv_sec - start.tv_sec)*1000000 + (stop.tv_usec - start.tv_usec);
307     std::cout << "===== " << timediff*1.0/n << " micro seconds per lookup\n";
308 }
309
310 static void Delete(int n)
311 {
312     timeval start, stop;
313     int nfound = 0;
314     char kv[65];
315
316 #if LMDB
317     if(!sync_on_write)
318         mdb_txn_begin(env, NULL, 0, &txn);
319 #endif
320
321     gettimeofday(&start,NULL);
322     for(int i = 0; i < n; i++) {
323         std::string key;
324         if(key_type == 0) {
325             key = std::to_string(i);
326         } else {
327             if(key_type == 1) {
328                 get_sha1_str(i, kv);
329             } else {
330                 get_sha256_str(i, kv);
331             }
332             key = kv;
333         }
334 #ifdef LEVEL_DB
335         leveldb::WriteOptions opts = leveldb::WriteOptions();
336         opts.sync = sync_on_write;
337         leveldb::Status s = db->Delete(opts, key);
338         if(s.ok()) nfound++;
339 #elif KYOTO_CABINET
340         std::string value;
341         if(db->remove(key)) nfound++;
342 #elif LMDB
343         MDB_val lmdb_key;
344         lmdb_key.mv_size = key.size();
345         lmdb_key.mv_data = (void*) key.data();
346         if(sync_on_write) {
347             mdb_txn_begin(env, NULL, 0, &txn);
348             if(mdb_del(txn, db, &lmdb_key, NULL) == 0) nfound++;
349             mdb_txn_commit(txn);
350         } else {
351             if(mdb_del(txn, db, &lmdb_key, NULL) == 0) nfound++;
352         }
353 #elif MABAIN
354         int rval = db->Remove(key);
355         if(rval == 0) nfound++;
356 #endif
357
358         if((i+1) % ONE_MILLION == 0) {
359             std::cout << "deleted: " << (i+1) << " keys\n";
360         }
361     }
362
363 #if LMDB
364     if(!sync_on_write)
365         mdb_txn_commit(txn);
366 #endif
367     gettimeofday(&stop,NULL);
368
369     std::cout << "deleted " << nfound << " key-value pairs\n";
370     uint64_t timediff = (stop.tv_sec - start.tv_sec)*1000000 + (stop.tv_usec - start.tv_usec);
371     std::cout << "===== " << timediff*1.0/n << " micro seconds per deletion\n";
372 }
373
374 static void *Writer(void *arg)
375 {
376     int num = *((int *) arg);
377     char kv[65];
378     int tid = static_cast<int>(syscall(SYS_gettid));
379
380     std::cout << "\n[writer : " << tid << "] started" << std::endl;
381     for(int i = 0; i < num; i++) {
382         std::string key, val;
383         if(key_type == 0) {
384             key = std::to_string(i);
385             val = std::to_string(i);
386         } else {
387             if(key_type == 1) {
388                 get_sha1_str(i, kv);
389             } else {
390                 get_sha256_str(i, kv);
391             }
392             key = kv;
393             val = kv;
394         }
395
396 #ifdef LEVEL_DB
397         leveldb::WriteOptions opts = leveldb::WriteOptions();
398         opts.sync = sync_on_write;
399         db->Put(opts, key, val);
400 #elif MABAIN
401         db->Add(key.c_str(), key.length(), val.c_str(), val.length());
402 #ifdef DEFRAG
403         if((i+1) % (2*ONE_MILLION)== 0) {
404             std::cout<<"\nRC SCHEDULED " << std::endl;
405             db->CollectResource(1, 1);
406         }
407 #endif
408 #endif
409
410         if((i+1) % ONE_MILLION == 0) {
411             std::cout << "\nwriter inserted " << (i+1) << std::endl;
412         }
413     }
414
415     return NULL;
416 }
417 static void *Reader(void *arg)
418 {
419     int num = *((int *) arg);
420     int i = 0;
421     int tid = static_cast<int>(syscall(SYS_gettid));
422     char kv[65];
423
424 #if MABAIN
425     std::string db_dir_tmp = std::string(db_dir) + "/mabain/";
426     mabain::DB *db_r = new mabain::DB(db_dir_tmp.c_str(), mabain::CONSTS::ReaderOptions(),
427                                       (unsigned long long)(0.6666667*memcap),
428                                       (unsigned long long)(0.3333333*memcap));
429     assert(db_r->is_open());
430 #endif
431
432     std::cout << "\n[reader : " << tid << "] started" << std::endl;
433     while(i < num) {
434         std::string key;
435         bool found = false;
436
437         if(key_type == 0) {
438             key = std::to_string(i);
439         } else {
440             if(key_type == 1) {
441                 get_sha1_str(i, kv);
442             } else {
443                 get_sha256_str(i, kv);
444             }
445             key = kv;
446         }
447
448         std::string value;
449 #ifdef LEVEL_DB
450         leveldb::Status s = db->Get(leveldb::ReadOptions(), key, &value);
451         if(s.ok()) {
452             found = true;
453         }
454 #elif MABAIN
455         mabain::MBData mbd;
456         int rval = db_r->Find(key, mbd);
457         if(rval == 0) {
458             value = std::string((const char *)mbd.buff, mbd.data_len);
459             found = true;
460         }
461 #endif
462         if(found) {
463             if(key.compare(value) != 0) {
464                 std::cout << "\nVALUE NOT MATCH for key:" << key << ":" << value << "\n";
465                 abort();
466             }
467
468             i++;
469             if((i+1) % ONE_MILLION == 0) {
470                 std::cout << "\n[reader : " << tid << "] found " << (i+1) << "\n";
471             }
472         }
473     }
474
475 #if MABAIN
476     db_r->Close();
477     delete db_r;
478 #endif
479     return NULL;
480 }
481 static void ConcurrencyTest(int num, int n_r)
482 {
483 #ifdef KYOTO_CABINET
484     std::cout << "===== concurrency test ignored for kyotocabinet\n";
485     return;
486 #elif LMDB
487     std::cout << "===== concurrency test ignored for lmdb\n";
488     return;
489 #endif
490
491     pthread_t wid;
492     timeval start, stop;
493
494     gettimeofday(&start,NULL);
495
496     // Start the writer
497     if(pthread_create(&wid, NULL, Writer, &num) != 0) {
498         std::cerr << "failed to create writer thread\n";
499         abort();
500     }    
501
502     // start the readers
503     pthread_t rid[256];
504     assert(n_r <= 256);
505     for(int i = 0; i < n_r; i++) {
506         if(pthread_create(&rid[i], NULL, Reader, &num) != 0) {
507             std::cerr << "failed to create writer thread\n";
508             abort();
509         }    
510     }
511
512     for(int i = 0; i < n_r; i++) {
513         pthread_join(rid[i], NULL);
514     }
515     pthread_join(wid, NULL);
516
517     gettimeofday(&stop,NULL);
518
519     uint64_t timediff = (stop.tv_sec - start.tv_sec)*1000000 + (stop.tv_usec - start.tv_usec);
520     std::cout << "===== " << timediff*1.0/num_kv << " micro seconds per concurrent insertion/lookup\n";
521 }
522
523 static void DestroyDB()
524 {
525 #ifdef LEVEL_DB
526     delete db;
527     db = NULL;
528 #elif KYOTO_CABINET
529     db->close();
530     delete db;
531     db = NULL;
532 #elif LMDB
533     mdb_close(env, db);
534     mdb_env_close(env);
535 #elif MABAIN
536     db->Close();
537     delete db;
538     db = NULL;
539 #endif
540 }
541
542 static void RemoveDB()
543 {
544     std::string cmd = std::string("rm -rf ") + db_dir;
545 #ifdef LEVEL_DB
546     cmd += "leveldb/*";
547 #elif KYOTO_CABINET
548     cmd += "kyotocabinet/*";
549 #elif LMDB
550     cmd += "lmdb/*";
551 #elif MABAIN
552     cmd += "mabain/*";
553     mabain::DB::ClearResources(std::string("/var/tmp/"));
554 #endif
555
556     if(system(cmd.c_str()) != 0) {
557     }
558 }
559
560 int main(int argc, char *argv[])
561 {
562 #ifdef MABAIN
563     mabain::DB::SetLogFile("/var/tmp/mabain_test/mabain.log");
564     // mabain::DB::SetLogLevel(2);
565 #endif
566     for(int i = 1; i < argc; i++) {
567         if(strcmp(argv[i], "-n") == 0) {
568             if(++i >= argc) abort();
569             num_kv = atoi(argv[i]);
570         } else if(strcmp(argv[i], "-k") == 0) {
571             if(++i >= argc) abort();
572             if(strcmp(argv[i], "int") == 0) {
573                 key_type = 0;
574             } else if(strcmp(argv[i], "sha1") == 0) {
575                 key_type = 1;
576             } else if(strcmp(argv[i], "sha2") == 0) {
577                 key_type = 2;
578             } else {
579                 std::cerr << "invalid key type: " << argv[i] << "\n";
580                 abort();
581             }
582         } else if(strcmp(argv[i], "-t") == 0) {
583             if(++i >= argc) abort();
584             n_reader = atoi(argv[i]);
585         } else if(strcmp(argv[i], "-d") == 0) {
586             if(++i >= argc) abort();
587             db_dir = argv[i];
588         } else if(strcmp(argv[i], "-s") == 0) {
589             sync_on_write = true;
590         } else if(strcmp(argv[i], "-m") == 0) {
591             if(++i >= argc) abort();
592             memcap = atoi(argv[i]);
593         } else {
594             std::cerr << "invalid argument: " << argv[i] << "\n";
595         }
596     }
597
598     print_cpu_info();
599     if(sync_on_write)
600         std::cout << "===== Disk sync is on\n";
601     else
602         std::cout << "===== Disk sync is off\n";
603     std::cout << "===== Memcap is " << memcap << "\n";
604
605     InitTestDir();
606     RemoveDB();
607
608     InitDB();
609     Add(num_kv);
610     DestroyDB();
611
612     InitDB(false);
613     Lookup(num_kv);
614     DestroyDB();
615
616     InitDB();
617     Delete(num_kv);
618     DestroyDB();
619
620     RemoveDB();
621
622     InitDB();
623     ConcurrencyTest(num_kv, n_reader);
624     DestroyDB();
625
626 #ifdef MABAIN
627     mabain::DB::CloseLogFile();
628 #endif
629     return 0;
630 }