24
24
namespace rocksdb {
25
25
const std::string CuckooTablePropertyNames::kEmptyKey =
26
26
" rocksdb.cuckoo.bucket.empty.key" ;
27
- const std::string CuckooTablePropertyNames::kNumHashTable =
27
+ const std::string CuckooTablePropertyNames::kNumHashFunc =
28
28
" rocksdb.cuckoo.hash.num" ;
29
- const std::string CuckooTablePropertyNames::kMaxNumBuckets =
30
- " rocksdb.cuckoo.bucket.maxnum " ;
29
+ const std::string CuckooTablePropertyNames::kHashTableSize =
30
+ " rocksdb.cuckoo.hash.size " ;
31
31
const std::string CuckooTablePropertyNames::kValueLength =
32
32
" rocksdb.cuckoo.value.length" ;
33
33
const std::string CuckooTablePropertyNames::kIsLastLevel =
34
34
" rocksdb.cuckoo.file.islastlevel" ;
35
+ const std::string CuckooTablePropertyNames::kCuckooBlockSize =
36
+ " rocksdb.cuckoo.hash.cuckooblocksize" ;
35
37
36
38
// Obtained by running echo rocksdb.table.cuckoo | sha1sum
37
39
extern const uint64_t kCuckooTableMagicNumber = 0x926789d0c5f17873ull ;
38
40
39
41
CuckooTableBuilder::CuckooTableBuilder (
40
42
WritableFile* file, double hash_table_ratio,
41
43
uint32_t max_num_hash_table, uint32_t max_search_depth,
42
- const Comparator* user_comparator,
44
+ const Comparator* user_comparator, uint32_t cuckoo_block_size,
43
45
uint64_t (*get_slice_hash)(const Slice&, uint32_t , uint64_t ))
44
- : num_hash_table_ (2 ),
46
+ : num_hash_func_ (2 ),
45
47
file_(file),
46
48
hash_table_ratio_(hash_table_ratio),
47
- max_num_hash_table_ (max_num_hash_table),
49
+ max_num_hash_func_ (max_num_hash_table),
48
50
max_search_depth_(max_search_depth),
51
+ cuckoo_block_size_(std::max(1U , cuckoo_block_size)),
49
52
is_last_level_file_(false ),
50
53
has_seen_first_key_(false ),
51
54
ucomp_(user_comparator),
@@ -101,48 +104,58 @@ void CuckooTableBuilder::Add(const Slice& key, const Slice& value) {
101
104
}
102
105
103
106
Status CuckooTableBuilder::MakeHashTable (std::vector<CuckooBucket>* buckets) {
104
- uint64_t num_buckets = kvs_.size () / hash_table_ratio_;
105
- buckets->resize (num_buckets );
107
+ uint64_t hash_table_size = kvs_.size () / hash_table_ratio_;
108
+ buckets->resize (hash_table_size + cuckoo_block_size_ - 1 );
106
109
uint64_t make_space_for_key_call_id = 0 ;
107
110
for (uint32_t vector_idx = 0 ; vector_idx < kvs_.size (); vector_idx++) {
108
111
uint64_t bucket_id;
109
112
bool bucket_found = false ;
110
113
autovector<uint64_t > hash_vals;
111
114
Slice user_key = is_last_level_file_ ? kvs_[vector_idx].first :
112
115
ExtractUserKey (kvs_[vector_idx].first );
113
- for (uint32_t hash_cnt = 0 ; hash_cnt < num_hash_table_; ++hash_cnt) {
114
- uint64_t hash_val = get_slice_hash_ (user_key, hash_cnt, num_buckets);
115
- if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx ) {
116
- bucket_id = hash_val;
117
- bucket_found = true ;
118
- break ;
119
- } else {
120
- if (ucomp_->Compare (user_key, is_last_level_file_
121
- ? Slice (kvs_[(*buckets)[hash_val].vector_idx ].first )
122
- : ExtractUserKey (
123
- kvs_[(*buckets)[hash_val].vector_idx ].first )) == 0 ) {
124
- return Status::NotSupported (" Same key is being inserted again." );
116
+ for (uint32_t hash_cnt = 0 ; hash_cnt < num_hash_func_ && !bucket_found;
117
+ ++hash_cnt) {
118
+ uint64_t hash_val = get_slice_hash_ (user_key, hash_cnt, hash_table_size);
119
+ // If there is a collision, check next cuckoo_block_size_ locations for
120
+ // empty locations. While checking, if we reach end of the hash table,
121
+ // stop searching and proceed for next hash function.
122
+ for (uint32_t block_idx = 0 ; block_idx < cuckoo_block_size_;
123
+ ++block_idx, ++hash_val) {
124
+ if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx ) {
125
+ bucket_id = hash_val;
126
+ bucket_found = true ;
127
+ break ;
128
+ } else {
129
+ if (ucomp_->Compare (user_key, is_last_level_file_
130
+ ? Slice (kvs_[(*buckets)[hash_val].vector_idx ].first )
131
+ : ExtractUserKey (
132
+ kvs_[(*buckets)[hash_val].vector_idx ].first )) == 0 ) {
133
+ return Status::NotSupported (" Same key is being inserted again." );
134
+ }
135
+ hash_vals.push_back (hash_val);
125
136
}
126
- hash_vals.push_back (hash_val);
127
137
}
128
138
}
129
139
while (!bucket_found && !MakeSpaceForKey (hash_vals,
130
- ++make_space_for_key_call_id, buckets, &bucket_id)) {
140
+ hash_table_size, ++make_space_for_key_call_id, buckets, &bucket_id)) {
131
141
// Rehash by increashing number of hash tables.
132
- if (num_hash_table_ >= max_num_hash_table_ ) {
133
- return Status::NotSupported (" Too many collissions . Unable to hash." );
142
+ if (num_hash_func_ >= max_num_hash_func_ ) {
143
+ return Status::NotSupported (" Too many collisions . Unable to hash." );
134
144
}
135
145
// We don't really need to rehash the entire table because old hashes are
136
146
// still valid and we only increased the number of hash functions.
137
147
uint64_t hash_val = get_slice_hash_ (user_key,
138
- num_hash_table_, num_buckets);
139
- ++num_hash_table_;
140
- if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx ) {
141
- bucket_found = true ;
142
- bucket_id = hash_val;
143
- break ;
144
- } else {
145
- hash_vals.push_back (hash_val);
148
+ num_hash_func_, hash_table_size);
149
+ ++num_hash_func_;
150
+ for (uint32_t block_idx = 0 ; block_idx < cuckoo_block_size_;
151
+ ++block_idx, ++hash_val) {
152
+ if ((*buckets)[hash_val].vector_idx == kMaxVectorIdx ) {
153
+ bucket_found = true ;
154
+ bucket_id = hash_val;
155
+ break ;
156
+ } else {
157
+ hash_vals.push_back (hash_val);
158
+ }
146
159
}
147
160
}
148
161
(*buckets)[bucket_id].vector_idx = vector_idx;
@@ -226,16 +239,22 @@ Status CuckooTableBuilder::Finish() {
226
239
properties_.user_collected_properties [
227
240
CuckooTablePropertyNames::kEmptyKey ] = unused_bucket;
228
241
properties_.user_collected_properties [
229
- CuckooTablePropertyNames::kNumHashTable ].assign (
230
- reinterpret_cast <char *>(&num_hash_table_), sizeof (num_hash_table_));
231
- uint64_t num_buckets = buckets.size ();
242
+ CuckooTablePropertyNames::kNumHashFunc ].assign (
243
+ reinterpret_cast <char *>(&num_hash_func_), sizeof (num_hash_func_));
244
+
245
+ uint64_t hash_table_size = buckets.size () - cuckoo_block_size_ + 1 ;
232
246
properties_.user_collected_properties [
233
- CuckooTablePropertyNames::kMaxNumBuckets ].assign (
234
- reinterpret_cast <const char *>(&num_buckets), sizeof (num_buckets));
247
+ CuckooTablePropertyNames::kHashTableSize ].assign (
248
+ reinterpret_cast <const char *>(&hash_table_size),
249
+ sizeof (hash_table_size));
235
250
properties_.user_collected_properties [
236
251
CuckooTablePropertyNames::kIsLastLevel ].assign (
237
252
reinterpret_cast <const char *>(&is_last_level_file_),
238
253
sizeof (is_last_level_file_));
254
+ properties_.user_collected_properties [
255
+ CuckooTablePropertyNames::kCuckooBlockSize ].assign (
256
+ reinterpret_cast <const char *>(&cuckoo_block_size_),
257
+ sizeof (cuckoo_block_size_));
239
258
240
259
// Write meta blocks.
241
260
MetaIndexBuilder meta_index_builder;
@@ -307,6 +326,7 @@ uint64_t CuckooTableBuilder::FileSize() const {
307
326
// If tree depth exceedes max depth, we return false indicating failure.
308
327
bool CuckooTableBuilder::MakeSpaceForKey (
309
328
const autovector<uint64_t >& hash_vals,
329
+ const uint64_t hash_table_size,
310
330
const uint64_t make_space_for_key_call_id,
311
331
std::vector<CuckooBucket>* buckets,
312
332
uint64_t * bucket_id) {
@@ -322,12 +342,13 @@ bool CuckooTableBuilder::MakeSpaceForKey(
322
342
std::vector<CuckooNode> tree;
323
343
// We want to identify already visited buckets in the current method call so
324
344
// that we don't add same buckets again for exploration in the tree.
325
- // We do this by maintaining a count of current method call, which acts as a
326
- // unique id for this invocation of the method. We store this number into
327
- // the nodes that we explore in current method call.
345
+ // We do this by maintaining a count of current method call in
346
+ // make_space_for_key_call_id, which acts as a unique id for this invocation
347
+ // of the method. We store this number into the nodes that we explore in
348
+ // current method call.
328
349
// It is unlikely for the increment operation to overflow because the maximum
329
- // no. of times this will be called is <= max_num_hash_table_ + kvs_.size().
330
- for (uint32_t hash_cnt = 0 ; hash_cnt < num_hash_table_ ; ++hash_cnt) {
350
+ // no. of times this will be called is <= max_num_hash_func_ + kvs_.size().
351
+ for (uint32_t hash_cnt = 0 ; hash_cnt < num_hash_func_ ; ++hash_cnt) {
331
352
uint64_t bucket_id = hash_vals[hash_cnt];
332
353
(*buckets)[bucket_id].make_space_for_key_call_id =
333
354
make_space_for_key_call_id;
@@ -342,22 +363,26 @@ bool CuckooTableBuilder::MakeSpaceForKey(
342
363
break ;
343
364
}
344
365
CuckooBucket& curr_bucket = (*buckets)[curr_node.bucket_id ];
345
- for (uint32_t hash_cnt = 0 ; hash_cnt < num_hash_table_; ++hash_cnt) {
366
+ for (uint32_t hash_cnt = 0 ;
367
+ hash_cnt < num_hash_func_ && !null_found; ++hash_cnt) {
346
368
uint64_t child_bucket_id = get_slice_hash_ (
347
369
is_last_level_file_ ? kvs_[curr_bucket.vector_idx ].first
348
370
: ExtractUserKey (Slice (kvs_[curr_bucket.vector_idx ].first )),
349
- hash_cnt, buckets->size ());
350
- if ((*buckets)[child_bucket_id].make_space_for_key_call_id ==
351
- make_space_for_key_call_id) {
352
- continue ;
353
- }
354
- (*buckets)[child_bucket_id].make_space_for_key_call_id =
355
- make_space_for_key_call_id;
356
- tree.push_back (CuckooNode (child_bucket_id, curr_depth + 1 ,
357
- curr_pos));
358
- if ((*buckets)[child_bucket_id].vector_idx == kMaxVectorIdx ) {
359
- null_found = true ;
360
- break ;
371
+ hash_cnt, hash_table_size);
372
+ for (uint32_t block_idx = 0 ; block_idx < cuckoo_block_size_;
373
+ ++block_idx, ++child_bucket_id) {
374
+ if ((*buckets)[child_bucket_id].make_space_for_key_call_id ==
375
+ make_space_for_key_call_id) {
376
+ continue ;
377
+ }
378
+ (*buckets)[child_bucket_id].make_space_for_key_call_id =
379
+ make_space_for_key_call_id;
380
+ tree.push_back (CuckooNode (child_bucket_id, curr_depth + 1 ,
381
+ curr_pos));
382
+ if ((*buckets)[child_bucket_id].vector_idx == kMaxVectorIdx ) {
383
+ null_found = true ;
384
+ break ;
385
+ }
361
386
}
362
387
}
363
388
++curr_pos;
@@ -367,10 +392,10 @@ bool CuckooTableBuilder::MakeSpaceForKey(
367
392
// There is an empty node in tree.back(). Now, traverse the path from this
368
393
// empty node to top of the tree and at every node in the path, replace
369
394
// child with the parent. Stop when first level is reached in the tree
370
- // (happens when 0 <= bucket_to_replace_pos < num_hash_table_ ) and return
395
+ // (happens when 0 <= bucket_to_replace_pos < num_hash_func_ ) and return
371
396
// this location in first level for target key to be inserted.
372
397
uint32_t bucket_to_replace_pos = tree.size ()-1 ;
373
- while (bucket_to_replace_pos >= num_hash_table_ ) {
398
+ while (bucket_to_replace_pos >= num_hash_func_ ) {
374
399
CuckooNode& curr_node = tree[bucket_to_replace_pos];
375
400
(*buckets)[curr_node.bucket_id ] =
376
401
(*buckets)[tree[curr_node.parent_pos ].bucket_id ];
0 commit comments