From 9b7a803ba98afab6b101c32fe44f9043c170b1b5 Mon Sep 17 00:00:00 2001 From: Ian Boros Date: Tue, 7 Feb 2023 21:51:01 +0000 Subject: SERVER-73785 Improve SBE ArrayEnumerator to avoid repeated strlen calls --- src/mongo/db/exec/sbe/sbe_mkobj_test.cpp | 2 +- src/mongo/db/exec/sbe/stages/makeobj.cpp | 2 +- src/mongo/db/exec/sbe/stages/scan.cpp | 6 +++--- src/mongo/db/exec/sbe/values/bson.cpp | 13 +++++++------ src/mongo/db/exec/sbe/values/bson.h | 6 +++++- src/mongo/db/exec/sbe/values/makeobj_spec.h | 5 +++-- src/mongo/db/exec/sbe/values/value.cpp | 29 ++++++++++++++++++++--------- src/mongo/db/exec/sbe/values/value.h | 6 +++++- src/mongo/db/exec/sbe/vm/vm.cpp | 28 ++++++++++++++-------------- 9 files changed, 59 insertions(+), 38 deletions(-) (limited to 'src/mongo/db/exec') diff --git a/src/mongo/db/exec/sbe/sbe_mkobj_test.cpp b/src/mongo/db/exec/sbe/sbe_mkobj_test.cpp index 40df6e6491c..64433238c9c 100644 --- a/src/mongo/db/exec/sbe/sbe_mkobj_test.cpp +++ b/src/mongo/db/exec/sbe/sbe_mkobj_test.cpp @@ -50,7 +50,7 @@ public: be += 4; const char* end = be + obj.objsize(); while (*be != 0) { - auto sv = bson::fieldNameView(be); + auto sv = bson::fieldNameAndLength(be); auto [tag, val] = bson::convertFrom(be, end, sv.size()); be = bson::advance(be, sv.size()); diff --git a/src/mongo/db/exec/sbe/stages/makeobj.cpp b/src/mongo/db/exec/sbe/stages/makeobj.cpp index a2cb193240b..9f14c32eef5 100644 --- a/src/mongo/db/exec/sbe/stages/makeobj.cpp +++ b/src/mongo/db/exec/sbe/stages/makeobj.cpp @@ -191,7 +191,7 @@ void MakeObjStageBase::produceObject() { // Skip document length. be += 4; while (*be != 0) { - auto sv = bson::fieldNameView(be); + auto sv = bson::fieldNameAndLength(be); auto key = StringMapHasher{}.hashed_key(StringData(sv)); if (!isFieldProjectedOrRestricted(key)) { diff --git a/src/mongo/db/exec/sbe/stages/scan.cpp b/src/mongo/db/exec/sbe/stages/scan.cpp index 6ff452ed30e..d0f19ed8cdf 100644 --- a/src/mongo/db/exec/sbe/stages/scan.cpp +++ b/src/mongo/db/exec/sbe/stages/scan.cpp @@ -474,7 +474,7 @@ PlanState ScanStage::getNext() { auto name = StringData{_fields[0]}; auto [tag, val] = [start, last, end, name] { for (auto bsonElement = start; bsonElement != last;) { - auto field = bson::fieldNameView(bsonElement); + auto field = bson::fieldNameAndLength(bsonElement); if (field == name) { return bson::convertFrom(bsonElement, end, field.size()); } @@ -499,7 +499,7 @@ PlanState ScanStage::getNext() { // machine instructions) in front of the hashtable. When we "miss" in the bloom // filter, we can quickly skip over a field without having to generate the hash for // the field. - auto field = bson::fieldNameView(bsonElement); + auto field = bson::fieldNameAndLength(bsonElement); const size_t offset = computeFieldMaskOffset(field.rawData(), field.size()); if (!(_fieldsBloomFilter & computeFieldMask(offset))) { bsonElement = bson::advance(bsonElement, field.size()); @@ -1032,7 +1032,7 @@ PlanState ParallelScanStage::getNext() { accessor->reset(); } while (*be != 0) { - auto sv = bson::fieldNameView(be); + auto sv = bson::fieldNameAndLength(be); if (auto it = _fieldAccessors.find(sv); it != _fieldAccessors.end()) { // Found the field so convert it to Value. auto [tag, val] = bson::convertFrom(be, end, sv.size()); diff --git a/src/mongo/db/exec/sbe/values/bson.cpp b/src/mongo/db/exec/sbe/values/bson.cpp index 446a71b2301..877fd6405b5 100644 --- a/src/mongo/db/exec/sbe/values/bson.cpp +++ b/src/mongo/db/exec/sbe/values/bson.cpp @@ -167,13 +167,13 @@ std::pair convertFrom(const char* be, if constexpr (View) { return {value::TypeTags::bsonObject, value::bitcastFrom(be)}; } - // Skip document length. + const auto objEnd = be + ConstDataView(be).read>(); be += 4; auto [tag, val] = value::makeNewObject(); - auto obj = value::getObjectView(val); + const auto obj = value::getObjectView(val); - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != objEnd - 1) { + auto sv = bson::fieldNameAndLength(be); auto [tag, val] = convertFrom(be, end, sv.size()); obj->push_back(sv, tag, val); @@ -187,12 +187,13 @@ std::pair convertFrom(const char* be, return {value::TypeTags::bsonArray, value::bitcastFrom(be)}; } // Skip array length. + const auto arrEnd = be + ConstDataView(be).read>(); be += 4; auto [tag, val] = value::makeNewArray(); auto arr = value::getArrayView(val); - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != arrEnd - 1) { + auto sv = bson::fieldNameAndLength(be); auto [tag, val] = convertFrom(be, end, sv.size()); arr->push_back(tag, val); diff --git a/src/mongo/db/exec/sbe/values/bson.h b/src/mongo/db/exec/sbe/values/bson.h index 0965ba11421..622c9c1da98 100644 --- a/src/mongo/db/exec/sbe/values/bson.h +++ b/src/mongo/db/exec/sbe/values/bson.h @@ -48,10 +48,14 @@ std::pair convertFrom(const BSONElement& elem) { const char* advance(const char* be, size_t fieldNameSize); -inline auto fieldNameView(const char* be) noexcept { +inline auto fieldNameAndLength(const char* be) noexcept { return StringData{be + 1}; } +inline const char* fieldNameRaw(const char* be) noexcept { + return be + 1; +} + template void convertToBsonObj(ArrayBuilder& builder, value::Array* arr); diff --git a/src/mongo/db/exec/sbe/values/makeobj_spec.h b/src/mongo/db/exec/sbe/values/makeobj_spec.h index 9e6ee5a9119..f260e94d812 100644 --- a/src/mongo/db/exec/sbe/values/makeobj_spec.h +++ b/src/mongo/db/exec/sbe/values/makeobj_spec.h @@ -111,10 +111,11 @@ public: if (rootTag == value::TypeTags::bsonObject) { if (!(nFieldsNeededIfInclusion == 0 && fieldBehavior == FieldBehavior::keep)) { auto be = value::bitcastTo(rootVal); + const auto end = be + ConstDataView(be).read>(); // Skip document length. be += 4; - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != end - 1) { + auto sv = bson::fieldNameAndLength(be); auto key = StringMapHasher{}.hashed_key(StringData(sv)); auto nextBe = bson::advance(be, sv.size()); diff --git a/src/mongo/db/exec/sbe/values/value.cpp b/src/mongo/db/exec/sbe/values/value.cpp index a24883a7be9..69252625fdd 100644 --- a/src/mongo/db/exec/sbe/values/value.cpp +++ b/src/mongo/db/exec/sbe/values/value.cpp @@ -859,8 +859,7 @@ std::pair ArrayEnumerator::getViewOfValue() const { } else if (_arraySet) { return {_iter->first, _iter->second}; } else { - auto sv = bson::fieldNameView(_arrayCurrent); - return bson::convertFrom(_arrayCurrent, _arrayEnd, sv.size()); + return bson::convertFrom(_arrayCurrent, _arrayEnd, _fieldNameSize); } } @@ -878,12 +877,24 @@ bool ArrayEnumerator::advance() { return _iter != _arraySet->values().end(); } else { - if (*_arrayCurrent != 0) { - auto sv = bson::fieldNameView(_arrayCurrent); - _arrayCurrent = bson::advance(_arrayCurrent, sv.size()); + if (_arrayCurrent != _arrayEnd - 1) { + _arrayCurrent = bson::advance(_arrayCurrent, _fieldNameSize); + + // Hand rolled strlen() code is faster than calling into shared library + // strlen() for small strings. Since this is a BSON array we are guaranteed + // the key names are small. + if (_arrayCurrent != _arrayEnd - 1) { + size_t keySize = 0; + auto* fieldName = bson::fieldNameRaw(_arrayCurrent); + while (*fieldName) { + keySize++; + fieldName++; + } + _fieldNameSize = keySize; + } } - return *_arrayCurrent != 0; + return _arrayCurrent != _arrayEnd - 1; } } @@ -891,7 +902,7 @@ std::pair ObjectEnumerator::getViewOfValue() const { if (_object) { return _object->getAt(_index); } else { - auto sv = bson::fieldNameView(_objectCurrent); + auto sv = bson::fieldNameAndLength(_objectCurrent); return bson::convertFrom(_objectCurrent, _objectEnd, sv.size()); } } @@ -905,7 +916,7 @@ bool ObjectEnumerator::advance() { return _index < _object->size(); } else { if (*_objectCurrent != 0) { - auto sv = bson::fieldNameView(_objectCurrent); + auto sv = bson::fieldNameAndLength(_objectCurrent); _objectCurrent = bson::advance(_objectCurrent, sv.size()); } @@ -923,7 +934,7 @@ StringData ObjectEnumerator::getFieldName() const { } } else { if (*_objectCurrent != 0) { - return bson::fieldNameView(_objectCurrent); + return bson::fieldNameAndLength(_objectCurrent); } else { return ""_sd; } diff --git a/src/mongo/db/exec/sbe/values/value.h b/src/mongo/db/exec/sbe/values/value.h index afd11b94662..c6c88561aa4 100644 --- a/src/mongo/db/exec/sbe/values/value.h +++ b/src/mongo/db/exec/sbe/values/value.h @@ -1643,6 +1643,9 @@ public: auto bson = getRawPointerView(val); _arrayCurrent = bson + 4; _arrayEnd = bson + ConstDataView(bson).read>(); + if (_arrayCurrent != _arrayEnd - 1) { + _fieldNameSize = strlen(_arrayCurrent + 1); + } } else { MONGO_UNREACHABLE; } @@ -1661,7 +1664,7 @@ public: } else if (_arraySet) { return _iter == _arraySet->values().end(); } else { - return *_arrayCurrent == 0; + return _arrayCurrent == _arrayEnd - 1; } } @@ -1682,6 +1685,7 @@ private: // bsonArray const char* _arrayCurrent{nullptr}; const char* _arrayEnd{nullptr}; + size_t _fieldNameSize = 0; }; /** diff --git a/src/mongo/db/exec/sbe/vm/vm.cpp b/src/mongo/db/exec/sbe/vm/vm.cpp index 3426a691ccf..0f2fa427ded 100644 --- a/src/mongo/db/exec/sbe/vm/vm.cpp +++ b/src/mongo/db/exec/sbe/vm/vm.cpp @@ -964,11 +964,11 @@ FastTuple ByteCode::getField(value::TypeTag return {false, tag, val}; } else if (objTag == value::TypeTags::bsonObject) { auto be = value::bitcastTo(objValue); - auto end = be + ConstDataView(be).read>(); + const auto end = be + ConstDataView(be).read>(); // Skip document length. be += 4; while (be != end - 1) { - auto sv = bson::fieldNameView(be); + auto sv = bson::fieldNameAndLength(be); if (sv == fieldStr) { auto [tag, val] = bson::convertFrom(be, end, fieldStr.size()); @@ -1383,12 +1383,12 @@ FastTuple ByteCode::setField() { if (objTag == value::TypeTags::bsonObject) { auto be = value::bitcastTo(objVal); - auto end = be + ConstDataView(be).read>(); + const auto end = be + ConstDataView(be).read>(); // Skip document length. be += 4; - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != end - 1) { + auto sv = bson::fieldNameAndLength(be); if (sv != fieldName) { auto [tag, val] = bson::convertFrom(be, end, sv.size()); @@ -1424,12 +1424,12 @@ FastTuple ByteCode::setField() { if (objTag == value::TypeTags::bsonObject) { auto be = value::bitcastTo(objVal); - auto end = be + ConstDataView(be).read>(); + const auto end = be + ConstDataView(be).read>(); // Skip document length. be += 4; - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != end - 1) { + auto sv = bson::fieldNameAndLength(be); if (sv != fieldName) { auto [tag, val] = bson::convertFrom(be, end, sv.size()); @@ -1908,11 +1908,11 @@ FastTuple ByteCode::builtinDropFields(Arity if (tagInObj == value::TypeTags::bsonObject) { auto be = value::bitcastTo(valInObj); - auto end = be + ConstDataView(be).read>(); + const auto end = be + ConstDataView(be).read>(); // Skip document length. be += 4; - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != end - 1) { + auto sv = bson::fieldNameAndLength(be); if (restrictFieldsSet.count(sv) == 0) { auto [tag, val] = bson::convertFrom(be, end, sv.size()); @@ -1984,11 +1984,11 @@ FastTuple ByteCode::builtinKeepFields(Arity if (tagInObj == value::TypeTags::bsonObject) { auto be = value::bitcastTo(valInObj); - auto end = be + ConstDataView(be).read>(); + const auto end = be + ConstDataView(be).read>(); // Skip document length. be += 4; - while (*be != 0) { - auto sv = bson::fieldNameView(be); + while (be != end - 1) { + auto sv = bson::fieldNameAndLength(be); if (keepFieldsSet.count(sv) == 1) { auto [tag, val] = bson::convertFrom(be, end, sv.size()); -- cgit v1.2.1