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(¬_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(¬_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