summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Boros <ian.boros@mongodb.com>2023-02-07 21:51:01 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-14 23:32:06 +0000
commit9b7a803ba98afab6b101c32fe44f9043c170b1b5 (patch)
tree10ba078a29a9637feee694d29dea6e5a92bec437
parent43315055bc002c4ec1426f45a14c9966abea1e6a (diff)
downloadmongo-9b7a803ba98afab6b101c32fe44f9043c170b1b5.tar.gz
SERVER-73785 Improve SBE ArrayEnumerator to avoid repeated strlen calls
-rw-r--r--src/mongo/db/exec/sbe/sbe_mkobj_test.cpp2
-rw-r--r--src/mongo/db/exec/sbe/stages/makeobj.cpp2
-rw-r--r--src/mongo/db/exec/sbe/stages/scan.cpp6
-rw-r--r--src/mongo/db/exec/sbe/values/bson.cpp13
-rw-r--r--src/mongo/db/exec/sbe/values/bson.h6
-rw-r--r--src/mongo/db/exec/sbe/values/makeobj_spec.h5
-rw-r--r--src/mongo/db/exec/sbe/values/value.cpp29
-rw-r--r--src/mongo/db/exec/sbe/values/value.h6
-rw-r--r--src/mongo/db/exec/sbe/vm/vm.cpp28
-rw-r--r--src/mongo/db/query/sbe_stage_builder_accumulator_test.cpp2
10 files changed, 60 insertions, 39 deletions
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<false>(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<MakeObjOutputType::object>::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<true>(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<true>(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<value::TypeTags, value::Value> convertFrom(const char* be,
if constexpr (View) {
return {value::TypeTags::bsonObject, value::bitcastFrom<const char*>(be)};
}
- // Skip document length.
+ const auto objEnd = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
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<false>(be, end, sv.size());
obj->push_back(sv, tag, val);
@@ -187,12 +187,13 @@ std::pair<value::TypeTags, value::Value> convertFrom(const char* be,
return {value::TypeTags::bsonArray, value::bitcastFrom<const char*>(be)};
}
// Skip array length.
+ const auto arrEnd = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
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<false>(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<value::TypeTags, value::Value> 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 <class ArrayBuilder>
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<const char*>(rootVal);
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
// 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<TypeTags, Value> ArrayEnumerator::getViewOfValue() const {
} else if (_arraySet) {
return {_iter->first, _iter->second};
} else {
- auto sv = bson::fieldNameView(_arrayCurrent);
- return bson::convertFrom<true>(_arrayCurrent, _arrayEnd, sv.size());
+ return bson::convertFrom<true>(_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<TypeTags, Value> ObjectEnumerator::getViewOfValue() const {
if (_object) {
return _object->getAt(_index);
} else {
- auto sv = bson::fieldNameView(_objectCurrent);
+ auto sv = bson::fieldNameAndLength(_objectCurrent);
return bson::convertFrom<true>(_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<LittleEndian<uint32_t>>();
+ 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<bool, value::TypeTags, value::Value> ByteCode::getField(value::TypeTag
return {false, tag, val};
} else if (objTag == value::TypeTags::bsonObject) {
auto be = value::bitcastTo<const char*>(objValue);
- auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
// 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<true>(be, end, fieldStr.size());
@@ -1383,12 +1383,12 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::setField() {
if (objTag == value::TypeTags::bsonObject) {
auto be = value::bitcastTo<const char*>(objVal);
- auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
// 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<false>(be, end, sv.size());
@@ -1424,12 +1424,12 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::setField() {
if (objTag == value::TypeTags::bsonObject) {
auto be = value::bitcastTo<const char*>(objVal);
- auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
// 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<false>(be, end, sv.size());
@@ -1908,11 +1908,11 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinDropFields(Arity
if (tagInObj == value::TypeTags::bsonObject) {
auto be = value::bitcastTo<const char*>(valInObj);
- auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
// 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<false>(be, end, sv.size());
@@ -1984,11 +1984,11 @@ FastTuple<bool, value::TypeTags, value::Value> ByteCode::builtinKeepFields(Arity
if (tagInObj == value::TypeTags::bsonObject) {
auto be = value::bitcastTo<const char*>(valInObj);
- auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
+ const auto end = be + ConstDataView(be).read<LittleEndian<uint32_t>>();
// 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<false>(be, end, sv.size());
diff --git a/src/mongo/db/query/sbe_stage_builder_accumulator_test.cpp b/src/mongo/db/query/sbe_stage_builder_accumulator_test.cpp
index 8967f05841c..d98c96fe8a9 100644
--- a/src/mongo/db/query/sbe_stage_builder_accumulator_test.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_accumulator_test.cpp
@@ -1892,7 +1892,7 @@ public:
const char* bsonElt = valuesToAgg.objdata() + 4;
const char* bsonEnd = bsonElt + valuesToAgg.objsize();
while (*bsonElt != 0) {
- auto fieldName = sbe::bson::fieldNameView(bsonElt);
+ auto fieldName = sbe::bson::fieldNameAndLength(bsonElt);
// Convert the BSON value to an SBE value and put it inside the input slot.
auto [tag, val] = sbe::bson::convertFrom<false>(bsonElt, bsonEnd, fieldName.size());