1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 //  This source code is licensed under both the GPLv2 (found in the
3 //  COPYING file in the root directory) and Apache 2.0 License
4 //  (found in the LICENSE.Apache file in the root directory).
5 
6 #ifndef ROCKSDB_LITE
7 
8 #ifndef GFLAGS
9 #include <cstdio>
main()10 int main() {
11   fprintf(stderr, "Please install gflags to run this test... Skipping...\n");
12   return 0;
13 }
14 #else
15 
16 #include <cinttypes>
17 #include <map>
18 #include <string>
19 #include <vector>
20 
21 #include "memory/arena.h"
22 #include "table/cuckoo/cuckoo_table_builder.h"
23 #include "table/cuckoo/cuckoo_table_factory.h"
24 #include "table/cuckoo/cuckoo_table_reader.h"
25 #include "table/get_context.h"
26 #include "table/meta_blocks.h"
27 #include "test_util/testharness.h"
28 #include "test_util/testutil.h"
29 #include "util/gflags_compat.h"
30 #include "util/random.h"
31 #include "util/string_util.h"
32 
33 using GFLAGS_NAMESPACE::ParseCommandLineFlags;
34 using GFLAGS_NAMESPACE::SetUsageMessage;
35 
36 DEFINE_string(file_dir, "", "Directory where the files will be created"
37     " for benchmark. Added for using tmpfs.");
38 DEFINE_bool(enable_perf, false, "Run Benchmark Tests too.");
39 DEFINE_bool(write, false,
40     "Should write new values to file in performance tests?");
41 DEFINE_bool(identity_as_first_hash, true, "use identity as first hash");
42 
43 namespace ROCKSDB_NAMESPACE {
44 
45 namespace {
46 const uint32_t kNumHashFunc = 10;
47 // Methods, variables related to Hash functions.
48 std::unordered_map<std::string, std::vector<uint64_t>> hash_map;
49 
AddHashLookups(const std::string & s,uint64_t bucket_id,uint32_t num_hash_fun)50 void AddHashLookups(const std::string& s, uint64_t bucket_id,
51         uint32_t num_hash_fun) {
52   std::vector<uint64_t> v;
53   for (uint32_t i = 0; i < num_hash_fun; i++) {
54     v.push_back(bucket_id + i);
55   }
56   hash_map[s] = v;
57 }
58 
GetSliceHash(const Slice & s,uint32_t index,uint64_t)59 uint64_t GetSliceHash(const Slice& s, uint32_t index,
60                       uint64_t /*max_num_buckets*/) {
61   return hash_map[s.ToString()][index];
62 }
63 }  // namespace
64 
65 class CuckooReaderTest : public testing::Test {
66  public:
67   using testing::Test::SetUp;
68 
CuckooReaderTest()69   CuckooReaderTest() {
70     options.allow_mmap_reads = true;
71     env = options.env;
72     env_options = EnvOptions(options);
73   }
74 
SetUp(int num)75   void SetUp(int num) {
76     num_items = num;
77     hash_map.clear();
78     keys.clear();
79     keys.resize(num_items);
80     user_keys.clear();
81     user_keys.resize(num_items);
82     values.clear();
83     values.resize(num_items);
84   }
85 
NumToStr(int64_t i)86   std::string NumToStr(int64_t i) {
87     return std::string(reinterpret_cast<char*>(&i), sizeof(i));
88   }
89 
CreateCuckooFileAndCheckReader(const Comparator * ucomp=BytewiseComparator ())90   void CreateCuckooFileAndCheckReader(
91       const Comparator* ucomp = BytewiseComparator()) {
92     std::unique_ptr<WritableFile> writable_file;
93     ASSERT_OK(env->NewWritableFile(fname, &writable_file, env_options));
94     std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
95         NewLegacyWritableFileWrapper(std::move(writable_file)), fname,
96         env_options));
97 
98     CuckooTableBuilder builder(
99         file_writer.get(), 0.9, kNumHashFunc, 100, ucomp, 2, false, false,
100         GetSliceHash, 0 /* column_family_id */, kDefaultColumnFamilyName);
101     ASSERT_OK(builder.status());
102     for (uint32_t key_idx = 0; key_idx < num_items; ++key_idx) {
103       builder.Add(Slice(keys[key_idx]), Slice(values[key_idx]));
104       ASSERT_OK(builder.status());
105       ASSERT_EQ(builder.NumEntries(), key_idx + 1);
106     }
107     ASSERT_OK(builder.Finish());
108     ASSERT_EQ(num_items, builder.NumEntries());
109     file_size = builder.FileSize();
110     ASSERT_OK(file_writer->Close());
111 
112     // Check reader now.
113     std::unique_ptr<RandomAccessFile> read_file;
114     ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
115     std::unique_ptr<RandomAccessFileReader> file_reader(
116         new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(read_file),
117                                    fname));
118     const ImmutableCFOptions ioptions(options);
119     CuckooTableReader reader(ioptions, std::move(file_reader), file_size, ucomp,
120                              GetSliceHash);
121     ASSERT_OK(reader.status());
122     // Assume no merge/deletion
123     for (uint32_t i = 0; i < num_items; ++i) {
124       PinnableSlice value;
125       GetContext get_context(ucomp, nullptr, nullptr, nullptr,
126                              GetContext::kNotFound, Slice(user_keys[i]), &value,
127                              nullptr, nullptr, true, nullptr, nullptr);
128       ASSERT_OK(
129           reader.Get(ReadOptions(), Slice(keys[i]), &get_context, nullptr));
130       ASSERT_STREQ(values[i].c_str(), value.data());
131     }
132   }
UpdateKeys(bool with_zero_seqno)133   void UpdateKeys(bool with_zero_seqno) {
134     for (uint32_t i = 0; i < num_items; i++) {
135       ParsedInternalKey ikey(user_keys[i],
136           with_zero_seqno ? 0 : i + 1000, kTypeValue);
137       keys[i].clear();
138       AppendInternalKey(&keys[i], ikey);
139     }
140   }
141 
CheckIterator(const Comparator * ucomp=BytewiseComparator ())142   void CheckIterator(const Comparator* ucomp = BytewiseComparator()) {
143     std::unique_ptr<RandomAccessFile> read_file;
144     ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
145     std::unique_ptr<RandomAccessFileReader> file_reader(
146         new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(read_file),
147                                    fname));
148     const ImmutableCFOptions ioptions(options);
149     CuckooTableReader reader(ioptions, std::move(file_reader), file_size, ucomp,
150                              GetSliceHash);
151     ASSERT_OK(reader.status());
152     InternalIterator* it = reader.NewIterator(
153         ReadOptions(), /*prefix_extractor=*/nullptr, /*arena=*/nullptr,
154         /*skip_filters=*/false, TableReaderCaller::kUncategorized);
155     ASSERT_OK(it->status());
156     ASSERT_TRUE(!it->Valid());
157     it->SeekToFirst();
158     int cnt = 0;
159     while (it->Valid()) {
160       ASSERT_OK(it->status());
161       ASSERT_TRUE(Slice(keys[cnt]) == it->key());
162       ASSERT_TRUE(Slice(values[cnt]) == it->value());
163       ++cnt;
164       it->Next();
165     }
166     ASSERT_EQ(static_cast<uint32_t>(cnt), num_items);
167 
168     it->SeekToLast();
169     cnt = static_cast<int>(num_items) - 1;
170     ASSERT_TRUE(it->Valid());
171     while (it->Valid()) {
172       ASSERT_OK(it->status());
173       ASSERT_TRUE(Slice(keys[cnt]) == it->key());
174       ASSERT_TRUE(Slice(values[cnt]) == it->value());
175       --cnt;
176       it->Prev();
177     }
178     ASSERT_EQ(cnt, -1);
179 
180     cnt = static_cast<int>(num_items) / 2;
181     it->Seek(keys[cnt]);
182     while (it->Valid()) {
183       ASSERT_OK(it->status());
184       ASSERT_TRUE(Slice(keys[cnt]) == it->key());
185       ASSERT_TRUE(Slice(values[cnt]) == it->value());
186       ++cnt;
187       it->Next();
188     }
189     ASSERT_EQ(static_cast<uint32_t>(cnt), num_items);
190     delete it;
191 
192     Arena arena;
193     it = reader.NewIterator(ReadOptions(), /*prefix_extractor=*/nullptr, &arena,
194                             /*skip_filters=*/false,
195                             TableReaderCaller::kUncategorized);
196     ASSERT_OK(it->status());
197     ASSERT_TRUE(!it->Valid());
198     it->Seek(keys[num_items/2]);
199     ASSERT_TRUE(it->Valid());
200     ASSERT_OK(it->status());
201     ASSERT_TRUE(keys[num_items/2] == it->key());
202     ASSERT_TRUE(values[num_items/2] == it->value());
203     ASSERT_OK(it->status());
204     it->~InternalIterator();
205   }
206 
207   std::vector<std::string> keys;
208   std::vector<std::string> user_keys;
209   std::vector<std::string> values;
210   uint64_t num_items;
211   std::string fname;
212   uint64_t file_size;
213   Options options;
214   Env* env;
215   EnvOptions env_options;
216 };
217 
TEST_F(CuckooReaderTest,FileNotMmaped)218 TEST_F(CuckooReaderTest, FileNotMmaped) {
219   options.allow_mmap_reads = false;
220   ImmutableCFOptions ioptions(options);
221   CuckooTableReader reader(ioptions, nullptr, 0, nullptr, nullptr);
222   ASSERT_TRUE(reader.status().IsInvalidArgument());
223   ASSERT_STREQ("File is not mmaped", reader.status().getState());
224 }
225 
TEST_F(CuckooReaderTest,WhenKeyExists)226 TEST_F(CuckooReaderTest, WhenKeyExists) {
227   SetUp(kNumHashFunc);
228   fname = test::PerThreadDBPath("CuckooReader_WhenKeyExists");
229   for (uint64_t i = 0; i < num_items; i++) {
230     user_keys[i] = "key" + NumToStr(i);
231     ParsedInternalKey ikey(user_keys[i], i + 1000, kTypeValue);
232     AppendInternalKey(&keys[i], ikey);
233     values[i] = "value" + NumToStr(i);
234     // Give disjoint hash values.
235     AddHashLookups(user_keys[i], i, kNumHashFunc);
236   }
237   CreateCuckooFileAndCheckReader();
238   // Last level file.
239   UpdateKeys(true);
240   CreateCuckooFileAndCheckReader();
241   // Test with collision. Make all hash values collide.
242   hash_map.clear();
243   for (uint32_t i = 0; i < num_items; i++) {
244     AddHashLookups(user_keys[i], 0, kNumHashFunc);
245   }
246   UpdateKeys(false);
247   CreateCuckooFileAndCheckReader();
248   // Last level file.
249   UpdateKeys(true);
250   CreateCuckooFileAndCheckReader();
251 }
252 
TEST_F(CuckooReaderTest,WhenKeyExistsWithUint64Comparator)253 TEST_F(CuckooReaderTest, WhenKeyExistsWithUint64Comparator) {
254   SetUp(kNumHashFunc);
255   fname = test::PerThreadDBPath("CuckooReaderUint64_WhenKeyExists");
256   for (uint64_t i = 0; i < num_items; i++) {
257     user_keys[i].resize(8);
258     memcpy(&user_keys[i][0], static_cast<void*>(&i), 8);
259     ParsedInternalKey ikey(user_keys[i], i + 1000, kTypeValue);
260     AppendInternalKey(&keys[i], ikey);
261     values[i] = "value" + NumToStr(i);
262     // Give disjoint hash values.
263     AddHashLookups(user_keys[i], i, kNumHashFunc);
264   }
265   CreateCuckooFileAndCheckReader(test::Uint64Comparator());
266   // Last level file.
267   UpdateKeys(true);
268   CreateCuckooFileAndCheckReader(test::Uint64Comparator());
269   // Test with collision. Make all hash values collide.
270   hash_map.clear();
271   for (uint32_t i = 0; i < num_items; i++) {
272     AddHashLookups(user_keys[i], 0, kNumHashFunc);
273   }
274   UpdateKeys(false);
275   CreateCuckooFileAndCheckReader(test::Uint64Comparator());
276   // Last level file.
277   UpdateKeys(true);
278   CreateCuckooFileAndCheckReader(test::Uint64Comparator());
279 }
280 
TEST_F(CuckooReaderTest,CheckIterator)281 TEST_F(CuckooReaderTest, CheckIterator) {
282   SetUp(2*kNumHashFunc);
283   fname = test::PerThreadDBPath("CuckooReader_CheckIterator");
284   for (uint64_t i = 0; i < num_items; i++) {
285     user_keys[i] = "key" + NumToStr(i);
286     ParsedInternalKey ikey(user_keys[i], 1000, kTypeValue);
287     AppendInternalKey(&keys[i], ikey);
288     values[i] = "value" + NumToStr(i);
289     // Give disjoint hash values, in reverse order.
290     AddHashLookups(user_keys[i], num_items-i-1, kNumHashFunc);
291   }
292   CreateCuckooFileAndCheckReader();
293   CheckIterator();
294   // Last level file.
295   UpdateKeys(true);
296   CreateCuckooFileAndCheckReader();
297   CheckIterator();
298 }
299 
TEST_F(CuckooReaderTest,CheckIteratorUint64)300 TEST_F(CuckooReaderTest, CheckIteratorUint64) {
301   SetUp(2*kNumHashFunc);
302   fname = test::PerThreadDBPath("CuckooReader_CheckIterator");
303   for (uint64_t i = 0; i < num_items; i++) {
304     user_keys[i].resize(8);
305     memcpy(&user_keys[i][0], static_cast<void*>(&i), 8);
306     ParsedInternalKey ikey(user_keys[i], 1000, kTypeValue);
307     AppendInternalKey(&keys[i], ikey);
308     values[i] = "value" + NumToStr(i);
309     // Give disjoint hash values, in reverse order.
310     AddHashLookups(user_keys[i], num_items-i-1, kNumHashFunc);
311   }
312   CreateCuckooFileAndCheckReader(test::Uint64Comparator());
313   CheckIterator(test::Uint64Comparator());
314   // Last level file.
315   UpdateKeys(true);
316   CreateCuckooFileAndCheckReader(test::Uint64Comparator());
317   CheckIterator(test::Uint64Comparator());
318 }
319 
TEST_F(CuckooReaderTest,WhenKeyNotFound)320 TEST_F(CuckooReaderTest, WhenKeyNotFound) {
321   // Add keys with colliding hash values.
322   SetUp(kNumHashFunc);
323   fname = test::PerThreadDBPath("CuckooReader_WhenKeyNotFound");
324   for (uint64_t i = 0; i < num_items; i++) {
325     user_keys[i] = "key" + NumToStr(i);
326     ParsedInternalKey ikey(user_keys[i], i + 1000, kTypeValue);
327     AppendInternalKey(&keys[i], ikey);
328     values[i] = "value" + NumToStr(i);
329     // Make all hash values collide.
330     AddHashLookups(user_keys[i], 0, kNumHashFunc);
331   }
332   auto* ucmp = BytewiseComparator();
333   CreateCuckooFileAndCheckReader();
334   std::unique_ptr<RandomAccessFile> read_file;
335   ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
336   std::unique_ptr<RandomAccessFileReader> file_reader(
337       new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(read_file),
338                                  fname));
339   const ImmutableCFOptions ioptions(options);
340   CuckooTableReader reader(ioptions, std::move(file_reader), file_size, ucmp,
341                            GetSliceHash);
342   ASSERT_OK(reader.status());
343   // Search for a key with colliding hash values.
344   std::string not_found_user_key = "key" + NumToStr(num_items);
345   std::string not_found_key;
346   AddHashLookups(not_found_user_key, 0, kNumHashFunc);
347   ParsedInternalKey ikey(not_found_user_key, 1000, kTypeValue);
348   AppendInternalKey(&not_found_key, ikey);
349   PinnableSlice value;
350   GetContext get_context(ucmp, nullptr, nullptr, nullptr, GetContext::kNotFound,
351                          Slice(not_found_key), &value, nullptr, nullptr, true,
352                          nullptr, nullptr);
353   ASSERT_OK(
354       reader.Get(ReadOptions(), Slice(not_found_key), &get_context, nullptr));
355   ASSERT_TRUE(value.empty());
356   ASSERT_OK(reader.status());
357   // Search for a key with an independent hash value.
358   std::string not_found_user_key2 = "key" + NumToStr(num_items + 1);
359   AddHashLookups(not_found_user_key2, kNumHashFunc, kNumHashFunc);
360   ParsedInternalKey ikey2(not_found_user_key2, 1000, kTypeValue);
361   std::string not_found_key2;
362   AppendInternalKey(&not_found_key2, ikey2);
363   value.Reset();
364   GetContext get_context2(ucmp, nullptr, nullptr, nullptr,
365                           GetContext::kNotFound, Slice(not_found_key2), &value,
366                           nullptr, nullptr, true, nullptr, nullptr);
367   ASSERT_OK(
368       reader.Get(ReadOptions(), Slice(not_found_key2), &get_context2, nullptr));
369   ASSERT_TRUE(value.empty());
370   ASSERT_OK(reader.status());
371 
372   // Test read when key is unused key.
373   std::string unused_key =
374     reader.GetTableProperties()->user_collected_properties.at(
375     CuckooTablePropertyNames::kEmptyKey);
376   // Add hash values that map to empty buckets.
377   AddHashLookups(ExtractUserKey(unused_key).ToString(),
378       kNumHashFunc, kNumHashFunc);
379   value.Reset();
380   GetContext get_context3(ucmp, nullptr, nullptr, nullptr,
381                           GetContext::kNotFound, Slice(unused_key), &value,
382                           nullptr, nullptr, true, nullptr, nullptr);
383   ASSERT_OK(
384       reader.Get(ReadOptions(), Slice(unused_key), &get_context3, nullptr));
385   ASSERT_TRUE(value.empty());
386   ASSERT_OK(reader.status());
387 }
388 
389 // Performance tests
390 namespace {
GetKeys(uint64_t num,std::vector<std::string> * keys)391 void GetKeys(uint64_t num, std::vector<std::string>* keys) {
392   keys->clear();
393   IterKey k;
394   k.SetInternalKey("", 0, kTypeValue);
395   std::string internal_key_suffix = k.GetInternalKey().ToString();
396   ASSERT_EQ(static_cast<size_t>(8), internal_key_suffix.size());
397   for (uint64_t key_idx = 0; key_idx < num; ++key_idx) {
398     uint64_t value = 2 * key_idx;
399     std::string new_key(reinterpret_cast<char*>(&value), sizeof(value));
400     new_key += internal_key_suffix;
401     keys->push_back(new_key);
402   }
403 }
404 
GetFileName(uint64_t num)405 std::string GetFileName(uint64_t num) {
406   if (FLAGS_file_dir.empty()) {
407     FLAGS_file_dir = test::TmpDir();
408   }
409   return test::PerThreadDBPath(FLAGS_file_dir, "cuckoo_read_benchmark") +
410          ToString(num / 1000000) + "Mkeys";
411 }
412 
413 // Create last level file as we are interested in measuring performance of
414 // last level file only.
WriteFile(const std::vector<std::string> & keys,const uint64_t num,double hash_ratio)415 void WriteFile(const std::vector<std::string>& keys,
416     const uint64_t num, double hash_ratio) {
417   Options options;
418   options.allow_mmap_reads = true;
419   Env* env = options.env;
420   EnvOptions env_options = EnvOptions(options);
421   std::string fname = GetFileName(num);
422 
423   std::unique_ptr<WritableFile> writable_file;
424   ASSERT_OK(env->NewWritableFile(fname, &writable_file, env_options));
425   std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
426       NewLegacyWritableFileWrapper(std::move(writable_file)), fname,
427       env_options));
428   CuckooTableBuilder builder(
429       file_writer.get(), hash_ratio, 64, 1000, test::Uint64Comparator(), 5,
430       false, FLAGS_identity_as_first_hash, nullptr, 0 /* column_family_id */,
431       kDefaultColumnFamilyName);
432   ASSERT_OK(builder.status());
433   for (uint64_t key_idx = 0; key_idx < num; ++key_idx) {
434     // Value is just a part of key.
435     builder.Add(Slice(keys[key_idx]), Slice(&keys[key_idx][0], 4));
436     ASSERT_EQ(builder.NumEntries(), key_idx + 1);
437     ASSERT_OK(builder.status());
438   }
439   ASSERT_OK(builder.Finish());
440   ASSERT_EQ(num, builder.NumEntries());
441   ASSERT_OK(file_writer->Close());
442 
443   uint64_t file_size;
444   env->GetFileSize(fname, &file_size);
445   std::unique_ptr<RandomAccessFile> read_file;
446   ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
447   std::unique_ptr<RandomAccessFileReader> file_reader(
448       new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(read_file),
449                                  fname));
450 
451   const ImmutableCFOptions ioptions(options);
452   CuckooTableReader reader(ioptions, std::move(file_reader), file_size,
453                            test::Uint64Comparator(), nullptr);
454   ASSERT_OK(reader.status());
455   ReadOptions r_options;
456   PinnableSlice value;
457   // Assume only the fast path is triggered
458   GetContext get_context(nullptr, nullptr, nullptr, nullptr,
459                          GetContext::kNotFound, Slice(), &value, nullptr,
460                          nullptr, true, nullptr, nullptr);
461   for (uint64_t i = 0; i < num; ++i) {
462     value.Reset();
463     value.clear();
464     ASSERT_OK(reader.Get(r_options, Slice(keys[i]), &get_context, nullptr));
465     ASSERT_TRUE(Slice(keys[i]) == Slice(&keys[i][0], 4));
466   }
467 }
468 
ReadKeys(uint64_t num,uint32_t batch_size)469 void ReadKeys(uint64_t num, uint32_t batch_size) {
470   Options options;
471   options.allow_mmap_reads = true;
472   Env* env = options.env;
473   EnvOptions env_options = EnvOptions(options);
474   std::string fname = GetFileName(num);
475 
476   uint64_t file_size;
477   env->GetFileSize(fname, &file_size);
478   std::unique_ptr<RandomAccessFile> read_file;
479   ASSERT_OK(env->NewRandomAccessFile(fname, &read_file, env_options));
480   std::unique_ptr<RandomAccessFileReader> file_reader(
481       new RandomAccessFileReader(NewLegacyRandomAccessFileWrapper(read_file),
482                                  fname));
483 
484   const ImmutableCFOptions ioptions(options);
485   CuckooTableReader reader(ioptions, std::move(file_reader), file_size,
486                            test::Uint64Comparator(), nullptr);
487   ASSERT_OK(reader.status());
488   const UserCollectedProperties user_props =
489     reader.GetTableProperties()->user_collected_properties;
490   const uint32_t num_hash_fun = *reinterpret_cast<const uint32_t*>(
491       user_props.at(CuckooTablePropertyNames::kNumHashFunc).data());
492   const uint64_t table_size = *reinterpret_cast<const uint64_t*>(
493       user_props.at(CuckooTablePropertyNames::kHashTableSize).data());
494   fprintf(stderr, "With %" PRIu64 " items, utilization is %.2f%%, number of"
495       " hash functions: %u.\n", num, num * 100.0 / (table_size), num_hash_fun);
496   ReadOptions r_options;
497 
498   std::vector<uint64_t> keys;
499   keys.reserve(num);
500   for (uint64_t i = 0; i < num; ++i) {
501     keys.push_back(2 * i);
502   }
503   std::random_shuffle(keys.begin(), keys.end());
504 
505   PinnableSlice value;
506   // Assume only the fast path is triggered
507   GetContext get_context(nullptr, nullptr, nullptr, nullptr,
508                          GetContext::kNotFound, Slice(), &value, nullptr,
509                          nullptr, true, nullptr, nullptr);
510   uint64_t start_time = env->NowMicros();
511   if (batch_size > 0) {
512     for (uint64_t i = 0; i < num; i += batch_size) {
513       for (uint64_t j = i; j < i+batch_size && j < num; ++j) {
514         reader.Prepare(Slice(reinterpret_cast<char*>(&keys[j]), 16));
515       }
516       for (uint64_t j = i; j < i+batch_size && j < num; ++j) {
517         reader.Get(r_options, Slice(reinterpret_cast<char*>(&keys[j]), 16),
518                    &get_context, nullptr);
519       }
520     }
521   } else {
522     for (uint64_t i = 0; i < num; i++) {
523       reader.Get(r_options, Slice(reinterpret_cast<char*>(&keys[i]), 16),
524                  &get_context, nullptr);
525     }
526   }
527   float time_per_op = (env->NowMicros() - start_time) * 1.0f / num;
528   fprintf(stderr,
529       "Time taken per op is %.3fus (%.1f Mqps) with batch size of %u\n",
530       time_per_op, 1.0 / time_per_op, batch_size);
531 }
532 }  // namespace.
533 
TEST_F(CuckooReaderTest,TestReadPerformance)534 TEST_F(CuckooReaderTest, TestReadPerformance) {
535   if (!FLAGS_enable_perf) {
536     return;
537   }
538   double hash_ratio = 0.95;
539   // These numbers are chosen to have a hash utilization % close to
540   // 0.9, 0.75, 0.6 and 0.5 respectively.
541   // They all create 128 M buckets.
542   std::vector<uint64_t> nums = {120*1024*1024, 100*1024*1024, 80*1024*1024,
543     70*1024*1024};
544 #ifndef NDEBUG
545   fprintf(stdout,
546       "WARNING: Not compiled with DNDEBUG. Performance tests may be slow.\n");
547 #endif
548   for (uint64_t num : nums) {
549     if (FLAGS_write ||
550         Env::Default()->FileExists(GetFileName(num)).IsNotFound()) {
551       std::vector<std::string> all_keys;
552       GetKeys(num, &all_keys);
553       WriteFile(all_keys, num, hash_ratio);
554     }
555     ReadKeys(num, 0);
556     ReadKeys(num, 10);
557     ReadKeys(num, 25);
558     ReadKeys(num, 50);
559     ReadKeys(num, 100);
560     fprintf(stderr, "\n");
561   }
562 }
563 }  // namespace ROCKSDB_NAMESPACE
564 
main(int argc,char ** argv)565 int main(int argc, char** argv) {
566   if (ROCKSDB_NAMESPACE::port::kLittleEndian) {
567     ::testing::InitGoogleTest(&argc, argv);
568     ParseCommandLineFlags(&argc, &argv, true);
569     return RUN_ALL_TESTS();
570   } else {
571     fprintf(stderr, "SKIPPED as Cuckoo table doesn't support Big Endian\n");
572     return 0;
573   }
574 }
575 
576 #endif  // GFLAGS.
577 
578 #else
579 #include <stdio.h>
580 
main(int,char **)581 int main(int /*argc*/, char** /*argv*/) {
582   fprintf(stderr, "SKIPPED as Cuckoo table is not supported in ROCKSDB_LITE\n");
583   return 0;
584 }
585 
586 #endif  // ROCKSDB_LITE
587