[Only for Performance Branch] A Hacky patch to lazily generate memtable key for prefix-hashed memtables.

Summary:
For prefix mem tables, encoding mem table key may be unnecessary if the prefix doesn't have any key. This patch is a little bit hacky but I want to try out the performance gain of removing this lazy initialization.

In longer term, we might want to revisit the way we abstract mem tables implementations.

Test Plan: make all check

Reviewers: haobo, igor, kailiu

Reviewed By: igor

CC: leveldb

Differential Revision: https://reviews.facebook.net/D14265
This commit is contained in:
Siying Dong 2013-11-20 19:49:27 -08:00
parent 7b10fe9fac
commit 58e1956d50
7 changed files with 47 additions and 26 deletions

View file

@ -75,7 +75,7 @@ Slice MemTableRep::UserKey(const char* key) const {
// Encode a suitable internal key target for "target" and return it.
// Uses *scratch as scratch space, and the returned pointer will point
// into this scratch space.
static const char* EncodeKey(std::string* scratch, const Slice& target) {
const char* EncodeKey(std::string* scratch, const Slice& target) {
scratch->clear();
PutVarint32(scratch, target.size());
scratch->append(target.data(), target.size());
@ -96,7 +96,7 @@ class MemTableIterator: public Iterator {
}
virtual bool Valid() const { return iter_->Valid(); }
virtual void Seek(const Slice& k) { iter_->Seek(EncodeKey(&tmp_, k)); }
virtual void Seek(const Slice& k) { iter_->Seek(k, nullptr); }
virtual void SeekToFirst() { iter_->SeekToFirst(); }
virtual void SeekToLast() { iter_->SeekToLast(); }
virtual void Next() { iter_->Next(); }
@ -113,7 +113,6 @@ class MemTableIterator: public Iterator {
private:
std::shared_ptr<MemTableRep::Iterator> iter_;
std::string tmp_; // For passing to EncodeKey
// No copying allowed
MemTableIterator(const MemTableIterator&);
@ -165,7 +164,7 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
Slice memkey = key.memtable_key();
std::shared_ptr<MemTableRep::Iterator> iter(
table_->GetIterator(key.user_key()));
iter->Seek(memkey.data());
iter->Seek(key.user_key(), memkey.data());
// It is the caller's responsibility to allocate/delete operands list
assert(operands != nullptr);
@ -274,7 +273,7 @@ bool MemTable::Update(SequenceNumber seq, ValueType type,
std::shared_ptr<MemTableRep::Iterator> iter(
table_.get()->GetIterator(lkey.user_key()));
iter->Seek(memkey.data());
iter->Seek(key, memkey.data());
if (iter->Valid()) {
// entry format is:

View file

@ -169,4 +169,6 @@ class MemTable {
port::RWMutex* GetLock(const Slice& key);
};
extern const char* EncodeKey(std::string* scratch, const Slice& target);
} // namespace rocksdb

View file

@ -107,7 +107,7 @@ class MemTableRep {
virtual void Prev() = 0;
// Advance to the first entry with a key >= target
virtual void Seek(const char* target) = 0;
virtual void Seek(const Slice& user_key, const char* memtable_key) = 0;
// Position at the first entry in collection.
// Final state of iterator is Valid() iff collection is not empty.

View file

@ -11,6 +11,7 @@
#include "port/port.h"
#include "port/atomic_pointer.h"
#include "util/murmurhash.h"
#include "db/memtable.h"
#include "db/skiplist.h"
namespace rocksdb {
@ -112,9 +113,12 @@ class HashSkipListRep : public MemTableRep {
}
// Advance to the first entry with a key >= target
virtual void Seek(const char* target) {
virtual void Seek(const Slice& user_key, const char* memtable_key) {
if (list_ != nullptr) {
iter_.Seek(target);
const char* encoded_key =
(memtable_key != nullptr) ?
memtable_key : EncodeKey(&tmp_, user_key);
iter_.Seek(encoded_key);
}
}
@ -151,6 +155,7 @@ class HashSkipListRep : public MemTableRep {
// here we track if we own list_. If we own it, we are also
// responsible for it's cleaning. This is a poor man's shared_ptr
bool own_list_;
std::string tmp_; // For passing to EncodeKey
};
class DynamicIterator : public HashSkipListRep::Iterator {
@ -160,11 +165,10 @@ class HashSkipListRep : public MemTableRep {
memtable_rep_(memtable_rep) {}
// Advance to the first entry with a key >= target
virtual void Seek(const char* target) {
auto transformed = memtable_rep_.transform_->Transform(
memtable_rep_.UserKey(target));
virtual void Seek(const Slice& k, const char* memtable_key) {
auto transformed = memtable_rep_.transform_->Transform(k);
Reset(memtable_rep_.GetBucket(transformed));
HashSkipListRep::Iterator::Seek(target);
HashSkipListRep::Iterator::Seek(k, memtable_key);
}
// Position at the first entry in collection.
@ -201,7 +205,7 @@ class HashSkipListRep : public MemTableRep {
}
virtual void Next() { }
virtual void Prev() { }
virtual void Seek(const char* target) { }
virtual void Seek(const Slice& user_key, const char* memtable_key) { }
virtual void SeekToFirst() { }
virtual void SeekToLast() { }
private:

View file

@ -70,8 +70,13 @@ public:
}
// Advance to the first entry with a key >= target
virtual void Seek(const char* target) override {
iter_.Seek(target);
virtual void Seek(const Slice& user_key, const char* memtable_key)
override {
if (memtable_key != nullptr) {
iter_.Seek(memtable_key);
} else {
iter_.Seek(EncodeKey(&tmp_, user_key));
}
}
// Position at the first entry in list.
@ -85,6 +90,8 @@ public:
virtual void SeekToLast() override {
iter_.SeekToLast();
}
protected:
std::string tmp_; // For passing to EncodeKey
};
// Unhide default implementations of GetIterator

View file

@ -13,6 +13,7 @@
#include "rocksdb/arena.h"
#include "rocksdb/slice.h"
#include "rocksdb/slice_transform.h"
#include "db/memtable.h"
#include "port/port.h"
#include "util/mutexlock.h"
#include "util/murmurhash.h"
@ -110,7 +111,7 @@ class TransformRep : public MemTableRep {
virtual void Prev();
// Advance to the first entry with a key >= target
virtual void Seek(const char* target);
virtual void Seek(const Slice& user_key, const char* memtable_key);
// Position at the first entry in collection.
// Final state of iterator is Valid() iff collection is not empty.
@ -122,6 +123,7 @@ class TransformRep : public MemTableRep {
private:
std::shared_ptr<Bucket> items_;
Bucket::const_iterator cit_;
std::string tmp_; // For passing to EncodeKey
};
class EmptyIterator : public MemTableRep::Iterator {
@ -137,7 +139,7 @@ class TransformRep : public MemTableRep {
}
virtual void Next() { }
virtual void Prev() { }
virtual void Seek(const char* target) { }
virtual void Seek(const Slice& user_key, const char* memtable_key) { }
virtual void SeekToFirst() { }
virtual void SeekToLast() { }
static std::shared_ptr<EmptyIterator> GetInstance();
@ -197,9 +199,8 @@ class TransformRep : public MemTableRep {
// Advance to the first entry with a key >= target within the
// same bucket as target
virtual void Seek(const char* target) {
Slice prefix = memtable_rep_.transform_->Transform(
memtable_rep_.UserKey(target));
virtual void Seek(const Slice& user_key, const char* memtable_key) {
Slice prefix = memtable_rep_.transform_->Transform(user_key);
ReadLock l(&memtable_rep_.rwlock_);
auto bucket = memtable_rep_.buckets_.find(prefix);
@ -208,7 +209,7 @@ class TransformRep : public MemTableRep {
} else {
bucket_iterator_.reset(
new TransformIterator(bucket->second, memtable_rep_.GetLock(prefix)));
bucket_iterator_->Seek(target);
bucket_iterator_->Seek(user_key, memtable_key);
}
}
@ -343,8 +344,11 @@ void TransformRep::Iterator::Prev() {
}
// Advance to the first entry with a key >= target
void TransformRep::Iterator::Seek(const char* target) {
cit_ = items_->lower_bound(target);
void TransformRep::Iterator::Seek(const Slice& user_key,
const char* memtable_key) {
const char* encoded_key =
(memtable_key != nullptr) ? memtable_key : EncodeKey(&tmp_, user_key);
cit_ = items_->lower_bound(encoded_key);
}
// Position at the first entry in collection.

View file

@ -12,6 +12,7 @@
#include <type_traits>
#include "rocksdb/arena.h"
#include "db/memtable.h"
#include "port/port.h"
#include "util/mutexlock.h"
#include "util/stl_wrappers.h"
@ -45,6 +46,7 @@ class VectorRep : public MemTableRep {
std::shared_ptr<std::vector<const char*>> bucket_;
typename std::vector<const char*>::const_iterator mutable cit_;
const KeyComparator& compare_;
std::string tmp_; // For passing to EncodeKey
bool mutable sorted_;
void DoSort() const;
public:
@ -73,7 +75,7 @@ class VectorRep : public MemTableRep {
virtual void Prev() override;
// Advance to the first entry with a key >= target
virtual void Seek(const char* target) override;
virtual void Seek(const Slice& user_key, const char* memtable_key) override;
// Position at the first entry in collection.
// Final state of iterator is Valid() iff collection is not empty.
@ -200,12 +202,15 @@ void VectorRep::Iterator::Prev() {
}
// Advance to the first entry with a key >= target
void VectorRep::Iterator::Seek(const char* target) {
void VectorRep::Iterator::Seek(const Slice& user_key,
const char* memtable_key) {
DoSort();
// Do binary search to find first value not less than the target
const char* encoded_key =
(memtable_key != nullptr) ? memtable_key : EncodeKey(&tmp_, user_key);
cit_ = std::equal_range(bucket_->begin(),
bucket_->end(),
target,
encoded_key,
[this] (const char* a, const char* b) {
return compare_(a, b) < 0;
}).first;