Improve PropertyStore (#1142)

Improve AnyVersionHasLabelProperty by doing less work in some instances.
Improve FindSpecificProperty.
This commit is contained in:
Gareth Andrew Lloyd 2023-09-09 13:00:43 +01:00 committed by GitHub
parent 0403b67073
commit 1bd47318cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 188 additions and 109 deletions

View File

@ -106,10 +106,12 @@ inline bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, Prop
const Delta *delta = nullptr; const Delta *delta = nullptr;
{ {
auto guard = std::shared_lock{vertex.lock}; auto guard = std::shared_lock{vertex.lock};
has_label = utils::Contains(vertex.labels, label);
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
deleted = vertex.deleted;
delta = vertex.delta; delta = vertex.delta;
deleted = vertex.deleted;
has_label = utils::Contains(vertex.labels, label);
// Avoid IsPropertyEqual if already not possible
if (delta == nullptr && (deleted || !has_label)) return false;
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
} }
if (!deleted && has_label && current_value_equal_to_value) { if (!deleted && has_label && current_value_equal_to_value) {

View File

@ -141,11 +141,25 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
deleted = vertex.deleted; deleted = vertex.deleted;
delta = vertex.delta; delta = vertex.delta;
size_t i = 0; // Avoid IsPropertyEqual if already not possible
for (const auto &property : properties) { if (delta == nullptr && (deleted || !has_label)) return false;
current_value_equal_to_value[i] = vertex.properties.IsPropertyEqual(property, values[i]);
property_array.values[i] = property; if (delta) {
i++; // If delta we need to fetch for later processing
size_t i = 0;
for (const auto &property : properties) {
current_value_equal_to_value[i] = vertex.properties.IsPropertyEqual(property, values[i]);
property_array.values[i] = property;
i++;
}
} else {
// otherwise do a short-circuiting check (we already know !deleted && has_label)
size_t i = 0;
for (const auto &property : properties) {
if (!vertex.properties.IsPropertyEqual(property, values[i])) return false;
i++;
}
return true;
} }
} }

View File

@ -316,7 +316,7 @@ class Reader {
return ret; return ret;
} }
std::optional<int64_t> ReadUint(Size size) { std::optional<uint64_t> ReadUint(Size size) {
uint64_t ret = 0; uint64_t ret = 0;
switch (size) { switch (size) {
case Size::INT8: { case Size::INT8: {
@ -488,127 +488,146 @@ std::optional<TemporalData> DecodeTemporalData(Reader &reader) {
} // namespace } // namespace
// Function used to decode a PropertyValue from a byte stream. It can either // Function used to decode a PropertyValue from a byte stream.
// decode or skip the encoded PropertyValue, depending on the supplied value
// pointer.
// //
// @sa ComparePropertyValue // @sa ComparePropertyValue
[[nodiscard]] bool DecodePropertyValue(Reader *reader, Type type, Size payload_size, PropertyValue *value) { [[nodiscard]] bool DecodePropertyValue(Reader *reader, Type type, Size payload_size, PropertyValue &value) {
switch (type) { switch (type) {
case Type::EMPTY: { case Type::EMPTY: {
return false; return false;
} }
case Type::NONE: { case Type::NONE: {
if (value) { value = PropertyValue();
*value = PropertyValue();
}
return true; return true;
} }
case Type::BOOL: { case Type::BOOL: {
if (value) { if (payload_size == Size::INT64) {
if (payload_size == Size::INT64) { value = PropertyValue(true);
*value = PropertyValue(true); } else {
} else { value = PropertyValue(false);
*value = PropertyValue(false);
}
} }
return true; return true;
} }
case Type::INT: { case Type::INT: {
auto int_v = reader->ReadInt(payload_size); auto int_v = reader->ReadInt(payload_size);
if (!int_v) return false; if (!int_v) return false;
if (value) { value = PropertyValue(*int_v);
*value = PropertyValue(*int_v);
}
return true; return true;
} }
case Type::DOUBLE: { case Type::DOUBLE: {
auto double_v = reader->ReadDouble(payload_size); auto double_v = reader->ReadDouble(payload_size);
if (!double_v) return false; if (!double_v) return false;
if (value) { value = PropertyValue(*double_v);
*value = PropertyValue(*double_v);
}
return true; return true;
} }
case Type::STRING: { case Type::STRING: {
auto size = reader->ReadUint(payload_size); auto size = reader->ReadUint(payload_size);
if (!size) return false; if (!size) return false;
if (value) { std::string str_v(*size, '\0');
std::string str_v(*size, '\0'); if (!reader->ReadBytes(str_v.data(), *size)) return false;
if (!reader->ReadBytes(str_v.data(), *size)) return false; value = PropertyValue(std::move(str_v));
*value = PropertyValue(std::move(str_v));
} else {
if (!reader->SkipBytes(*size)) return false;
}
return true; return true;
} }
case Type::LIST: { case Type::LIST: {
auto size = reader->ReadUint(payload_size); auto size = reader->ReadUint(payload_size);
if (!size) return false; if (!size) return false;
if (value) { std::vector<PropertyValue> list;
std::vector<PropertyValue> list; list.reserve(*size);
list.reserve(*size); for (uint64_t i = 0; i < *size; ++i) {
for (uint64_t i = 0; i < *size; ++i) { auto metadata = reader->ReadMetadata();
auto metadata = reader->ReadMetadata(); if (!metadata) return false;
if (!metadata) return false; PropertyValue item;
PropertyValue item; if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, item)) return false;
if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, &item)) return false; list.emplace_back(std::move(item));
list.emplace_back(std::move(item));
}
*value = PropertyValue(std::move(list));
} else {
for (uint64_t i = 0; i < *size; ++i) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, nullptr)) return false;
}
} }
value = PropertyValue(std::move(list));
return true; return true;
} }
case Type::MAP: { case Type::MAP: {
auto size = reader->ReadUint(payload_size); auto size = reader->ReadUint(payload_size);
if (!size) return false; if (!size) return false;
if (value) { std::map<std::string, PropertyValue> map;
std::map<std::string, PropertyValue> map; for (uint64_t i = 0; i < *size; ++i) {
for (uint64_t i = 0; i < *size; ++i) { auto metadata = reader->ReadMetadata();
auto metadata = reader->ReadMetadata(); if (!metadata) return false;
if (!metadata) return false; auto key_size = reader->ReadUint(metadata->id_size);
auto key_size = reader->ReadUint(metadata->id_size); if (!key_size) return false;
if (!key_size) return false; std::string key(*key_size, '\0');
std::string key(*key_size, '\0'); if (!reader->ReadBytes(key.data(), *key_size)) return false;
if (!reader->ReadBytes(key.data(), *key_size)) return false; PropertyValue item;
PropertyValue item; if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, item)) return false;
if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, &item)) return false; map.emplace(std::move(key), std::move(item));
map.emplace(std::move(key), std::move(item));
}
*value = PropertyValue(std::move(map));
} else {
for (uint64_t i = 0; i < *size; ++i) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
auto key_size = reader->ReadUint(metadata->id_size);
if (!key_size) return false;
if (!reader->SkipBytes(*key_size)) return false;
if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, nullptr)) return false;
}
} }
value = PropertyValue(std::move(map));
return true; return true;
} }
case Type::TEMPORAL_DATA: { case Type::TEMPORAL_DATA: {
const auto maybe_temporal_data = DecodeTemporalData(*reader); const auto maybe_temporal_data = DecodeTemporalData(*reader);
if (!maybe_temporal_data) return false; if (!maybe_temporal_data) return false;
value = PropertyValue(*maybe_temporal_data);
if (value) {
*value = PropertyValue(*maybe_temporal_data);
}
return true; return true;
} }
} }
} }
// Function used to skip a PropertyValue from a byte stream.
//
// @sa ComparePropertyValue
[[nodiscard]] bool SkipPropertyValue(Reader *reader, Type type, Size payload_size) {
switch (type) {
case Type::EMPTY: {
return false;
}
case Type::NONE:
case Type::BOOL: {
return true;
}
case Type::INT: {
return reader->ReadInt(payload_size).has_value();
}
case Type::DOUBLE: {
return reader->ReadDouble(payload_size).has_value();
}
case Type::STRING: {
auto size = reader->ReadUint(payload_size);
if (!size) return false;
if (!reader->SkipBytes(*size)) return false;
return true;
}
case Type::LIST: {
auto const size = reader->ReadUint(payload_size);
if (!size) return false;
auto size_val = *size;
for (uint64_t i = 0; i != size_val; ++i) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
if (!SkipPropertyValue(reader, metadata->type, metadata->payload_size)) return false;
}
return true;
}
case Type::MAP: {
auto const size = reader->ReadUint(payload_size);
if (!size) return false;
auto size_val = *size;
for (uint64_t i = 0; i != size_val; ++i) {
auto metadata = reader->ReadMetadata();
if (!metadata) return false;
auto key_size = reader->ReadUint(metadata->id_size);
if (!key_size) return false;
if (!reader->SkipBytes(*key_size)) return false;
if (!SkipPropertyValue(reader, metadata->type, metadata->payload_size)) return false;
}
return true;
}
case Type::TEMPORAL_DATA: {
return DecodeTemporalData(*reader).has_value();
}
}
}
// Function used to compare a PropertyValue to the one stored in the byte // Function used to compare a PropertyValue to the one stored in the byte
// stream. // stream.
// //
@ -726,7 +745,7 @@ bool EncodeProperty(Writer *writer, PropertyId property, const PropertyValue &va
} }
// Enum used to return status from the `DecodeExpectedProperty` function. // Enum used to return status from the `DecodeExpectedProperty` function.
enum class DecodeExpectedPropertyStatus { enum class ExpectedPropertyStatus {
MISSING_DATA, MISSING_DATA,
SMALLER, SMALLER,
EQUAL, EQUAL,
@ -734,9 +753,8 @@ enum class DecodeExpectedPropertyStatus {
}; };
// Function used to decode a property (PropertyId, PropertyValue) from a byte // Function used to decode a property (PropertyId, PropertyValue) from a byte
// stream. It can either decode or skip the encoded PropertyValue, depending on // stream. The `expected_property` provides another hint whether the property
// the provided PropertyValue. The `expected_property` provides another hint // should be decoded or skipped.
// whether the property should be decoded or skipped.
// //
// @return MISSING_DATA when there is not enough data in the buffer to decode // @return MISSING_DATA when there is not enough data in the buffer to decode
// the property // the property
@ -751,38 +769,65 @@ enum class DecodeExpectedPropertyStatus {
// //
// @sa DecodeAnyProperty // @sa DecodeAnyProperty
// @sa CompareExpectedProperty // @sa CompareExpectedProperty
[[nodiscard]] DecodeExpectedPropertyStatus DecodeExpectedProperty(Reader *reader, PropertyId expected_property, [[nodiscard]] ExpectedPropertyStatus DecodeExpectedProperty(Reader *reader, PropertyId expected_property,
PropertyValue *value) { PropertyValue &value) {
auto metadata = reader->ReadMetadata(); auto metadata = reader->ReadMetadata();
if (!metadata) return DecodeExpectedPropertyStatus::MISSING_DATA; if (!metadata) return ExpectedPropertyStatus::MISSING_DATA;
auto property_id = reader->ReadUint(metadata->id_size); auto property_id = reader->ReadUint(metadata->id_size);
if (!property_id) return DecodeExpectedPropertyStatus::MISSING_DATA; if (!property_id) return ExpectedPropertyStatus::MISSING_DATA;
// Don't load the value if this isn't the expected property. if (*property_id == expected_property.AsUint()) {
if (*property_id != expected_property.AsUint()) { if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, value))
value = nullptr; return ExpectedPropertyStatus::MISSING_DATA;
return ExpectedPropertyStatus::EQUAL;
} }
// Don't load the value if this isn't the expected property.
if (!SkipPropertyValue(reader, metadata->type, metadata->payload_size)) return ExpectedPropertyStatus::MISSING_DATA;
return (*property_id < expected_property.AsUint()) ? ExpectedPropertyStatus::SMALLER
: ExpectedPropertyStatus::GREATER;
}
if (!DecodePropertyValue(reader, metadata->type, metadata->payload_size, value)) // Function used to check a property exists (PropertyId) from a byte stream.
return DecodeExpectedPropertyStatus::MISSING_DATA; // It will skip the encoded PropertyValue.
//
// @return MISSING_DATA when there is not enough data in the buffer to decode
// the property
// @return SMALLER when the property that was currently read has a smaller
// property ID than the expected property; the value isn't
// loaded in this case
// @return EQUAL when the property that was currently read has an ID equal to
// the expected property ID; the value is loaded in this case
// @return GREATER when the property that was currenly read has a greater
// property ID than the expected property; the value isn't
// loaded in this case
//
// @sa DecodeAnyProperty
// @sa CompareExpectedProperty
[[nodiscard]] ExpectedPropertyStatus HasExpectedProperty(Reader *reader, PropertyId expected_property) {
auto metadata = reader->ReadMetadata();
if (!metadata) return ExpectedPropertyStatus::MISSING_DATA;
auto property_id = reader->ReadUint(metadata->id_size);
if (!property_id) return ExpectedPropertyStatus::MISSING_DATA;
if (!SkipPropertyValue(reader, metadata->type, metadata->payload_size)) return ExpectedPropertyStatus::MISSING_DATA;
if (*property_id < expected_property.AsUint()) { if (*property_id < expected_property.AsUint()) {
return DecodeExpectedPropertyStatus::SMALLER; return ExpectedPropertyStatus::SMALLER;
} else if (*property_id == expected_property.AsUint()) { } else if (*property_id == expected_property.AsUint()) {
return DecodeExpectedPropertyStatus::EQUAL; return ExpectedPropertyStatus::EQUAL;
} else { } else {
return DecodeExpectedPropertyStatus::GREATER; return ExpectedPropertyStatus::GREATER;
} }
} }
// Function used to decode a property (PropertyId, PropertyValue) from a byte // Function used to decode a property (PropertyId, PropertyValue) from a byte
// stream. It can either decode or skip the encoded PropertyValue, depending on // stream.
// the provided PropertyValue.
// //
// @sa DecodeExpectedProperty // @sa DecodeExpectedProperty
// @sa CompareExpectedProperty // @sa CompareExpectedProperty
[[nodiscard]] std::optional<PropertyId> DecodeAnyProperty(Reader *reader, PropertyValue *value) { [[nodiscard]] std::optional<PropertyId> DecodeAnyProperty(Reader *reader, PropertyValue &value) {
auto metadata = reader->ReadMetadata(); auto metadata = reader->ReadMetadata();
if (!metadata) return std::nullopt; if (!metadata) return std::nullopt;
@ -816,8 +861,7 @@ enum class DecodeExpectedPropertyStatus {
// the `value` won't be updated. // the `value` won't be updated.
// //
// @sa FindSpecificPropertyAndBufferInfo // @sa FindSpecificPropertyAndBufferInfo
[[nodiscard]] DecodeExpectedPropertyStatus FindSpecificProperty(Reader *reader, PropertyId property, [[nodiscard]] ExpectedPropertyStatus FindSpecificProperty(Reader *reader, PropertyId property, PropertyValue &value) {
PropertyValue *value) {
while (true) { while (true) {
auto ret = DecodeExpectedProperty(reader, property, value); auto ret = DecodeExpectedProperty(reader, property, value);
// Because the properties are sorted in the buffer, we only need to // Because the properties are sorted in the buffer, we only need to
@ -825,7 +869,25 @@ enum class DecodeExpectedPropertyStatus {
// `SMALLER` value indicating that the ID of the found property is smaller // `SMALLER` value indicating that the ID of the found property is smaller
// than the seeked ID. All other return values (`MISSING_DATA`, `EQUAL` and // than the seeked ID. All other return values (`MISSING_DATA`, `EQUAL` and
// `GREATER`) terminate the search. // `GREATER`) terminate the search.
if (ret != DecodeExpectedPropertyStatus::SMALLER) { if (ret != ExpectedPropertyStatus::SMALLER) {
return ret;
}
}
}
// Function used to find if property is set. It relies on the fact that the properties
// are sorted (by ID) in the buffer.
//
// @sa FindSpecificPropertyAndBufferInfo
[[nodiscard]] ExpectedPropertyStatus ExistsSpecificProperty(Reader *reader, PropertyId property) {
while (true) {
auto ret = HasExpectedProperty(reader, property);
// Because the properties are sorted in the buffer, we only need to
// continue searching for the property while this function returns a
// `SMALLER` value indicating that the ID of the found property is smaller
// than the seeked ID. All other return values (`MISSING_DATA`, `EQUAL` and
// `GREATER`) terminate the search.
if (ret != ExpectedPropertyStatus::SMALLER) {
return ret; return ret;
} }
} }
@ -858,13 +920,14 @@ SpecificPropertyAndBufferInfo FindSpecificPropertyAndBufferInfo(Reader *reader,
uint64_t all_begin = reader->GetPosition(); uint64_t all_begin = reader->GetPosition();
uint64_t all_end = reader->GetPosition(); uint64_t all_end = reader->GetPosition();
while (true) { while (true) {
auto ret = DecodeExpectedProperty(reader, property, nullptr); auto ret = HasExpectedProperty(reader, property);
if (ret == DecodeExpectedPropertyStatus::MISSING_DATA) { if (ret == ExpectedPropertyStatus::MISSING_DATA) {
break; break;
} else if (ret == DecodeExpectedPropertyStatus::SMALLER) { }
if (ret == ExpectedPropertyStatus::SMALLER) {
property_begin = reader->GetPosition(); property_begin = reader->GetPosition();
property_end = reader->GetPosition(); property_end = reader->GetPosition();
} else if (ret == DecodeExpectedPropertyStatus::EQUAL) { } else if (ret == ExpectedPropertyStatus::EQUAL) {
property_end = reader->GetPosition(); property_end = reader->GetPosition();
} }
all_end = reader->GetPosition(); all_end = reader->GetPosition();
@ -970,7 +1033,7 @@ PropertyValue PropertyStore::GetProperty(PropertyId property) const {
} }
Reader reader(data, size); Reader reader(data, size);
PropertyValue value; PropertyValue value;
if (FindSpecificProperty(&reader, property, &value) != DecodeExpectedPropertyStatus::EQUAL) return PropertyValue(); if (FindSpecificProperty(&reader, property, value) != ExpectedPropertyStatus::EQUAL) return {};
return value; return value;
} }
@ -984,7 +1047,7 @@ bool PropertyStore::HasProperty(PropertyId property) const {
data = &buffer_[1]; data = &buffer_[1];
} }
Reader reader(data, size); Reader reader(data, size);
return FindSpecificProperty(&reader, property, nullptr) == DecodeExpectedPropertyStatus::EQUAL; return ExistsSpecificProperty(&reader, property) == ExpectedPropertyStatus::EQUAL;
} }
/// TODO: andi write a unit test for it /// TODO: andi write a unit test for it
@ -1050,7 +1113,7 @@ std::map<PropertyId, PropertyValue> PropertyStore::Properties() const {
std::map<PropertyId, PropertyValue> props; std::map<PropertyId, PropertyValue> props;
while (true) { while (true) {
PropertyValue value; PropertyValue value;
auto prop = DecodeAnyProperty(&reader, &value); auto prop = DecodeAnyProperty(&reader, value);
if (!prop) break; if (!prop) break;
props.emplace(*prop, std::move(value)); props.emplace(*prop, std::move(value));
} }