Skip to content

Commit fa430bf

Browse files
committed
Minimize accessing multiple objects in Version::Get()
Summary: One of our profilings shows that Version::Get() sometimes is slow when getting pointer of user comparators or other global objects. In this patch: (1) we keep pointers of immutable objects in Version to avoid accesses them though option objects or cfd objects (2) table_reader is directly cached in FileMetaData so that table cache don't have to go through handle first to fetch it (3) If level 0 has less than 3 files, skip the filtering logic based on SST tables' key range. Smallest and largest key are stored in separated memory locations, which has potential cache misses Test Plan: make all check Reviewers: haobo, ljin Reviewed By: haobo CC: igor, yhchiang, nkg-, leveldb Differential Revision: https://reviews.facebook.net/D17739
1 parent e37dd21 commit fa430bf

6 files changed

+69
-44
lines changed

db/db_impl.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -3219,7 +3219,7 @@ Status DBImpl::GetImpl(const ReadOptions& options,
32193219
PERF_TIMER_START(get_from_output_files_time);
32203220

32213221
sv->current->Get(options, lkey, value, &s, &merge_context, &stats,
3222-
*cfd->options(), value_found);
3222+
value_found);
32233223
have_stat_update = true;
32243224
PERF_TIMER_STOP(get_from_output_files_time);
32253225
RecordTick(options_.statistics.get(), MEMTABLE_MISS);
@@ -3334,7 +3334,7 @@ std::vector<Status> DBImpl::MultiGet(
33343334
// Done
33353335
} else {
33363336
super_version->current->Get(options, lkey, value, &s, &merge_context,
3337-
&mgd->stats, *cfd->options());
3337+
&mgd->stats);
33383338
mgd->have_stat_update = true;
33393339
}
33403340

db/db_impl_readonly.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Status DBImplReadOnly::Get(const ReadOptions& options,
6767
} else {
6868
Version::GetStats stats;
6969
super_version->current->Get(options, lkey, value, &s, &merge_context,
70-
&stats, *cfd->options());
70+
&stats);
7171
}
7272
return s;
7373
}

db/table_cache.cc

