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:
parent
9b3bf32632
commit
2119cbfd59
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user