Initialize Tizen 2.3
[external/leveldb.git] / doc / bench / db_bench_tree_db.cc
1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <kcpolydb.h>
8 #include "util/histogram.h"
9 #include "util/random.h"
10 #include "util/testutil.h"
11
12 // Comma-separated list of operations to run in the specified order
13 //   Actual benchmarks:
14 //
15 //   fillseq       -- write N values in sequential key order in async mode
16 //   fillrandom    -- write N values in random key order in async mode
17 //   overwrite     -- overwrite N values in random key order in async mode
18 //   fillseqsync   -- write N/100 values in sequential key order in sync mode
19 //   fillrandsync  -- write N/100 values in random key order in sync mode
20 //   fillrand100K  -- write N/1000 100K values in random order in async mode
21 //   fillseq100K   -- write N/1000 100K values in seq order in async mode
22 //   readseq       -- read N times sequentially
23 //   readseq100K   -- read N/1000 100K values in sequential order in async mode
24 //   readrand100K  -- read N/1000 100K values in sequential order in async mode
25 //   readrandom    -- read N times in random order
26 static const char* FLAGS_benchmarks =
27     "fillseq,"
28     "fillseqsync,"
29     "fillrandsync,"
30     "fillrandom,"
31     "overwrite,"
32     "readrandom,"
33     "readseq,"
34     "fillrand100K,"
35     "fillseq100K,"
36     "readseq100K,"
37     "readrand100K,"
38     ;
39
40 // Number of key/values to place in database
41 static int FLAGS_num = 1000000;
42
43 // Number of read operations to do.  If negative, do FLAGS_num reads.
44 static int FLAGS_reads = -1;
45
46 // Size of each value
47 static int FLAGS_value_size = 100;
48
49 // Arrange to generate values that shrink to this fraction of
50 // their original size after compression
51 static double FLAGS_compression_ratio = 0.5;
52
53 // Print histogram of operation timings
54 static bool FLAGS_histogram = false;
55
56 // Cache size. Default 4 MB
57 static int FLAGS_cache_size = 4194304;
58
59 // Page size. Default 1 KB
60 static int FLAGS_page_size = 1024;
61
62 // If true, do not destroy the existing database.  If you set this
63 // flag and also specify a benchmark that wants a fresh database, that
64 // benchmark will fail.
65 static bool FLAGS_use_existing_db = false;
66
67 // Compression flag. If true, compression is on. If false, compression
68 // is off.
69 static bool FLAGS_compression = true;
70
71 inline
72 static void DBSynchronize(kyotocabinet::TreeDB* db_)
73 {
74   // Synchronize will flush writes to disk
75   if (!db_->synchronize()) {
76     fprintf(stderr, "synchronize error: %s\n", db_->error().name());
77   }
78 }
79
80 namespace leveldb {
81
82 // Helper for quickly generating random data.
83 namespace {
84 class RandomGenerator {
85  private:
86   std::string data_;
87   int pos_;
88
89  public:
90   RandomGenerator() {
91     // We use a limited amount of data over and over again and ensure
92     // that it is larger than the compression window (32KB), and also
93     // large enough to serve all typical value sizes we want to write.
94     Random rnd(301);
95     std::string piece;
96     while (data_.size() < 1048576) {
97       // Add a short fragment that is as compressible as specified
98       // by FLAGS_compression_ratio.
99       test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);
100       data_.append(piece);
101     }
102     pos_ = 0;
103   }
104
105   Slice Generate(int len) {
106     if (pos_ + len > data_.size()) {
107       pos_ = 0;
108       assert(len < data_.size());
109     }
110     pos_ += len;
111     return Slice(data_.data() + pos_ - len, len);
112   }
113 };
114
115 static Slice TrimSpace(Slice s) {
116   int start = 0;
117   while (start < s.size() && isspace(s[start])) {
118     start++;
119   }
120   int limit = s.size();
121   while (limit > start && isspace(s[limit-1])) {
122     limit--;
123   }
124   return Slice(s.data() + start, limit - start);
125 }
126
127 }  // namespace
128
129 class Benchmark {
130  private:
131   kyotocabinet::TreeDB* db_;
132   int db_num_;
133   int num_;
134   int reads_;
135   double start_;
136   double last_op_finish_;
137   int64_t bytes_;
138   std::string message_;
139   Histogram hist_;
140   RandomGenerator gen_;
141   Random rand_;
142   kyotocabinet::LZOCompressor<kyotocabinet::LZO::RAW> comp_;
143
144   // State kept for progress messages
145   int done_;
146   int next_report_;     // When to report next
147
148   void PrintHeader() {
149     const int kKeySize = 16;
150     PrintEnvironment();
151     fprintf(stdout, "Keys:       %d bytes each\n", kKeySize);
152     fprintf(stdout, "Values:     %d bytes each (%d bytes after compression)\n",
153             FLAGS_value_size,
154             static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));
155     fprintf(stdout, "Entries:    %d\n", num_);
156     fprintf(stdout, "RawSize:    %.1f MB (estimated)\n",
157             ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)
158              / 1048576.0));
159     fprintf(stdout, "FileSize:   %.1f MB (estimated)\n",
160             (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)
161              / 1048576.0));
162     PrintWarnings();
163     fprintf(stdout, "------------------------------------------------\n");
164   }
165
166   void PrintWarnings() {
167 #if defined(__GNUC__) && !defined(__OPTIMIZE__)
168     fprintf(stdout,
169             "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"
170             );
171 #endif
172 #ifndef NDEBUG
173     fprintf(stdout,
174             "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
175 #endif
176   }
177
178   void PrintEnvironment() {
179     fprintf(stderr, "Kyoto Cabinet:    version %s, lib ver %d, lib rev %d\n",
180             kyotocabinet::VERSION, kyotocabinet::LIBVER, kyotocabinet::LIBREV);
181
182 #if defined(__linux)
183     time_t now = time(NULL);
184     fprintf(stderr, "Date:           %s", ctime(&now));  // ctime() adds newline
185
186     FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
187     if (cpuinfo != NULL) {
188       char line[1000];
189       int num_cpus = 0;
190       std::string cpu_type;
191       std::string cache_size;
192       while (fgets(line, sizeof(line), cpuinfo) != NULL) {
193         const char* sep = strchr(line, ':');
194         if (sep == NULL) {
195           continue;
196         }
197         Slice key = TrimSpace(Slice(line, sep - 1 - line));
198         Slice val = TrimSpace(Slice(sep + 1));
199         if (key == "model name") {
200           ++num_cpus;
201           cpu_type = val.ToString();
202         } else if (key == "cache size") {
203           cache_size = val.ToString();
204         }
205       }
206       fclose(cpuinfo);
207       fprintf(stderr, "CPU:            %d * %s\n", num_cpus, cpu_type.c_str());
208       fprintf(stderr, "CPUCache:       %s\n", cache_size.c_str());
209     }
210 #endif
211   }
212
213   void Start() {
214     start_ = Env::Default()->NowMicros() * 1e-6;
215     bytes_ = 0;
216     message_.clear();
217     last_op_finish_ = start_;
218     hist_.Clear();
219     done_ = 0;
220     next_report_ = 100;
221   }
222
223   void FinishedSingleOp() {
224     if (FLAGS_histogram) {
225       double now = Env::Default()->NowMicros() * 1e-6;
226       double micros = (now - last_op_finish_) * 1e6;
227       hist_.Add(micros);
228       if (micros > 20000) {
229         fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");
230         fflush(stderr);
231       }
232       last_op_finish_ = now;
233     }
234
235     done_++;
236     if (done_ >= next_report_) {
237       if      (next_report_ < 1000)   next_report_ += 100;
238       else if (next_report_ < 5000)   next_report_ += 500;
239       else if (next_report_ < 10000)  next_report_ += 1000;
240       else if (next_report_ < 50000)  next_report_ += 5000;
241       else if (next_report_ < 100000) next_report_ += 10000;
242       else if (next_report_ < 500000) next_report_ += 50000;
243       else                            next_report_ += 100000;
244       fprintf(stderr, "... finished %d ops%30s\r", done_, "");
245       fflush(stderr);
246     }
247   }
248
249   void Stop(const Slice& name) {
250     double finish = Env::Default()->NowMicros() * 1e-6;
251
252     // Pretend at least one op was done in case we are running a benchmark
253     // that does not call FinishedSingleOp().
254     if (done_ < 1) done_ = 1;
255
256     if (bytes_ > 0) {
257       char rate[100];
258       snprintf(rate, sizeof(rate), "%6.1f MB/s",
259                (bytes_ / 1048576.0) / (finish - start_));
260       if (!message_.empty()) {
261         message_  = std::string(rate) + " " + message_;
262       } else {
263         message_ = rate;
264       }
265     }
266
267     fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",
268             name.ToString().c_str(),
269             (finish - start_) * 1e6 / done_,
270             (message_.empty() ? "" : " "),
271             message_.c_str());
272     if (FLAGS_histogram) {
273       fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());
274     }
275     fflush(stdout);
276   }
277
278  public:
279   enum Order {
280     SEQUENTIAL,
281     RANDOM
282   };
283   enum DBState {
284     FRESH,
285     EXISTING
286   };
287
288   Benchmark()
289   : db_(NULL),
290     num_(FLAGS_num),
291     reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),
292     bytes_(0),
293     rand_(301) {
294     std::vector<std::string> files;
295     Env::Default()->GetChildren("/tmp", &files);
296     if (!FLAGS_use_existing_db) {
297       for (int i = 0; i < files.size(); i++) {
298         if (Slice(files[i]).starts_with("dbbench_polyDB")) {
299           Env::Default()->DeleteFile("/tmp/" + files[i]);
300         }
301       }
302     }
303   }
304
305   ~Benchmark() {
306     if (!db_->close()) {
307       fprintf(stderr, "close error: %s\n", db_->error().name());
308     }
309   }
310
311   void Run() {
312     PrintHeader();
313     Open(false);
314
315     const char* benchmarks = FLAGS_benchmarks;
316     while (benchmarks != NULL) {
317       const char* sep = strchr(benchmarks, ',');
318       Slice name;
319       if (sep == NULL) {
320         name = benchmarks;
321         benchmarks = NULL;
322       } else {
323         name = Slice(benchmarks, sep - benchmarks);
324         benchmarks = sep + 1;
325       }
326
327       Start();
328
329       bool known = true;
330       bool write_sync = false;
331       if (name == Slice("fillseq")) {
332         Write(write_sync, SEQUENTIAL, FRESH, num_, FLAGS_value_size, 1);
333         
334       } else if (name == Slice("fillrandom")) {
335         Write(write_sync, RANDOM, FRESH, num_, FLAGS_value_size, 1);
336         DBSynchronize(db_);
337       } else if (name == Slice("overwrite")) {
338         Write(write_sync, RANDOM, EXISTING, num_, FLAGS_value_size, 1);
339         DBSynchronize(db_);
340       } else if (name == Slice("fillrandsync")) {
341         write_sync = true;
342         Write(write_sync, RANDOM, FRESH, num_ / 100, FLAGS_value_size, 1);
343         DBSynchronize(db_);
344       } else if (name == Slice("fillseqsync")) {
345         write_sync = true;
346         Write(write_sync, SEQUENTIAL, FRESH, num_ / 100, FLAGS_value_size, 1);
347         DBSynchronize(db_);
348       } else if (name == Slice("fillrand100K")) {
349         Write(write_sync, RANDOM, FRESH, num_ / 1000, 100 * 1000, 1);
350         DBSynchronize(db_);
351       } else if (name == Slice("fillseq100K")) {
352         Write(write_sync, SEQUENTIAL, FRESH, num_ / 1000, 100 * 1000, 1);
353         DBSynchronize(db_);
354       } else if (name == Slice("readseq")) {
355         ReadSequential();
356       } else if (name == Slice("readrandom")) {
357         ReadRandom();
358       } else if (name == Slice("readrand100K")) {
359         int n = reads_;
360         reads_ /= 1000;
361         ReadRandom();
362         reads_ = n;
363       } else if (name == Slice("readseq100K")) {
364         int n = reads_;
365         reads_ /= 1000;
366         ReadSequential();
367         reads_ = n;
368       } else {
369         known = false;
370         if (name != Slice()) {  // No error message for empty name
371           fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());
372         }
373       }
374       if (known) {
375         Stop(name);
376       }
377     }
378   }
379
380  private:
381     void Open(bool sync) {
382     assert(db_ == NULL);
383
384     // Initialize db_
385     db_ = new kyotocabinet::TreeDB();
386     char file_name[100];
387     db_num_++;
388     snprintf(file_name, sizeof(file_name), "/tmp/dbbench_polyDB-%d.kct",
389                                db_num_);
390
391     // Create tuning options and open the database
392     int open_options = kyotocabinet::PolyDB::OWRITER |
393                        kyotocabinet::PolyDB::OCREATE;
394     int tune_options = kyotocabinet::TreeDB::TSMALL |
395         kyotocabinet::TreeDB::TLINEAR;
396     if (FLAGS_compression) {
397       tune_options |= kyotocabinet::TreeDB::TCOMPRESS;
398       db_->tune_compressor(&comp_);
399     }
400     db_->tune_options(tune_options);
401     db_->tune_page_cache(FLAGS_cache_size);
402     db_->tune_page(FLAGS_page_size);
403     db_->tune_map(256LL<<20);
404     if (sync) {
405       open_options |= kyotocabinet::PolyDB::OAUTOSYNC;
406     }
407     if (!db_->open(file_name, open_options)) {
408       fprintf(stderr, "open error: %s\n", db_->error().name());
409     }
410   }
411
412   void Write(bool sync, Order order, DBState state,
413              int num_entries, int value_size, int entries_per_batch) {
414     // Create new database if state == FRESH
415     if (state == FRESH) {
416       if (FLAGS_use_existing_db) {
417         message_ = "skipping (--use_existing_db is true)";
418         return;
419       }
420       delete db_;
421       db_ = NULL;
422       Open(sync);
423       Start();  // Do not count time taken to destroy/open
424     }
425
426     if (num_entries != num_) {
427       char msg[100];
428       snprintf(msg, sizeof(msg), "(%d ops)", num_entries);
429       message_ = msg;
430     }
431
432     // Write to database
433     for (int i = 0; i < num_entries; i++)
434     {
435       const int k = (order == SEQUENTIAL) ? i : (rand_.Next() % num_entries);
436       char key[100];
437       snprintf(key, sizeof(key), "%016d", k);
438       bytes_ += value_size + strlen(key);
439       std::string cpp_key = key;
440       if (!db_->set(cpp_key, gen_.Generate(value_size).ToString())) {
441         fprintf(stderr, "set error: %s\n", db_->error().name());
442       }
443       FinishedSingleOp();
444     }
445   }
446
447   void ReadSequential() {
448     kyotocabinet::DB::Cursor* cur = db_->cursor();
449     cur->jump();
450     std::string ckey, cvalue;
451     while (cur->get(&ckey, &cvalue, true)) {
452       bytes_ += ckey.size() + cvalue.size();
453       FinishedSingleOp();
454     }
455     delete cur;
456   }
457
458   void ReadRandom() {
459     std::string value;
460     for (int i = 0; i < reads_; i++) {
461       char key[100];
462       const int k = rand_.Next() % reads_;
463       snprintf(key, sizeof(key), "%016d", k);
464       db_->get(key, &value);
465       FinishedSingleOp();
466     }
467   }
468 };
469
470 }  // namespace leveldb
471
472 int main(int argc, char** argv) {
473   for (int i = 1; i < argc; i++) {
474     double d;
475     int n;
476     char junk;
477     if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {
478       FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");
479     } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {
480       FLAGS_compression_ratio = d;
481     } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&
482                (n == 0 || n == 1)) {
483       FLAGS_histogram = n;
484     } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {
485       FLAGS_num = n;
486     } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {
487       FLAGS_reads = n;
488     } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {
489       FLAGS_value_size = n;
490     } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {
491       FLAGS_cache_size = n;
492     } else if (sscanf(argv[i], "--page_size=%d%c", &n, &junk) == 1) {
493       FLAGS_page_size = n;
494     } else if (sscanf(argv[i], "--compression=%d%c", &n, &junk) == 1 &&
495                (n == 0 || n == 1)) {
496       FLAGS_compression = (n == 1) ? true : false;
497     } else {
498       fprintf(stderr, "Invalid flag '%s'\n", argv[i]);
499       exit(1);
500     }
501   }
502
503   leveldb::Benchmark benchmark;
504   benchmark.Run();
505   return 0;
506 }