perf: optimize serialization of arrays of trivial D-Bus types (#340)

* Improve performance of std::vector<> per Issue #339.

* refactor: improve performance of vector serialization

---------

Co-authored-by: Plinio Andrade <plinio.andrade@oracle.com>
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
This commit is contained in:
pliniofpa
2023-07-27 12:31:49 -04:00
committed by GitHub
parent 98f4929337
commit 29c877a89a
3 changed files with 127 additions and 56 deletions

146
include/sdbus-c++/Message.h Normal file → Executable file
View File

@@ -85,6 +85,10 @@ namespace sdbus {
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);
template <typename _Element> Message& operator<<(const std::vector<_Element>& items);
template <typename _Key, typename _Value> Message& operator<<(const std::map<_Key, _Value>& items);
template <typename... _ValueTypes> Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes> Message& operator<<(const std::tuple<_ValueTypes...>& item);
Message& operator>>(bool& item);
Message& operator>>(int16_t& item);
@@ -101,6 +105,10 @@ namespace sdbus {
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
template <typename _Element> Message& operator>>(std::vector<_Element>& items);
template <typename _Key, typename _Value> Message& operator>>(std::map<_Key, _Value>& items);
template <typename... _ValueTypes> Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes> Message& operator>>(std::tuple<_ValueTypes...>& item);
Message& openContainer(const std::string& signature);
Message& closeContainer();
@@ -146,6 +154,10 @@ namespace sdbus {
class Factory;
private:
void appendArray(char type, const void *ptr, size_t size);
void readArray(char type, const void **ptr, size_t *size);
protected:
Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept;
@@ -245,37 +257,46 @@ namespace sdbus {
};
template <typename _Element>
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
inline Message& Message::operator<<(const std::vector<_Element>& items)
{
msg.openContainer(signature_of<_Element>::str());
// Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step serialization of individual elements.
if constexpr (signature_of<_Element>::is_trivial_dbus_type && !std::is_same_v<_Element, bool>)
{
appendArray(*signature_of<_Element>::str().c_str(), items.data(), items.size() * sizeof(_Element));
}
else
{
openContainer(signature_of<_Element>::str());
for (const auto& item : items)
msg << item;
for (const auto& item : items)
*this << item;
msg.closeContainer();
closeContainer();
}
return msg;
return *this;
}
template <typename _Key, typename _Value>
inline Message& operator<<(Message& msg, const std::map<_Key, _Value>& items)
inline Message& Message::operator<<(const std::map<_Key, _Value>& items)
{
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
msg.openContainer(arraySignature);
openContainer(arraySignature);
for (const auto& item : items)
{
msg.openDictEntry(dictEntrySignature);
msg << item.first;
msg << item.second;
msg.closeDictEntry();
openDictEntry(dictEntrySignature);
*this << item.first;
*this << item.second;
closeDictEntry();
}
msg.closeContainer();
closeContainer();
return msg;
return *this;
}
namespace detail
@@ -296,78 +317,91 @@ namespace sdbus {
}
template <typename... _ValueTypes>
inline Message& operator<<(Message& msg, const Struct<_ValueTypes...>& item)
inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
{
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
assert(structSignature.size() > 2);
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
msg.openStruct(structContentSignature);
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
msg.closeStruct();
openStruct(structContentSignature);
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
closeStruct();
return msg;
return *this;
}
template <typename... _ValueTypes>
inline Message& operator<<(Message& msg, const std::tuple<_ValueTypes...>& item)
inline Message& Message::operator<<(const std::tuple<_ValueTypes...>& item)
{
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
return msg;
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
return *this;
}
template <typename _Element>
inline Message& operator>>(Message& msg, std::vector<_Element>& items)
inline Message& Message::operator>>(std::vector<_Element>& items)
{
if(!msg.enterContainer(signature_of<_Element>::str()))
return msg;
while (true)
// Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step deserialization of individual elements.
if constexpr (signature_of<_Element>::is_trivial_dbus_type && !std::is_same_v<_Element, bool>)
{
_Element elem;
if (msg >> elem)
items.emplace_back(std::move(elem));
else
break;
size_t arraySize{};
const _Element* arrayPtr{};
readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
}
else
{
if(!enterContainer(signature_of<_Element>::str()))
return *this;
while (true)
{
_Element elem;
if (*this >> elem)
items.emplace_back(std::move(elem));
else
break;
}
clearFlags();
exitContainer();
}
msg.clearFlags();
msg.exitContainer();
return msg;
return *this;
}
template <typename _Key, typename _Value>
inline Message& operator>>(Message& msg, std::map<_Key, _Value>& items)
inline Message& Message::operator>>(std::map<_Key, _Value>& items)
{
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
if (!msg.enterContainer(arraySignature))
return msg;
if (!enterContainer(arraySignature))
return *this;
while (true)
{
if (!msg.enterDictEntry(dictEntrySignature))
if (!enterDictEntry(dictEntrySignature))
break;
_Key key;
_Value value;
msg >> key >> value;
*this >> key >> value;
items.emplace(std::move(key), std::move(value));
msg.exitDictEntry();
exitDictEntry();
}
msg.clearFlags();
clearFlags();
msg.exitContainer();
exitContainer();
return msg;
return *this;
}
namespace detail
@@ -388,27 +422,27 @@ namespace sdbus {
}
template <typename... _ValueTypes>
inline Message& operator>>(Message& msg, Struct<_ValueTypes...>& item)
inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
{
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
if (!msg.enterStruct(structContentSignature))
return msg;
if (!enterStruct(structContentSignature))
return *this;
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
msg.exitStruct();
exitStruct();
return msg;
return *this;
}
template <typename... _ValueTypes>
inline Message& operator>>(Message& msg, std::tuple<_ValueTypes...>& item)
inline Message& Message::operator>>(std::tuple<_ValueTypes...>& item)
{
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
return msg;
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
return *this;
}
}

View File

@@ -94,6 +94,7 @@ namespace sdbus {
struct signature_of
{
static constexpr bool is_valid = false;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -108,6 +109,7 @@ namespace sdbus {
struct signature_of<void>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -119,6 +121,7 @@ namespace sdbus {
struct signature_of<bool>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -130,6 +133,7 @@ namespace sdbus {
struct signature_of<uint8_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -141,6 +145,7 @@ namespace sdbus {
struct signature_of<int16_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -152,6 +157,7 @@ namespace sdbus {
struct signature_of<uint16_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -163,6 +169,7 @@ namespace sdbus {
struct signature_of<int32_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -174,6 +181,7 @@ namespace sdbus {
struct signature_of<uint32_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -185,6 +193,7 @@ namespace sdbus {
struct signature_of<int64_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -196,6 +205,7 @@ namespace sdbus {
struct signature_of<uint64_t>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -207,6 +217,7 @@ namespace sdbus {
struct signature_of<double>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
@@ -218,6 +229,7 @@ namespace sdbus {
struct signature_of<char*>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -229,6 +241,7 @@ namespace sdbus {
struct signature_of<const char*>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -240,6 +253,7 @@ namespace sdbus {
struct signature_of<char[_N]>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -251,6 +265,7 @@ namespace sdbus {
struct signature_of<const char[_N]>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -262,6 +277,7 @@ namespace sdbus {
struct signature_of<std::string>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -273,6 +289,7 @@ namespace sdbus {
struct signature_of<Struct<_ValueTypes...>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -288,6 +305,7 @@ namespace sdbus {
struct signature_of<Variant>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -299,6 +317,7 @@ namespace sdbus {
struct signature_of<ObjectPath>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -310,6 +329,7 @@ namespace sdbus {
struct signature_of<Signature>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -321,6 +341,7 @@ namespace sdbus {
struct signature_of<UnixFd>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -332,6 +353,7 @@ namespace sdbus {
struct signature_of<std::vector<_Element>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
@@ -343,6 +365,7 @@ namespace sdbus {
struct signature_of<std::map<_Key, _Value>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{

14
src/Message.cpp Normal file → Executable file
View File

@@ -226,6 +226,11 @@ Message& Message::operator<<(const UnixFd &item)
return *this;
}
void Message::appendArray(char type, const void *ptr, size_t size)
{
auto r = sd_bus_message_append_array((sd_bus_message*)msg_, type, ptr, size);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an array", -r);
}
Message& Message::operator>>(bool& item)
{
@@ -318,6 +323,15 @@ Message& Message::operator>>(uint64_t& item)
return *this;
}
void Message::readArray(char type, const void **ptr, size_t *size)
{
auto r = sd_bus_message_read_array((sd_bus_message*)msg_, type, ptr, size);
if (r == 0)
ok_ = false;
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize an array", -r);
}
Message& Message::operator>>(double& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);