|
|
|
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
|
|
|
// This source code is licensed under the BSD-style license found in the
|
|
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
//
|
|
|
|
#include "rocksdb/memtablerep.h"
|
|
|
|
#include "db/memtable.h"
|
|
|
|
#include "db/skiplist.h"
|
In DB::NewIterator(), try to allocate the whole iterator tree in an arena
Summary:
In this patch, try to allocate the whole iterator tree starting from DBIter from an arena
1. ArenaWrappedDBIter is created when serves as the entry point of an iterator tree, with an arena in it.
2. Add an option to create iterator from arena for following iterators: DBIter, MergingIterator, MemtableIterator, all mem table's iterators, all table reader's iterators and two level iterator.
3. MergeIteratorBuilder is created to incrementally build the tree of internal iterators. It is passed to mem table list and version set and add iterators to it.
Limitations:
(1) Only DB::NewIterator() without tailing uses the arena. Other cases, including readonly DB and compactions are still from malloc
(2) Two level iterator itself is allocated in arena, but not iterators inside it.
Test Plan: make all check
Reviewers: ljin, haobo
Reviewed By: haobo
Subscribers: leveldb, dhruba, yhchiang, igor
Differential Revision: https://reviews.facebook.net/D18513
11 years ago
|
|
|
#include "util/arena.h"
|
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
namespace {
|
|
|
|
class SkipListRep : public MemTableRep {
|
|
|
|
SkipList<const char*, const MemTableRep::KeyComparator&> skip_list_;
|
SkipListRep::LookaheadIterator
Summary:
This diff introduces the `lookahead` argument to `SkipListFactory()`. This is an
optimization for the tailing use case which includes many seeks. E.g. consider
the following operations on a skip list iterator:
Seek(x), Next(), Next(), Seek(x+2), Next(), Seek(x+3), Next(), Next(), ...
If `lookahead` is positive, `SkipListRep` will return an iterator which also
keeps track of the previously visited node. Seek() then first does a linear
search starting from that node (up to `lookahead` steps). As in the tailing
example above, this may require fewer than ~log(n) comparisons as with regular
skip list search.
Test Plan:
Added a new benchmark (`fillseekseq`) which simulates the usage pattern. It
first writes N records (with consecutive keys), then measures how much time it
takes to read them by calling `Seek()` and `Next()`.
$ time ./db_bench -num 10000000 -benchmarks fillseekseq -prefix_size 1 \
-key_size 8 -write_buffer_size $[1024*1024*1024] -value_size 50 \
-seekseq_next 2 -skip_list_lookahead=0
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.389 micros/op 2569047 ops/sec;
real 0m21.806s
user 0m12.106s
sys 0m9.672s
$ time ./db_bench [...] -skip_list_lookahead=2
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.153 micros/op 6540684 ops/sec;
real 0m19.469s
user 0m10.192s
sys 0m9.252s
Reviewers: ljin, sdong, igor
Reviewed By: igor
Subscribers: dhruba, leveldb, march, lovro
Differential Revision: https://reviews.facebook.net/D23997
10 years ago
|
|
|
const MemTableRep::KeyComparator& cmp_;
|
|
|
|
const SliceTransform* transform_;
|
|
|
|
const size_t lookahead_;
|
|
|
|
|
|
|
|
friend class LookaheadIterator;
|
|
|
|
public:
|
SkipListRep::LookaheadIterator
Summary:
This diff introduces the `lookahead` argument to `SkipListFactory()`. This is an
optimization for the tailing use case which includes many seeks. E.g. consider
the following operations on a skip list iterator:
Seek(x), Next(), Next(), Seek(x+2), Next(), Seek(x+3), Next(), Next(), ...
If `lookahead` is positive, `SkipListRep` will return an iterator which also
keeps track of the previously visited node. Seek() then first does a linear
search starting from that node (up to `lookahead` steps). As in the tailing
example above, this may require fewer than ~log(n) comparisons as with regular
skip list search.
Test Plan:
Added a new benchmark (`fillseekseq`) which simulates the usage pattern. It
first writes N records (with consecutive keys), then measures how much time it
takes to read them by calling `Seek()` and `Next()`.
$ time ./db_bench -num 10000000 -benchmarks fillseekseq -prefix_size 1 \
-key_size 8 -write_buffer_size $[1024*1024*1024] -value_size 50 \
-seekseq_next 2 -skip_list_lookahead=0
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.389 micros/op 2569047 ops/sec;
real 0m21.806s
user 0m12.106s
sys 0m9.672s
$ time ./db_bench [...] -skip_list_lookahead=2
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.153 micros/op 6540684 ops/sec;
real 0m19.469s
user 0m10.192s
sys 0m9.252s
Reviewers: ljin, sdong, igor
Reviewed By: igor
Subscribers: dhruba, leveldb, march, lovro
Differential Revision: https://reviews.facebook.net/D23997
10 years ago
|
|
|
explicit SkipListRep(const MemTableRep::KeyComparator& compare, Arena* arena,
|
|
|
|
const SliceTransform* transform, const size_t lookahead)
|
|
|
|
: MemTableRep(arena), skip_list_(compare, arena), cmp_(compare),
|
|
|
|
transform_(transform), lookahead_(lookahead) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert key into the list.
|
|
|
|
// REQUIRES: nothing that compares equal to key is currently in the list.
|
|
|
|
virtual void Insert(KeyHandle handle) override {
|
|
|
|
skip_list_.Insert(static_cast<char*>(handle));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true iff an entry that compares equal to key is in the list.
|
|
|
|
virtual bool Contains(const char* key) const override {
|
|
|
|
return skip_list_.Contains(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual size_t ApproximateMemoryUsage() override {
|
|
|
|
// All memory is allocated through arena; nothing to report here
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Get(const LookupKey& k, void* callback_args,
|
|
|
|
bool (*callback_func)(void* arg,
|
|
|
|
const char* entry)) override {
|
|
|
|
SkipListRep::Iterator iter(&skip_list_);
|
|
|
|
Slice dummy_slice;
|
|
|
|
for (iter.Seek(dummy_slice, k.memtable_key().data());
|
|
|
|
iter.Valid() && callback_func(callback_args, iter.key());
|
|
|
|
iter.Next()) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~SkipListRep() override { }
|
|
|
|
|
|
|
|
// Iteration over the contents of a skip list
|
|
|
|
class Iterator : public MemTableRep::Iterator {
|
|
|
|
SkipList<const char*, const MemTableRep::KeyComparator&>::Iterator iter_;
|
|
|
|
public:
|
|
|
|
// Initialize an iterator over the specified list.
|
|
|
|
// The returned iterator is not valid.
|
|
|
|
explicit Iterator(
|
|
|
|
const SkipList<const char*, const MemTableRep::KeyComparator&>* list
|
|
|
|
) : iter_(list) { }
|
|
|
|
|
|
|
|
virtual ~Iterator() override { }
|
|
|
|
|
|
|
|
// Returns true iff the iterator is positioned at a valid node.
|
|
|
|
virtual bool Valid() const override {
|
|
|
|
return iter_.Valid();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the key at the current position.
|
|
|
|
// REQUIRES: Valid()
|
|
|
|
virtual const char* key() const override {
|
|
|
|
return iter_.key();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advances to the next position.
|
|
|
|
// REQUIRES: Valid()
|
|
|
|
virtual void Next() override {
|
|
|
|
iter_.Next();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advances to the previous position.
|
|
|
|
// REQUIRES: Valid()
|
|
|
|
virtual void Prev() override {
|
|
|
|
iter_.Prev();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advance to the first entry with a key >= 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.
|
|
|
|
// Final state of iterator is Valid() iff list is not empty.
|
|
|
|
virtual void SeekToFirst() override {
|
|
|
|
iter_.SeekToFirst();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Position at the last entry in list.
|
|
|
|
// Final state of iterator is Valid() iff list is not empty.
|
|
|
|
virtual void SeekToLast() override {
|
|
|
|
iter_.SeekToLast();
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
std::string tmp_; // For passing to EncodeKey
|
|
|
|
};
|
|
|
|
|
SkipListRep::LookaheadIterator
Summary:
This diff introduces the `lookahead` argument to `SkipListFactory()`. This is an
optimization for the tailing use case which includes many seeks. E.g. consider
the following operations on a skip list iterator:
Seek(x), Next(), Next(), Seek(x+2), Next(), Seek(x+3), Next(), Next(), ...
If `lookahead` is positive, `SkipListRep` will return an iterator which also
keeps track of the previously visited node. Seek() then first does a linear
search starting from that node (up to `lookahead` steps). As in the tailing
example above, this may require fewer than ~log(n) comparisons as with regular
skip list search.
Test Plan:
Added a new benchmark (`fillseekseq`) which simulates the usage pattern. It
first writes N records (with consecutive keys), then measures how much time it
takes to read them by calling `Seek()` and `Next()`.
$ time ./db_bench -num 10000000 -benchmarks fillseekseq -prefix_size 1 \
-key_size 8 -write_buffer_size $[1024*1024*1024] -value_size 50 \
-seekseq_next 2 -skip_list_lookahead=0
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.389 micros/op 2569047 ops/sec;
real 0m21.806s
user 0m12.106s
sys 0m9.672s
$ time ./db_bench [...] -skip_list_lookahead=2
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.153 micros/op 6540684 ops/sec;
real 0m19.469s
user 0m10.192s
sys 0m9.252s
Reviewers: ljin, sdong, igor
Reviewed By: igor
Subscribers: dhruba, leveldb, march, lovro
Differential Revision: https://reviews.facebook.net/D23997
10 years ago
|
|
|
// Iterator over the contents of a skip list which also keeps track of the
|
|
|
|
// previously visited node. In Seek(), it examines a few nodes after it
|
|
|
|
// first, falling back to O(log n) search from the head of the list only if
|
|
|
|
// the target key hasn't been found.
|
|
|
|
class LookaheadIterator : public MemTableRep::Iterator {
|
|
|
|
public:
|
|
|
|
explicit LookaheadIterator(const SkipListRep& rep) :
|
|
|
|
rep_(rep), iter_(&rep_.skip_list_), prev_(iter_) {}
|
|
|
|
|
|
|
|
virtual ~LookaheadIterator() override {}
|
|
|
|
|
|
|
|
virtual bool Valid() const override {
|
|
|
|
return iter_.Valid();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual const char *key() const override {
|
|
|
|
assert(Valid());
|
|
|
|
return iter_.key();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Next() override {
|
|
|
|
assert(Valid());
|
|
|
|
|
|
|
|
bool advance_prev = true;
|
|
|
|
if (prev_.Valid()) {
|
|
|
|
auto k1 = rep_.UserKey(prev_.key());
|
|
|
|
auto k2 = rep_.UserKey(iter_.key());
|
|
|
|
|
|
|
|
if (k1.compare(k2) == 0) {
|
|
|
|
// same user key, don't move prev_
|
|
|
|
advance_prev = false;
|
|
|
|
} else if (rep_.transform_) {
|
|
|
|
// only advance prev_ if it has the same prefix as iter_
|
|
|
|
auto t1 = rep_.transform_->Transform(k1);
|
|
|
|
auto t2 = rep_.transform_->Transform(k2);
|
|
|
|
advance_prev = t1.compare(t2) == 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (advance_prev) {
|
|
|
|
prev_ = iter_;
|
|
|
|
}
|
|
|
|
iter_.Next();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Prev() override {
|
|
|
|
assert(Valid());
|
|
|
|
iter_.Prev();
|
|
|
|
prev_ = iter_;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Seek(const Slice& internal_key, const char *memtable_key)
|
|
|
|
override {
|
|
|
|
const char *encoded_key =
|
|
|
|
(memtable_key != nullptr) ?
|
|
|
|
memtable_key : EncodeKey(&tmp_, internal_key);
|
|
|
|
|
|
|
|
if (prev_.Valid() && rep_.cmp_(encoded_key, prev_.key()) >= 0) {
|
|
|
|
// prev_.key() is smaller or equal to our target key; do a quick
|
|
|
|
// linear search (at most lookahead_ steps) starting from prev_
|
|
|
|
iter_ = prev_;
|
|
|
|
|
|
|
|
size_t cur = 0;
|
|
|
|
while (cur++ <= rep_.lookahead_ && iter_.Valid()) {
|
|
|
|
if (rep_.cmp_(encoded_key, iter_.key()) <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
iter_.Seek(encoded_key);
|
|
|
|
prev_ = iter_;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SeekToFirst() override {
|
|
|
|
iter_.SeekToFirst();
|
|
|
|
prev_ = iter_;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SeekToLast() override {
|
|
|
|
iter_.SeekToLast();
|
|
|
|
prev_ = iter_;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
std::string tmp_; // For passing to EncodeKey
|
|
|
|
|
|
|
|
private:
|
|
|
|
const SkipListRep& rep_;
|
|
|
|
SkipList<const char*, const MemTableRep::KeyComparator&>::Iterator iter_;
|
|
|
|
SkipList<const char*, const MemTableRep::KeyComparator&>::Iterator prev_;
|
|
|
|
};
|
|
|
|
|
In DB::NewIterator(), try to allocate the whole iterator tree in an arena
Summary:
In this patch, try to allocate the whole iterator tree starting from DBIter from an arena
1. ArenaWrappedDBIter is created when serves as the entry point of an iterator tree, with an arena in it.
2. Add an option to create iterator from arena for following iterators: DBIter, MergingIterator, MemtableIterator, all mem table's iterators, all table reader's iterators and two level iterator.
3. MergeIteratorBuilder is created to incrementally build the tree of internal iterators. It is passed to mem table list and version set and add iterators to it.
Limitations:
(1) Only DB::NewIterator() without tailing uses the arena. Other cases, including readonly DB and compactions are still from malloc
(2) Two level iterator itself is allocated in arena, but not iterators inside it.
Test Plan: make all check
Reviewers: ljin, haobo
Reviewed By: haobo
Subscribers: leveldb, dhruba, yhchiang, igor
Differential Revision: https://reviews.facebook.net/D18513
11 years ago
|
|
|
virtual MemTableRep::Iterator* GetIterator(Arena* arena = nullptr) override {
|
SkipListRep::LookaheadIterator
Summary:
This diff introduces the `lookahead` argument to `SkipListFactory()`. This is an
optimization for the tailing use case which includes many seeks. E.g. consider
the following operations on a skip list iterator:
Seek(x), Next(), Next(), Seek(x+2), Next(), Seek(x+3), Next(), Next(), ...
If `lookahead` is positive, `SkipListRep` will return an iterator which also
keeps track of the previously visited node. Seek() then first does a linear
search starting from that node (up to `lookahead` steps). As in the tailing
example above, this may require fewer than ~log(n) comparisons as with regular
skip list search.
Test Plan:
Added a new benchmark (`fillseekseq`) which simulates the usage pattern. It
first writes N records (with consecutive keys), then measures how much time it
takes to read them by calling `Seek()` and `Next()`.
$ time ./db_bench -num 10000000 -benchmarks fillseekseq -prefix_size 1 \
-key_size 8 -write_buffer_size $[1024*1024*1024] -value_size 50 \
-seekseq_next 2 -skip_list_lookahead=0
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.389 micros/op 2569047 ops/sec;
real 0m21.806s
user 0m12.106s
sys 0m9.672s
$ time ./db_bench [...] -skip_list_lookahead=2
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.153 micros/op 6540684 ops/sec;
real 0m19.469s
user 0m10.192s
sys 0m9.252s
Reviewers: ljin, sdong, igor
Reviewed By: igor
Subscribers: dhruba, leveldb, march, lovro
Differential Revision: https://reviews.facebook.net/D23997
10 years ago
|
|
|
if (lookahead_ > 0) {
|
|
|
|
void *mem =
|
|
|
|
arena ? arena->AllocateAligned(sizeof(SkipListRep::LookaheadIterator))
|
|
|
|
: operator new(sizeof(SkipListRep::LookaheadIterator));
|
|
|
|
return new (mem) SkipListRep::LookaheadIterator(*this);
|
In DB::NewIterator(), try to allocate the whole iterator tree in an arena
Summary:
In this patch, try to allocate the whole iterator tree starting from DBIter from an arena
1. ArenaWrappedDBIter is created when serves as the entry point of an iterator tree, with an arena in it.
2. Add an option to create iterator from arena for following iterators: DBIter, MergingIterator, MemtableIterator, all mem table's iterators, all table reader's iterators and two level iterator.
3. MergeIteratorBuilder is created to incrementally build the tree of internal iterators. It is passed to mem table list and version set and add iterators to it.
Limitations:
(1) Only DB::NewIterator() without tailing uses the arena. Other cases, including readonly DB and compactions are still from malloc
(2) Two level iterator itself is allocated in arena, but not iterators inside it.
Test Plan: make all check
Reviewers: ljin, haobo
Reviewed By: haobo
Subscribers: leveldb, dhruba, yhchiang, igor
Differential Revision: https://reviews.facebook.net/D18513
11 years ago
|
|
|
} else {
|
SkipListRep::LookaheadIterator
Summary:
This diff introduces the `lookahead` argument to `SkipListFactory()`. This is an
optimization for the tailing use case which includes many seeks. E.g. consider
the following operations on a skip list iterator:
Seek(x), Next(), Next(), Seek(x+2), Next(), Seek(x+3), Next(), Next(), ...
If `lookahead` is positive, `SkipListRep` will return an iterator which also
keeps track of the previously visited node. Seek() then first does a linear
search starting from that node (up to `lookahead` steps). As in the tailing
example above, this may require fewer than ~log(n) comparisons as with regular
skip list search.
Test Plan:
Added a new benchmark (`fillseekseq`) which simulates the usage pattern. It
first writes N records (with consecutive keys), then measures how much time it
takes to read them by calling `Seek()` and `Next()`.
$ time ./db_bench -num 10000000 -benchmarks fillseekseq -prefix_size 1 \
-key_size 8 -write_buffer_size $[1024*1024*1024] -value_size 50 \
-seekseq_next 2 -skip_list_lookahead=0
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.389 micros/op 2569047 ops/sec;
real 0m21.806s
user 0m12.106s
sys 0m9.672s
$ time ./db_bench [...] -skip_list_lookahead=2
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.153 micros/op 6540684 ops/sec;
real 0m19.469s
user 0m10.192s
sys 0m9.252s
Reviewers: ljin, sdong, igor
Reviewed By: igor
Subscribers: dhruba, leveldb, march, lovro
Differential Revision: https://reviews.facebook.net/D23997
10 years ago
|
|
|
void *mem =
|
|
|
|
arena ? arena->AllocateAligned(sizeof(SkipListRep::Iterator))
|
|
|
|
: operator new(sizeof(SkipListRep::Iterator));
|
In DB::NewIterator(), try to allocate the whole iterator tree in an arena
Summary:
In this patch, try to allocate the whole iterator tree starting from DBIter from an arena
1. ArenaWrappedDBIter is created when serves as the entry point of an iterator tree, with an arena in it.
2. Add an option to create iterator from arena for following iterators: DBIter, MergingIterator, MemtableIterator, all mem table's iterators, all table reader's iterators and two level iterator.
3. MergeIteratorBuilder is created to incrementally build the tree of internal iterators. It is passed to mem table list and version set and add iterators to it.
Limitations:
(1) Only DB::NewIterator() without tailing uses the arena. Other cases, including readonly DB and compactions are still from malloc
(2) Two level iterator itself is allocated in arena, but not iterators inside it.
Test Plan: make all check
Reviewers: ljin, haobo
Reviewed By: haobo
Subscribers: leveldb, dhruba, yhchiang, igor
Differential Revision: https://reviews.facebook.net/D18513
11 years ago
|
|
|
return new (mem) SkipListRep::Iterator(&skip_list_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
MemTableRep* SkipListFactory::CreateMemTableRep(
|
|
|
|
const MemTableRep::KeyComparator& compare, Arena* arena,
|
SkipListRep::LookaheadIterator
Summary:
This diff introduces the `lookahead` argument to `SkipListFactory()`. This is an
optimization for the tailing use case which includes many seeks. E.g. consider
the following operations on a skip list iterator:
Seek(x), Next(), Next(), Seek(x+2), Next(), Seek(x+3), Next(), Next(), ...
If `lookahead` is positive, `SkipListRep` will return an iterator which also
keeps track of the previously visited node. Seek() then first does a linear
search starting from that node (up to `lookahead` steps). As in the tailing
example above, this may require fewer than ~log(n) comparisons as with regular
skip list search.
Test Plan:
Added a new benchmark (`fillseekseq`) which simulates the usage pattern. It
first writes N records (with consecutive keys), then measures how much time it
takes to read them by calling `Seek()` and `Next()`.
$ time ./db_bench -num 10000000 -benchmarks fillseekseq -prefix_size 1 \
-key_size 8 -write_buffer_size $[1024*1024*1024] -value_size 50 \
-seekseq_next 2 -skip_list_lookahead=0
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.389 micros/op 2569047 ops/sec;
real 0m21.806s
user 0m12.106s
sys 0m9.672s
$ time ./db_bench [...] -skip_list_lookahead=2
[...]
DB path: [/dev/shm/rocksdbtest/dbbench]
fillseekseq : 0.153 micros/op 6540684 ops/sec;
real 0m19.469s
user 0m10.192s
sys 0m9.252s
Reviewers: ljin, sdong, igor
Reviewed By: igor
Subscribers: dhruba, leveldb, march, lovro
Differential Revision: https://reviews.facebook.net/D23997
10 years ago
|
|
|
const SliceTransform* transform, Logger* logger) {
|
|
|
|
return new SkipListRep(compare, arena, transform, lookahead_);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace rocksdb
|