Upstream changes

git-svn-id: https://leveldb.googlecode.com/svn/trunk@15 62dab493-f737-651d-591e-8d6aee1b9529
This commit is contained in:
jorlow@chromium.org 2011-03-25 20:27:43 +00:00
parent 8303bb1b33
commit e11bdf1935
7 changed files with 447 additions and 266 deletions

1
TODO
View File

@ -8,7 +8,6 @@ Maybe afterwards
ss ss
- Stats - Stats
- Speed up backwards scan (avoid three passes over data)
db db
- Maybe implement DB::BulkDeleteForRange(start_key, end_key) - Maybe implement DB::BulkDeleteForRange(start_key, end_key)

View File

@ -11,6 +11,8 @@
#include "include/db.h" #include "include/db.h"
#include "include/env.h" #include "include/env.h"
#include "include/write_batch.h" #include "include/write_batch.h"
#include "port/port.h"
#include "util/crc32c.h"
#include "util/histogram.h" #include "util/histogram.h"
#include "util/random.h" #include "util/random.h"
#include "util/testutil.h" #include "util/testutil.h"
@ -25,6 +27,8 @@
// readseq -- read N values sequentially // readseq -- read N values sequentially
// readreverse -- read N values in reverse order // readreverse -- read N values in reverse order
// readrandom -- read N values in random order // readrandom -- read N values in random order
// crc32c -- repeated crc32c of 4K of data
// sha1 -- repeated SHA1 computation over 4K of data
// Meta operations: // Meta operations:
// compact -- Compact the entire DB // compact -- Compact the entire DB
// heapprofile -- Dump a heap profile (if supported by this port) // heapprofile -- Dump a heap profile (if supported by this port)
@ -34,17 +38,21 @@
// normal -- reset N back to its normal value (1000000) // normal -- reset N back to its normal value (1000000)
static const char* FLAGS_benchmarks = static const char* FLAGS_benchmarks =
"fillseq," "fillseq,"
"fillsync,"
"fillrandom," "fillrandom,"
"overwrite," "overwrite,"
"fillsync," "readrandom,"
"readrandom," // Extra run to allow previous compactions to quiesce
"readseq," "readseq,"
"readreverse," "readreverse,"
"readrandom,"
"compact," "compact,"
"readrandom,"
"readseq," "readseq,"
"readreverse," "readreverse,"
"readrandom," "fill100K,"
"fill100K"; "crc32c,"
"sha1"
;
// Number of key/values to place in database // Number of key/values to place in database
static int FLAGS_num = 1000000; static int FLAGS_num = 1000000;
@ -330,6 +338,10 @@ class Benchmark {
ReadRandom(); ReadRandom();
} else if (name == Slice("compact")) { } else if (name == Slice("compact")) {
Compact(); Compact();
} else if (name == Slice("crc32c")) {
Crc32c(4096, "(4K per op)");
} else if (name == Slice("sha1")) {
SHA1(4096, "(4K per op)");
} else if (name == Slice("heapprofile")) { } else if (name == Slice("heapprofile")) {
HeapProfile(); HeapProfile();
} else { } else {
@ -340,6 +352,41 @@ class Benchmark {
} }
private: private:
void Crc32c(int size, const char* label) {
// Checksum about 500MB of data total
string data(size, 'x');
int64_t bytes = 0;
uint32_t crc = 0;
while (bytes < 500 * 1048576) {
crc = crc32c::Value(data.data(), size);
FinishedSingleOp();
bytes += size;
}
// Print so result is not dead
fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
bytes_ = bytes;
message_ = label;
}
void SHA1(int size, const char* label) {
// SHA1 about 100MB of data total
string data(size, 'x');
int64_t bytes = 0;
char sha1[20];
while (bytes < 100 * 1048576) {
port::SHA1_Hash(data.data(), size, sha1);
FinishedSingleOp();
bytes += size;
}
// Print so result is not dead
fprintf(stderr, "... sha1=%02x...\r", static_cast<unsigned int>(sha1[0]));
bytes_ = bytes;
message_ = label;
}
void Open() { void Open() {
assert(db_ == NULL); assert(db_ == NULL);
Options options; Options options;

View File

@ -36,6 +36,16 @@ namespace {
// numbers, deletion markers, overwrites, etc. // numbers, deletion markers, overwrites, etc.
class DBIter: public Iterator { class DBIter: public Iterator {
public: public:
// Which direction is the iterator currently moving?
// (1) When moving forward, the internal iterator is positioned at
// the exact entry that yields this->key(), this->value()
// (2) When moving backwards, the internal iterator is positioned
// just before all entries whose user key == this->key().
enum Direction {
kForward,
kReverse
};
DBIter(const std::string* dbname, Env* env, DBIter(const std::string* dbname, Env* env,
const Comparator* cmp, Iterator* iter, SequenceNumber s) const Comparator* cmp, Iterator* iter, SequenceNumber s)
: dbname_(dbname), : dbname_(dbname),
@ -44,6 +54,7 @@ class DBIter: public Iterator {
iter_(iter), iter_(iter),
sequence_(s), sequence_(s),
large_(NULL), large_(NULL),
direction_(kForward),
valid_(false) { valid_(false) {
} }
virtual ~DBIter() { virtual ~DBIter() {
@ -53,48 +64,21 @@ class DBIter: public Iterator {
virtual bool Valid() const { return valid_; } virtual bool Valid() const { return valid_; }
virtual Slice key() const { virtual Slice key() const {
assert(valid_); assert(valid_);
return key_; return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_;
} }
virtual Slice value() const { virtual Slice value() const {
assert(valid_); assert(valid_);
Slice raw_value = (direction_ == kForward) ? iter_->value() : saved_value_;
if (large_ == NULL) { if (large_ == NULL) {
return value_; return raw_value;
} else { } else {
MutexLock l(&large_->mutex); MutexLock l(&large_->mutex);
if (!large_->produced) { if (!large_->produced) {
ReadIndirectValue(); ReadIndirectValue(raw_value);
} }
return large_->value; return large_->value;
} }
} }
virtual void Next() {
assert(valid_);
// iter_ is already positioned past DBIter::key()
FindNextUserEntry();
}
virtual void Prev() {
assert(valid_);
bool ignored;
ScanUntilBeforeCurrentKey(&ignored);
FindPrevUserEntry();
}
virtual void Seek(const Slice& target) {
ParsedInternalKey ikey(target, sequence_, kValueTypeForSeek);
std::string tmp;
AppendInternalKey(&tmp, ikey);
iter_->Seek(tmp);
FindNextUserEntry();
}
virtual void SeekToFirst() {
iter_->SeekToFirst();
FindNextUserEntry();
}
virtual void SeekToLast();
virtual Status status() const { virtual Status status() const {
if (status_.ok()) { if (status_.ok()) {
if (large_ != NULL && !large_->status.ok()) return large_->status; if (large_ != NULL && !large_->status.ok()) return large_->status;
@ -104,23 +88,13 @@ class DBIter: public Iterator {
} }
} }
virtual void Next();
virtual void Prev();
virtual void Seek(const Slice& target);
virtual void SeekToFirst();
virtual void SeekToLast();
private: private:
void FindNextUserEntry();
void FindPrevUserEntry();
void SaveKey(const Slice& k) { key_.assign(k.data(), k.size()); }
void SaveValue(const Slice& v) {
if (value_.capacity() > v.size() + 1048576) {
std::string empty;
swap(empty, value_);
}
value_.assign(v.data(), v.size());
}
bool ParseKey(ParsedInternalKey* key);
void SkipPast(const Slice& k);
void ScanUntilBeforeCurrentKey(bool* found_live);
void ReadIndirectValue() const;
struct Large { struct Large {
port::Mutex mutex; port::Mutex mutex;
std::string value; std::string value;
@ -128,19 +102,42 @@ class DBIter: public Iterator {
Status status; Status status;
}; };
void FindNextUserEntry(bool skipping, std::string* skip);
void FindPrevUserEntry();
bool ParseKey(ParsedInternalKey* key);
void ReadIndirectValue(Slice ref) const;
inline void SaveKey(const Slice& k, std::string* dst) {
dst->assign(k.data(), k.size());
}
inline void ForgetLargeValue() {
if (large_ != NULL) {
delete large_;
large_ = NULL;
}
}
inline void ClearSavedValue() {
if (saved_value_.capacity() > 1048576) {
std::string empty;
swap(empty, saved_value_);
} else {
saved_value_.clear();
}
}
const std::string* const dbname_; const std::string* const dbname_;
Env* const env_; Env* const env_;
const Comparator* const user_comparator_; const Comparator* const user_comparator_;
// iter_ is positioned just past current entry for DBIter if valid_
Iterator* const iter_; Iterator* const iter_;
SequenceNumber const sequence_; SequenceNumber const sequence_;
Status status_; Status status_;
std::string key_; // Always a user key std::string saved_key_; // == current key when direction_==kReverse
std::string value_; std::string saved_value_; // == current raw value when direction_==kReverse
Large* large_; // Non-NULL if value is an indirect reference Large* large_; // Non-NULL if value is an indirect reference
Direction direction_;
bool valid_; bool valid_;
// No copying allowed // No copying allowed
@ -157,204 +154,189 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
} }
} }
void DBIter::FindNextUserEntry() { void DBIter::Next() {
if (large_ != NULL) { assert(valid_);
if (status_.ok() && !large_->status.ok()) { ForgetLargeValue();
status_ = large_->status;
}
delete large_;
large_ = NULL;
}
while (iter_->Valid()) {
ParsedInternalKey ikey;
if (!ParseKey(&ikey)) {
// Skip past corrupted entry
iter_->Next();
continue;
}
if (ikey.sequence > sequence_) {
// Ignore entries newer than the snapshot
iter_->Next();
continue;
}
switch (ikey.type) { if (direction_ == kReverse) { // Switch directions?
case kTypeDeletion: direction_ = kForward;
SaveKey(ikey.user_key); // Make local copy for use by SkipPast() // iter_ is pointing just before the entries for this->key(),
iter_->Next(); // so advance into the range of entries for this->key() and then
SkipPast(key_); // use the normal skipping code below.
// Do not return deleted entries. Instead keep looping.
break;
case kTypeValue:
SaveKey(ikey.user_key);
SaveValue(iter_->value());
iter_->Next();
SkipPast(key_);
// Yield the value we just found.
valid_ = true;
return;
case kTypeLargeValueRef:
SaveKey(ikey.user_key);
// Save the large value ref as value_, and read it lazily on a call
// to value()
SaveValue(iter_->value());
large_ = new Large;
large_->produced = false;
iter_->Next();
SkipPast(key_);
// Yield the value we just found.
valid_ = true;
return;
}
}
valid_ = false;
key_.clear();
value_.clear();
assert(large_ == NULL);
}
void DBIter::SkipPast(const Slice& k) {
while (iter_->Valid()) {
ParsedInternalKey ikey;
// Note that if we cannot parse an internal key, we keep looping
// so that if we have a run like the following:
// <x,100,v> => value100
// <corrupted entry for user key x>
// <x,50,v> => value50
// we will skip over the corrupted entry as well as value50.
if (ParseKey(&ikey) && user_comparator_->Compare(ikey.user_key, k) != 0) {
break;
}
iter_->Next();
}
}
void DBIter::SeekToLast() {
// Position iter_ at the last uncorrupted user key and then
// let FindPrevUserEntry() do the heavy lifting to find
// a user key that is live.
iter_->SeekToLast();
ParsedInternalKey current;
while (iter_->Valid() && !ParseKey(&current)) {
iter_->Prev();
}
if (iter_->Valid()) {
SaveKey(current.user_key);
}
FindPrevUserEntry();
}
// Let X be the user key at which iter_ is currently positioned.
// Adjust DBIter to point at the last entry with a key <= X that
// has a live value.
void DBIter::FindPrevUserEntry() {
// Consider the following example:
//
// A@540
// A@400
//
// B@300
// B@200
// B@100 <- iter_
//
// C@301
// C@201
//
// The comments marked "(first iteration)" below relate what happens
// for the preceding example in the first iteration of the while loop
// below. There may be more than one iteration either if there are
// no live values for B, or if there is a corruption.
while (iter_->Valid()) {
std::string saved = key_;
bool found_live;
ScanUntilBeforeCurrentKey(&found_live);
// (first iteration) iter_ at A@400
if (found_live) {
// Step forward into range of entries with user key >= saved
if (!iter_->Valid()) { if (!iter_->Valid()) {
iter_->SeekToFirst(); iter_->SeekToFirst();
} else { } else {
iter_->Next(); iter_->Next();
} }
// (first iteration) iter_ at B@300 if (!iter_->Valid()) {
valid_ = false;
FindNextUserEntry(); // Sets key_ to the key of the next value it found saved_key_.clear();
if (valid_ && user_comparator_->Compare(key_, saved) == 0) {
// (first iteration) iter_ at C@301
return; return;
} }
}
// FindNextUserEntry() could not find any entries under the // Temporarily use saved_key_ as storage for key to skip.
// user key "saved". This is probably a corruption since std::string* skip = &saved_key_;
// ScanUntilBefore(saved) found a live value. So we skip SaveKey(ExtractUserKey(iter_->key()), skip);
// backwards to an earlier key and ignore the corrupted FindNextUserEntry(true, skip);
// entries for "saved".
//
// (first iteration) iter_ at C@301 and saved == "B"
key_ = saved;
bool ignored;
ScanUntilBeforeCurrentKey(&ignored);
// (first iteration) iter_ at A@400
}
}
valid_ = false;
key_.clear();
value_.clear();
} }
void DBIter::ScanUntilBeforeCurrentKey(bool* found_live) { void DBIter::FindNextUserEntry(bool skipping, std::string* skip) {
*found_live = false; // Loop until we hit an acceptable entry to yield
if (!iter_->Valid()) { assert(iter_->Valid());
iter_->SeekToLast(); assert(direction_ == kForward);
} assert(large_ == NULL);
do {
while (iter_->Valid()) { ParsedInternalKey ikey;
ParsedInternalKey current; if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
if (!ParseKey(&current)) { switch (ikey.type) {
iter_->Prev();
continue;
}
if (current.sequence > sequence_) {
// Ignore entries that are serialized after this read
iter_->Prev();
continue;
}
const int cmp = user_comparator_->Compare(current.user_key, key_);
if (cmp < 0) {
SaveKey(current.user_key);
return;
} else if (cmp == 0) {
switch (current.type) {
case kTypeDeletion: case kTypeDeletion:
*found_live = false; // Arrange to skip all upcoming entries for this key since
// they are hidden by this deletion.
SaveKey(ikey.user_key, skip);
skipping = true;
break; break;
case kTypeValue: case kTypeValue:
case kTypeLargeValueRef: case kTypeLargeValueRef:
*found_live = true; if (skipping &&
user_comparator_->Compare(ikey.user_key, *skip) <= 0) {
// Entry hidden
} else {
valid_ = true;
saved_key_.clear();
if (ikey.type == kTypeLargeValueRef) {
large_ = new Large;
large_->produced = false;
}
return;
}
break; break;
} }
} else { // cmp > 0 }
*found_live = false; iter_->Next();
} while (iter_->Valid());
saved_key_.clear();
valid_ = false;
}
void DBIter::Prev() {
assert(valid_);
ForgetLargeValue();
if (direction_ == kForward) { // Switch directions?
// iter_ is pointing at the current entry. Scan backwards until
// the key changes so we can use the normal reverse scanning code.
assert(iter_->Valid()); // Otherwise valid_ would have been false
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
while (true) {
iter_->Prev();
if (!iter_->Valid()) {
valid_ = false;
saved_key_.clear();
ClearSavedValue();
return;
}
if (user_comparator_->Compare(ExtractUserKey(iter_->key()),
saved_key_) < 0) {
break;
}
}
direction_ = kReverse;
} }
FindPrevUserEntry();
}
void DBIter::FindPrevUserEntry() {
assert(direction_ == kReverse);
assert(large_ == NULL);
ValueType value_type = kTypeDeletion;
if (iter_->Valid()) {
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
do {
ParsedInternalKey ikey;
if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
if ((value_type != kTypeDeletion) &&
user_comparator_->Compare(ikey.user_key, saved_key_) < 0) {
// We encountered a non-deleted value in entries for previous keys,
break;
}
value_type = ikey.type;
if (value_type == kTypeDeletion) {
ClearSavedValue();
} else {
Slice raw_value = iter_->value();
if (saved_value_.capacity() > raw_value.size() + 1048576) {
std::string empty;
swap(empty, saved_value_);
}
saved_value_.assign(raw_value.data(), raw_value.size());
}
}
iter_->Prev(); iter_->Prev();
} while (iter_->Valid());
}
if (value_type == kTypeDeletion) {
// End
valid_ = false;
saved_key_.clear();
ClearSavedValue();
direction_ = kForward;
} else {
valid_ = true;
if (value_type == kTypeLargeValueRef) {
large_ = new Large;
large_->produced = false;
}
} }
} }
void DBIter::ReadIndirectValue() const { void DBIter::Seek(const Slice& target) {
direction_ = kForward;
ForgetLargeValue();
ClearSavedValue();
saved_key_.clear();
AppendInternalKey(
&saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek));
iter_->Seek(saved_key_);
if (iter_->Valid()) {
FindNextUserEntry(false, &saved_key_ /* temporary storage */);
} else {
valid_ = false;
}
}
void DBIter::SeekToFirst() {
direction_ = kForward;
ForgetLargeValue();
ClearSavedValue();
iter_->SeekToFirst();
if (iter_->Valid()) {
FindNextUserEntry(false, &saved_key_ /* temporary storage */);
} else {
valid_ = false;
}
}
void DBIter::SeekToLast() {
direction_ = kReverse;
ForgetLargeValue();
ClearSavedValue();
iter_->SeekToLast();
FindPrevUserEntry();
}
void DBIter::ReadIndirectValue(Slice ref) const {
assert(!large_->produced); assert(!large_->produced);
large_->produced = true; large_->produced = true;
LargeValueRef large_ref; LargeValueRef large_ref;
if (value_.size() != LargeValueRef::ByteSize()) { if (ref.size() != LargeValueRef::ByteSize()) {
large_->status = Status::Corruption("malformed large value reference"); large_->status = Status::Corruption("malformed large value reference");
return; return;
} }
memcpy(large_ref.data, value_.data(), LargeValueRef::ByteSize()); memcpy(large_ref.data, ref.data(), LargeValueRef::ByteSize());
std::string fname = LargeValueFileName(*dbname_, large_ref); std::string fname = LargeValueFileName(*dbname_, large_ref);
RandomAccessFile* file; RandomAccessFile* file;
Status s = env_->NewRandomAccessFile(fname, &file); Status s = env_->NewRandomAccessFile(fname, &file);

View File

@ -209,6 +209,16 @@ class DBTest {
} }
} }
} }
std::string IterStatus(Iterator* iter) {
std::string result;
if (iter->Valid()) {
result = iter->key().ToString() + "->" + iter->value().ToString();
} else {
result = "(invalid)";
}
return result;
}
}; };
TEST(DBTest, Empty) { TEST(DBTest, Empty) {
@ -234,6 +244,180 @@ TEST(DBTest, PutDeleteGet) {
ASSERT_EQ("NOT_FOUND", Get("foo")); ASSERT_EQ("NOT_FOUND", Get("foo"));
} }
TEST(DBTest, IterEmpty) {
Iterator* iter = db_->NewIterator(ReadOptions());
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->Seek("foo");
ASSERT_EQ(IterStatus(iter), "(invalid)");
delete iter;
}
TEST(DBTest, IterSingle) {
ASSERT_OK(Put("a", "va"));
Iterator* iter = db_->NewIterator(ReadOptions());
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->Seek("");
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->Seek("a");
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->Seek("b");
ASSERT_EQ(IterStatus(iter), "(invalid)");
delete iter;
}
TEST(DBTest, IterMulti) {
ASSERT_OK(Put("a", "va"));
ASSERT_OK(Put("b", "vb"));
ASSERT_OK(Put("c", "vc"));
Iterator* iter = db_->NewIterator(ReadOptions());
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "b->vb");
iter->Next();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "b->vb");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->Seek("");
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Seek("a");
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Seek("ax");
ASSERT_EQ(IterStatus(iter), "b->vb");
iter->Seek("b");
ASSERT_EQ(IterStatus(iter), "b->vb");
iter->Seek("z");
ASSERT_EQ(IterStatus(iter), "(invalid)");
// Switch from reverse to forward
iter->SeekToLast();
iter->Prev();
iter->Prev();
iter->Next();
ASSERT_EQ(IterStatus(iter), "b->vb");
// Switch from forward to reverse
iter->SeekToFirst();
iter->Next();
iter->Next();
iter->Prev();
ASSERT_EQ(IterStatus(iter), "b->vb");
// Make sure iter stays at snapshot
ASSERT_OK(Put("a", "va2"));
ASSERT_OK(Put("a2", "va3"));
ASSERT_OK(Put("b", "vb2"));
ASSERT_OK(Put("c", "vc2"));
ASSERT_OK(Delete("b"));
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "b->vb");
iter->Next();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "b->vb");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "(invalid)");
delete iter;
}
TEST(DBTest, IterSmallAndLargeMix) {
ASSERT_OK(Put("a", "va"));
ASSERT_OK(Put("b", std::string(100000, 'b')));
ASSERT_OK(Put("c", "vc"));
ASSERT_OK(Put("d", std::string(100000, 'd')));
ASSERT_OK(Put("e", std::string(100000, 'e')));
Iterator* iter = db_->NewIterator(ReadOptions());
iter->SeekToFirst();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Next();
ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));
iter->Next();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Next();
ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));
iter->Next();
ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));
iter->Next();
ASSERT_EQ(IterStatus(iter), "(invalid)");
iter->SeekToLast();
ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));
iter->Prev();
ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));
iter->Prev();
ASSERT_EQ(IterStatus(iter), "c->vc");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));
iter->Prev();
ASSERT_EQ(IterStatus(iter), "a->va");
iter->Prev();
ASSERT_EQ(IterStatus(iter), "(invalid)");
delete iter;
}
TEST(DBTest, Recover) { TEST(DBTest, Recover) {
ASSERT_OK(Put("foo", "v1")); ASSERT_OK(Put("foo", "v1"));
ASSERT_OK(Put("baz", "v5")); ASSERT_OK(Put("baz", "v5"));

View File

@ -132,8 +132,7 @@ range <code>[start,limit)</code>:
} }
</pre> </pre>
You can also process entries in reverse order. (Caveat: reverse You can also process entries in reverse order. (Caveat: reverse
iteration is currently a factor of two or three slower than forward iteration may be somewhat slower than forward iteration.)
iteration.)
<p> <p>
<pre> <pre>
for (it-&gt;SeekToLast(); it-&gt;Valid(); it-&gt;Prev()) { for (it-&gt;SeekToLast(); it-&gt;Valid(); it-&gt;Prev()) {

View File

@ -31,22 +31,6 @@ TEST(SHA1, Simple) {
TestSHA1(x.data(), x.size())); TestSHA1(x.data(), x.size()));
} }
TEST(SHA1, Benchmark) {
std::string data(1048576 * 100, 'x');
double start = Env::Default()->NowMicros() * 1e-6;
static const int kIters = 10;
uint32_t sha1 = 0;
for (int i = 0; i < kIters; i++) {
char hash_val[20];
SHA1_Hash(data.data(), data.size(), hash_val);
sha1 |= hash_val[0];
}
double finish = Env::Default()->NowMicros() * 1e-6;
double mb = (static_cast<long long int>(data.size()) * kIters) / 1048576.0;
fprintf(stderr, "SHA1 %0.0f MB: %.3f secs; %.1f MB/s, dummy=0x%02x\n",
mb, (finish - start), mb / (finish - start), sha1);
}
} }
} }

View File

@ -64,20 +64,6 @@ TEST(CRC, Mask) {
ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc))))); ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc)))));
} }
TEST(CRC, Benchmark) {
std::string data(1048576 * 100, 'x');
double start = Env::Default()->NowMicros() * 1e-6;
static const int kIters = 10;
uint32_t crc = 0;
for (int i = 0; i < kIters; i++) {
crc |= Value(data.data(), data.size());
}
double finish = Env::Default()->NowMicros() * 1e-6;
double mb = (static_cast<long long int>(data.size()) * kIters) / 1048576.0;
fprintf(stderr, "CRC %0.0f MB: %.3f secs; %.1f MB/s, crc=0x%08x\n",
mb, (finish - start), mb / (finish - start), crc);
}
} }
} }