From 7a6dd463ddeb36150f4e476d710879ae22dfafc9 Mon Sep 17 00:00:00 2001 From: Tomasz Grabiec Date: Wed, 25 Mar 2015 18:11:15 +0100 Subject: [PATCH] types: Optimize serialization of single-element tuples There are two sides of this optimization: 1) We don't store the length of the last component, so the representation is now shorter. 2) A single-element tuple is serialized exactly as the component it holds, which allows us to optimize conversions for such keys. --- .../single_column_primary_key_restrictions.hh | 4 +- keys.hh | 9 +-- tuple.hh | 72 ++++++++++++++++--- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/cql3/restrictions/single_column_primary_key_restrictions.hh b/cql3/restrictions/single_column_primary_key_restrictions.hh index dbe8064ff7..8580d22041 100644 --- a/cql3/restrictions/single_column_primary_key_restrictions.hh +++ b/cql3/restrictions/single_column_primary_key_restrictions.hh @@ -144,7 +144,7 @@ public: std::vector result; result.reserve(cartesian_product_size(value_vector)); for (auto&& v : make_cartesian_product(value_vector)) { - result.emplace_back(ValueType::from_optional_exploded(*_schema, v)); + result.emplace_back(ValueType::from_optional_exploded(*_schema, std::move(v))); } return result; } @@ -228,7 +228,7 @@ public: ranges.reserve(cartesian_product_size(vec_of_values)); for (auto&& prefix : make_cartesian_product(vec_of_values)) { - ranges.emplace_back(range_type::make_singular(ValueType::from_optional_exploded(*_schema, prefix))); + ranges.emplace_back(range_type::make_singular(ValueType::from_optional_exploded(*_schema, std::move(prefix)))); } return std::move(ranges); diff --git a/keys.hh b/keys.hh index f9d3a68722..1439be9657 100644 --- a/keys.hh +++ b/keys.hh @@ -56,6 +56,10 @@ public: return TopLevel::from_bytes(get_tuple_type(s)->serialize_value(v)); } + static TopLevel from_exploded(const schema& s, std::vector&& v) { + return TopLevel::from_bytes(get_tuple_type(s)->serialize_value(std::move(v))); + } + // We don't allow optional values, but provide this method as an efficient adaptor static TopLevel from_optional_exploded(const schema& s, const std::vector& v) { return TopLevel::from_bytes(get_tuple_type(s)->serialize_optionals(v)); @@ -66,10 +70,7 @@ public: } static TopLevel from_single_value(const schema& s, bytes v) { - // FIXME: optimize - std::vector values; - values.emplace_back(std::move(v)); - return from_exploded(s, values); + return TopLevel::from_bytes(get_tuple_type(s)->serialize_single(std::move(v))); } // FIXME: return views diff --git a/tuple.hh b/tuple.hh index 88f2704fed..0d6f9ad7c5 100644 --- a/tuple.hh +++ b/tuple.hh @@ -64,7 +64,13 @@ public: /* * Format: - * ... + * ...(len(value_n))? + * + * The value corresponding to the last component of types doesn't have its length encoded, + * its length is deduced from the input range. + * + * serialize_value() and serialize_optionals() for single element rely on the fact that for a single-element + * tuples their serialized form is equal to the serialized form of the component. */ template void serialize_value(const std::vector& values, bytes::iterator& out) { @@ -74,29 +80,62 @@ public: assert(values.size() == _types.size()); } + size_t n_left = _types.size(); for (auto&& wrapped : values) { auto&& val = value_traits::unwrap(wrapped); assert(val.size() <= std::numeric_limits::max()); - write(out, uint32_t(val.size())); + if (--n_left) { + write(out, uint32_t(val.size())); + } out = std::copy(val.begin(), val.end(), out); } } template size_t serialized_size(const std::vector& values) { size_t len = 0; + size_t n_left = _types.size(); for (auto&& wrapped : values) { auto&& val = value_traits::unwrap(wrapped); assert(val.size() <= std::numeric_limits::max()); - len += sizeof(uint32_t) + val.size(); + if (--n_left) { + len += sizeof(uint32_t); + } + len += val.size(); } return len; } + bytes serialize_single(bytes&& v) { + if (!AllowPrefixes) { + assert(_types.size() == 1); + } else { + if (_types.size() > 1) { + std::vector vec; + vec.reserve(1); + vec.emplace_back(std::move(v)); + return ::serialize_value(*this, vec); + } + } + return std::move(v); + } bytes serialize_value(const std::vector& values) { return ::serialize_value(*this, values); } + bytes serialize_value(std::vector&& values) { + if (_types.size() == 1 && values.size() == 1) { + return std::move(values[0]); + } + return ::serialize_value(*this, values); + } bytes serialize_optionals(const std::vector& values) { return ::serialize_value(*this, values); } + bytes serialize_optionals(std::vector&& values) { + if (_types.size() == 1 && values.size() == 1) { + assert(values[0]); + return std::move(*values[0]); + } + return ::serialize_value(*this, values); + } bytes serialize_value_deep(const std::vector& values) { // TODO: Optimize std::vector partial; @@ -133,9 +172,14 @@ public: throw marshal_exception(); } } - auto len = read_simple(_v); - if (_v.size() < len) { - throw marshal_exception(); + uint32_t len; + if (_types_left == 1) { + len = _v.size(); + } else { + len = read_simple(_v); + if (_v.size() < len) { + throw marshal_exception(); + } } _current = bytes_view(_v.begin(), len); _v.remove_prefix(len); @@ -211,15 +255,23 @@ public: bool is_prefix_of(bytes_view prefix, bytes_view value) const { assert(AllowPrefixes); + size_t n_left = _types.size(); for (auto&& type : _types) { if (prefix.empty()) { return true; } assert(!value.empty()); - auto len1 = read_simple(prefix); - auto len2 = read_simple(value); - if (prefix.size() < len1 || value.size() < len2) { - throw marshal_exception(); + uint32_t len1; + uint32_t len2; + if (--n_left) { + len1 = read_simple(prefix); + len2 = read_simple(value); + if (prefix.size() < len1 || value.size() < len2) { + throw marshal_exception(); + } + } else { + len1 = prefix.size(); + len2 = value.size(); } if (!type->equal(bytes_view(prefix.begin(), len1), bytes_view(value.begin(), len2))) { return false;