+21-17
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,20 @@ Iterator* TableCache::NewIterator(const ReadOptions& options,
106106
if (table_reader_ptr != nullptr) {
107107
*table_reader_ptr = nullptr;
108108
}
109-
Cache::Handle* handle = file_meta.table_reader_handle;
109+
TableReader* table_reader = file_meta.table_reader;
110+
Cache::Handle* handle = nullptr;
110111
Status s;
111-
if (!handle) {
112+
if (table_reader == nullptr) {
112113
s = FindTable(toptions, icomparator, file_meta.number, file_meta.file_size,
113114
&handle, nullptr, options.read_tier == kBlockCacheTier);
115+
table_reader = GetTableReaderFromHandle(handle);
114116
}
115117
if (!s.ok()) {
116118
return NewErrorIterator(s);
117119
}
118120

119-
TableReader* table_reader = GetTableReaderFromHandle(handle);
120121
Iterator* result = table_reader->NewIterator(options);
121-
if (!file_meta.table_reader_handle) {
122+
if (handle != nullptr) {
122123
result->RegisterCleanup(&UnrefEntry, cache_, handle);
123124
}
124125
if (table_reader_ptr != nullptr) {
@@ -138,17 +139,18 @@ Status TableCache::Get(const ReadOptions& options,
138139
bool (*saver)(void*, const ParsedInternalKey&,
139140
const Slice&, bool),
140141
bool* table_io, void (*mark_key_may_exist)(void*)) {
141-
Cache::Handle* handle = file_meta.table_reader_handle;
142+
TableReader* t = file_meta.table_reader;
142143
Status s;
143-
if (!handle) {
144+
Cache::Handle* handle = nullptr;
145+
if (!t) {
144146
s = FindTable(storage_options_, internal_comparator, file_meta.number,
145147
file_meta.file_size, &handle, table_io,
146148
options.read_tier == kBlockCacheTier);
149+
t = GetTableReaderFromHandle(handle);
147150
}
148151
if (s.ok()) {
149-
TableReader* t = GetTableReaderFromHandle(handle);
150152
s = t->Get(options, k, arg, saver, mark_key_may_exist);
151-
if (!file_meta.table_reader_handle) {
153+
if (handle != nullptr) {
152154
ReleaseHandle(handle);
153155
}
154156
} else if (options.read_tier && s.IsIncomplete()) {
@@ -164,15 +166,16 @@ Status TableCache::GetTableProperties(
164166
const FileMetaData& file_meta,
165167
std::shared_ptr<const TableProperties>* properties, bool no_io) {
166168
Status s;
167-
auto table_handle = file_meta.table_reader_handle;
169+
auto table_reader = file_meta.table_reader;
168170
// table already been pre-loaded?
169-
if (table_handle) {
170-
auto table = GetTableReaderFromHandle(table_handle);
171-
*properties = table->GetTableProperties();
171+
if (table_reader) {
172+
*properties = table_reader->GetTableProperties();
173+
172174
return s;
173175
}
174176

175177
bool table_io;
178+
Cache::Handle* table_handle = nullptr;
176179
s = FindTable(toptions, internal_comparator, file_meta.number,
177180
file_meta.file_size, &table_handle, &table_io, no_io);
178181
if (!s.ok()) {
@@ -190,20 +193,21 @@ bool TableCache::PrefixMayMatch(const ReadOptions& options,
190193
const FileMetaData& file_meta,
191194
const Slice& internal_prefix, bool* table_io) {
192195
bool may_match = true;
193-
auto table_handle = file_meta.table_reader_handle;
194-
if (table_handle == nullptr) {
196+
auto table_reader = file_meta.table_reader;
197+
Cache::Handle* table_handle = nullptr;
198+
if (table_reader == nullptr) {
195199
// Need to get table handle from file number
196200
Status s = FindTable(storage_options_, icomparator, file_meta.number,
197201
file_meta.file_size, &table_handle, table_io);
198202
if (!s.ok()) {
199203
return may_match;
200204
}
205+
table_reader = GetTableReaderFromHandle(table_handle);
201206
}
202207

203-
auto table = GetTableReaderFromHandle(table_handle);
204-
may_match = table->PrefixMayMatch(internal_prefix);
208+
may_match = table_reader->PrefixMayMatch(internal_prefix);
205209

206-
if (file_meta.table_reader_handle == nullptr) {
210+
if (table_handle != nullptr) {
207211
// Need to release handle if it is generated from here.
208212
ReleaseHandle(table_handle);
209213
}

db/version_edit.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,17 @@ struct FileMetaData {
3232

3333
// Needs to be disposed when refs becomes 0.
3434
Cache::Handle* table_reader_handle;
35+
// Table reader in table_reader_handle
36+
TableReader* table_reader;
3537

3638
FileMetaData(uint64_t number, uint64_t file_size)
3739
: refs(0),
3840
allowed_seeks(1 << 30),
3941
number(number),
4042
file_size(file_size),
4143
being_compacted(false),
42-
table_reader_handle(nullptr) {}
44+
table_reader_handle(nullptr),
45+
table_reader(nullptr) {}
4346
FileMetaData() : FileMetaData(0, 0) {}
4447
};
4548

db/version_set.cc

+34-21
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,17 @@ bool BySmallestKey(FileMetaData* a, FileMetaData* b,
483483
Version::Version(ColumnFamilyData* cfd, VersionSet* vset,
484484
uint64_t version_number)
485485
: cfd_(cfd),
486+
internal_comparator_((cfd == nullptr) ? nullptr
487+
: &cfd->internal_comparator()),
488+
user_comparator_((cfd == nullptr)
489+
? nullptr
490+
: internal_comparator_->user_comparator()),
491+
table_cache_((cfd == nullptr) ? nullptr : cfd->table_cache()),
492+
merge_operator_((cfd == nullptr) ? nullptr
493+
: cfd->options()->merge_operator.get()),
494+
info_log_((cfd == nullptr) ? nullptr : cfd->options()->info_log.get()),
495+
db_statistics_((cfd == nullptr) ? nullptr
496+
: cfd->options()->statistics.get()),
486497
vset_(vset),
487498
next_(this),
488499
prev_(this),
@@ -504,27 +515,22 @@ void Version::Get(const ReadOptions& options,
504515
Status* status,
505516
MergeContext* merge_context,
506517
GetStats* stats,
507-
const Options& db_options,
508518
bool* value_found) {
509519
Slice ikey = k.internal_key();
510520
Slice user_key = k.user_key();
511-
const Comparator* ucmp = cfd_->internal_comparator().user_comparator();
512-
513-
auto merge_operator = db_options.merge_operator.get();
514-
auto logger = db_options.info_log.get();
515521

516522
assert(status->ok() || status->IsMergeInProgress());
517523
Saver saver;
518524
saver.state = status->ok()? kNotFound : kMerge;
519-
saver.ucmp = ucmp;
525+
saver.ucmp = user_comparator_;
520526
saver.user_key = user_key;
521527
saver.value_found = value_found;
522528
saver.value = value;
523-
saver.merge_operator = merge_operator;
529+
saver.merge_operator = merge_operator_;
524530
saver.merge_context = merge_context;
525-
saver.logger = logger;
531+
saver.logger = info_log_;
526532
saver.didIO = false;
527-
saver.statistics = db_options.statistics.get();
533+
saver.statistics = db_statistics_;
528534

529535
stats->seek_file = nullptr;
530536
stats->seek_file_level = -1;
@@ -555,7 +561,7 @@ void Version::Get(const ReadOptions& options,
555561
// On Level-n (n>=1), files are sorted.
556562
// Binary search to find earliest index whose largest key >= ikey.
557563
// We will also stop when the file no longer overlaps ikey
558-
start_index = FindFile(cfd_->internal_comparator(), files_[level], ikey);
564+
start_index = FindFile(*internal_comparator_, files_[level], ikey);
559565
}
560566

561567
// Traverse each relevant file to find the desired key
@@ -564,8 +570,10 @@ void Version::Get(const ReadOptions& options,
564570
#endif
565571
for (uint32_t i = start_index; i < num_files; ++i) {
566572
FileMetaData* f = files[i];
567-
if (ucmp->Compare(user_key, f->smallest.user_key()) < 0 ||
568-
ucmp->Compare(user_key, f->largest.user_key()) > 0) {
573+
// Skip key range filtering for levle 0 if there are few level 0 files.
574+
if ((level > 0 || num_files > 2) &&
575+
(user_comparator_->Compare(user_key, f->smallest.user_key()) < 0 ||
576+
user_comparator_->Compare(user_key, f->largest.user_key()) > 0)) {
569577
// Only process overlapping files.
570578
if (level > 0) {
571579
// If on Level-n (n>=1) then the files are sorted.
@@ -581,8 +589,8 @@ void Version::Get(const ReadOptions& options,
581589
// Sanity check to make sure that the files are correctly sorted
582590
if (prev_file) {
583591
if (level != 0) {
584-
int comp_sign = cfd_->internal_comparator().Compare(
585-
prev_file->largest, f->smallest);
592+
int comp_sign =
593+
internal_comparator_->Compare(prev_file->largest, f->smallest);
586594
assert(comp_sign < 0);
587595
} else {
588596
// level == 0, the current file cannot be newer than the previous one.
@@ -596,9 +604,8 @@ void Version::Get(const ReadOptions& options,
596604
prev_file = f;
597605
#endif
598606
bool tableIO = false;
599-
*status = cfd_->table_cache()->Get(options, cfd_->internal_comparator(),
600-
*f, ikey, &saver, SaveValue, &tableIO,
601-
MarkKeyMayExist);
607+
*status = table_cache_->Get(options, *internal_comparator_, *f, ikey,
608+
&saver, SaveValue, &tableIO, MarkKeyMayExist);
602609
// TODO: examine the behavior for corrupted key
603610
if (!status->ok()) {
604611
return;
@@ -643,12 +650,12 @@ void Version::Get(const ReadOptions& options,
643650
if (kMerge == saver.state) {
644651
// merge_operands are in saver and we hit the beginning of the key history
645652
// do a final merge of nullptr and operands;
646-
if (merge_operator->FullMerge(user_key, nullptr,
647-
saver.merge_context->GetOperands(),
648-
value, logger)) {
653+
if (merge_operator_->FullMerge(user_key, nullptr,
654+
saver.merge_context->GetOperands(), value,
655+
info_log_)) {
649656
*status = Status::OK();
650657
} else {
651-
RecordTick(db_options.statistics.get(), NUMBER_MERGE_FAILURES);
658+
RecordTick(db_statistics_, NUMBER_MERGE_FAILURES);
652659
*status = Status::Corruption("could not perform end-of-key merge for ",
653660
user_key);
654661
}
@@ -1458,6 +1465,12 @@ class VersionSet::Builder {
14581465
base_->vset_->storage_options_, cfd_->internal_comparator(),
14591466
file_meta->number, file_meta->file_size,
14601467
&file_meta->table_reader_handle, &table_io, false);
1468+
if (file_meta->table_reader_handle != nullptr) {
1469+
// Load table_reader
1470+
file_meta->table_reader =
1471+
cfd_->table_cache()->GetTableReaderFromHandle(
1472+
file_meta->table_reader_handle);
1473+
}
14611474
}
14621475
}
14631476
}

db/version_set.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ class Version {
8888
int seek_file_level;
8989
};
9090
void Get(const ReadOptions&, const LookupKey& key, std::string* val,
91-
Status* status, MergeContext* merge_context,
92-
GetStats* stats, const Options& db_option,
91+
Status* status, MergeContext* merge_context, GetStats* stats,
9392
bool* value_found = nullptr);
9493

9594
// Adds "stats" into the current state. Returns true if a new
@@ -230,6 +229,12 @@ class Version {
230229
void UpdateFilesBySize();
231230

232231
ColumnFamilyData* cfd_; // ColumnFamilyData to which this Version belongs
232+
const InternalKeyComparator* internal_comparator_;
233+
const Comparator* user_comparator_;
234+
TableCache* table_cache_;
235+
const MergeOperator* merge_operator_;
236+
Logger* info_log_;
237+
Statistics* db_statistics_;
233238
VersionSet* vset_; // VersionSet to which this Version belongs
234239
Version* next_; // Next version in linked list
235240
Version* prev_; // Previous version in linked list

0 commit comments

Comments
 (0)