0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

SERVER-60245 Reduce RecordId copying in classic query engine collection scans

This also makes minor improvements in RecordId comparison and `isValid()` functions
This commit is contained in:
Louis Williams 2021-10-15 18:37:30 +00:00 committed by Evergreen Agent
parent 9b3bf32632
commit 2119cbfd59
4 changed files with 81 additions and 30 deletions

View File

@ -210,7 +210,7 @@ PlanStage::StageState CollectionScan::doWork(WorkingSetID* out) {
WorkingSetID id = _workingSet->allocate();
WorkingSetMember* member = _workingSet->get(id);
member->recordId = record->id;
member->recordId = std::move(record->id);
member->resetDocument(opCtx()->recoveryUnit()->getSnapshotId(), record->data.releaseToBson());
_workingSet->transitionToRecordIdAndObj(id);

View File

@ -119,14 +119,17 @@ public:
*/
template <typename OnNull, typename OnLong, typename OnStr>
auto withFormat(OnNull&& onNull, OnLong&& onLong, OnStr&& onStr) const {
switch (auto f = _format) {
switch (_format) {
case Format::kNull:
return onNull(Null());
case Format::kLong:
return onLong(getLong());
case Format::kSmallStr:
return onLong(_getLongNoCheck());
case Format::kSmallStr: {
auto str = _getSmallStrNoCheck();
return onStr(str.rawData(), str.size());
}
case Format::kBigStr: {
auto str = getStr();
auto str = _getBigStrNoCheck();
return onStr(str.rawData(), str.size());
}
default:
@ -155,9 +158,7 @@ public:
}
invariant(isLong(),
fmt::format("expected RecordID long format, got: {}", _formatToString(_format)));
int64_t val;
memcpy(&val, _buffer, sizeof(val));
return val;
return _getLongNoCheck();
}
/**
@ -169,17 +170,9 @@ public:
isStr(),
fmt::format("expected RecordID string format, got: {}", _formatToString(_format)));
if (_format == Format::kSmallStr) {
char size = _buffer[0];
invariant(size > 0);
invariant(size <= kSmallStrMaxSize);
return StringData(_buffer + 1, size);
return _getSmallStrNoCheck();
} else if (_format == Format::kBigStr) {
// We use a ConstSharedBuffer that is only allocated once and assume the string size is
// just the originally allocated capacity.
size_t size = _sharedBuffer.capacity();
invariant(size > kSmallStrMaxSize);
invariant(size <= kBigStrMaxSize);
return StringData(_sharedBuffer.get(), size);
return _getBigStrNoCheck();
}
MONGO_UNREACHABLE;
}
@ -224,13 +217,19 @@ public:
if (rhs._format == Format::kNull) {
return 1;
}
return getLong() == rhs.getLong() ? 0 : (getLong() > rhs.getLong()) ? 1 : -1;
return _getLongNoCheck() == rhs.getLong()
? 0
: (_getLongNoCheck() > rhs.getLong()) ? 1 : -1;
case Format::kSmallStr:
if (rhs._format == Format::kNull) {
return 1;
}
return _getSmallStrNoCheck().compare(rhs.getStr());
case Format::kBigStr:
if (rhs._format == Format::kNull) {
return 1;
}
return getStr().compare(rhs.getStr());
return _getBigStrNoCheck().compare(rhs.getStr());
}
MONGO_UNREACHABLE;
}
@ -351,6 +350,28 @@ private:
MONGO_UNREACHABLE;
}
int64_t _getLongNoCheck() const {
int64_t val;
memcpy(&val, _buffer, sizeof(val));
return val;
}
StringData _getSmallStrNoCheck() const {
char size = _buffer[0];
invariant(size > 0);
invariant(size <= kSmallStrMaxSize);
return StringData(_buffer + 1, size);
}
StringData _getBigStrNoCheck() const {
// We use a ConstSharedBuffer that is only allocated once and assume the string size is
// just the originally allocated capacity.
size_t size = _sharedBuffer.capacity();
invariant(size > kSmallStrMaxSize);
invariant(size <= kBigStrMaxSize);
return StringData(_sharedBuffer.get(), size);
}
Format _format = Format::kNull;
// An extra byte of space is required to store the size for the
// kSmallStr Format. Zero the buffer so we don't need to write

View File

@ -37,12 +37,39 @@
namespace mongo {
namespace {
void BM_RecordIdCompareLong(benchmark::State& state) {
RecordId rid(1 << 31);
RecordId tmp(1);
for (auto _ : state) {
benchmark::DoNotOptimize(rid > tmp);
benchmark::ClobberMemory();
}
}
void BM_RecordIdCompareSmallStr(benchmark::State& state) {
std::string str1(20, 'x');
RecordId rid1(str1.c_str(), str1.size());
RecordId rid2(str1.c_str(), str1.size());
for (auto _ : state) {
benchmark::DoNotOptimize(rid1 > rid2);
benchmark::ClobberMemory();
}
}
void BM_RecordIdIsValidLong(benchmark::State& state) {
RecordId rid(1 << 31);
for (auto _ : state) {
benchmark::DoNotOptimize(rid.isValid());
benchmark::ClobberMemory();
}
}
void BM_RecordIdCopyLong(benchmark::State& state) {
RecordId rid(1 << 31);
for (auto _ : state) {
RecordId tmp;
benchmark::ClobberMemory();
benchmark::DoNotOptimize(tmp = rid);
benchmark::ClobberMemory();
}
}
@ -50,8 +77,8 @@ void BM_RecordIdCopyOID(benchmark::State& state) {
RecordId rid = record_id_helpers::keyForOID(OID::gen());
for (auto _ : state) {
RecordId tmp;
benchmark::ClobberMemory();
benchmark::DoNotOptimize(tmp = rid);
benchmark::ClobberMemory();
}
}
@ -63,8 +90,8 @@ void BM_RecordIdCopyMedString(benchmark::State& state) {
RecordId rid = RecordId(buf, bufLen);
for (auto _ : state) {
RecordId tmp;
benchmark::ClobberMemory();
benchmark::DoNotOptimize(tmp = rid);
benchmark::ClobberMemory();
}
}
@ -76,28 +103,28 @@ void BM_RecordIdCopyBigString(benchmark::State& state) {
RecordId rid = RecordId(buf, bufLen);
for (auto _ : state) {
RecordId tmp;
benchmark::ClobberMemory();
benchmark::DoNotOptimize(tmp = rid);
benchmark::ClobberMemory();
}
}
void BM_RecordIdFormatLong(benchmark::State& state) {
RecordId rid(1 << 31);
for (auto _ : state) {
benchmark::ClobberMemory();
benchmark::DoNotOptimize(rid.withFormat([](RecordId::Null) { return false; },
[](std::int64_t val) { return false; },
[](const char* str, int size) { return false; }));
benchmark::ClobberMemory();
}
}
void BM_RecordIdFormatString(benchmark::State& state) {
RecordId rid = record_id_helpers::keyForOID(OID::gen());
for (auto _ : state) {
benchmark::ClobberMemory();
benchmark::DoNotOptimize(rid.withFormat([](RecordId::Null) { return false; },
[](std::int64_t val) { return false; },
[](const char* str, int size) { return false; }));
benchmark::ClobberMemory();
}
}
@ -106,6 +133,10 @@ BENCHMARK(BM_RecordIdCopyOID);
BENCHMARK(BM_RecordIdCopyMedString);
BENCHMARK(BM_RecordIdCopyBigString);
BENCHMARK(BM_RecordIdCompareLong);
BENCHMARK(BM_RecordIdCompareSmallStr);
BENCHMARK(BM_RecordIdIsValidLong);
BENCHMARK(BM_RecordIdFormatLong);
BENCHMARK(BM_RecordIdFormatString);

View File

@ -694,7 +694,8 @@ public:
auto& metricsCollector = ResourceConsumption::MetricsCollector::get(_opCtx);
metricsCollector.incrementOneDocRead(value.size);
return {{id, {static_cast<const char*>(value.data), static_cast<int>(value.size)}}};
return {
{std::move(id), {static_cast<const char*>(value.data), static_cast<int>(value.size)}}};
}
void save() final {
@ -2062,7 +2063,7 @@ boost::optional<Record> WiredTigerRecordStoreCursorBase::next() {
metricsCollector.incrementOneDocRead(value.size);
_lastReturnedId = id;
return {{id, {static_cast<const char*>(value.data), static_cast<int>(value.size)}}};
return {{std::move(id), {static_cast<const char*>(value.data), static_cast<int>(value.size)}}};
}
boost::optional<Record> WiredTigerRecordStoreCursorBase::seekExact(const RecordId& id) {
@ -2218,13 +2219,11 @@ bool WiredTigerRecordStoreCursorBase::restore() {
int cmp;
int ret = wiredTigerPrepareConflictRetry(_opCtx, [&] { return c->search_near(c, &cmp); });
RecordId id;
if (ret == WT_NOTFOUND) {
_eof = true;
return !_rs._isCapped;
}
invariantWTOK(ret);
id = getKey(c);
if (cmp == 0)
return true; // Landed right where we left off.