I
Ilya_MJune 6, 2018, 4:07 p.m.

Serialization To and For Qt

In the first part of a series of articles, the author addressed the problem of tuning and merging messages and reducing their load in telemetry sensors.

This part is about message payload and its optimization.


There are many methods to serialize an object with Qt . In the first part of the article series, we used JSON . To do this, all information about the sensor is stored in QJsonObject , and QJsonDocument takes care of the flow of values in QByteArray.

QJsonObject jobject;

jobject[ "SensorID" ] = m_id;
jobject[ "AmbientTemperature" ] = m_ambientTemperature;
jobject[ "ObjectTemperature" ] = m_objectTemperature;
jobject[ "AccelerometerX" ] = m_accelerometerX;
jobject[ "AccelerometerY" ] = m_accelerometerY;
jobject[ "AccelerometerZ" ] = m_accelerometerZ;
jobject[ "Altitude" ] = m_altitude;
jobject[ "Light" ] = m_light;
jobject[ "Humidity" ] = m_humidity;

QJsonDocument doc( jobject );

return doc.toJson();

JSON has several advantages:

  • text JSON is descriptive, making it human-readable;
  • and the information is structured;
  • the exchange of basic information is simple and clear;
  • JSON allows you to expand messages with additional values;
  • There are many solutions for parsing and parsing JSON in the cloud.

However, this approach has some limitations. First, creating a JSON message can be a heavy operation that takes many cycles. The benchmark of the second part of testing shows that serialization and deserialization of 10,000 messages takes about 263 ms. This may not sound like a significant number of messages, but in this context, time corresponds to energy. This can significantly affect the sensor, which is designed to be used without charging for a long time.

Another aspect is that the payload for the sensor update MQTT message is 346 bytes. Given that the sensor only sends eight takes and one limited line, this can be a potentially large computational overhead.

inside the comments of the previous post, it is recommended to use QJsonDocument : Compact (compact), which reduces the size of the payload, on average to 290 bytes

How can this be improved?

As most of you know, there is also binary JSON which can reduce readability, but all other aspects are still relevant. The main thing about running benchmarks is that a simple doc.toJson switch in doc.toBinaryData will double the speed of the test, reducing the benchmark iteration to 125 milliseconds.

After checking the load, the message size is 338 bytes, the difference is almost imperceptible. However, this may change in different scenarios, for example if more lines are added within the message.

Depending on the requirements, it is possible to add third-party projects and other features.

In case the project is !within the Qt world", the entire data stream is defined and does not change QDataStream , is a possible option.

We add support for this to the SensorInformation class, which requires the addition of two statements.

QDataStream &operator<<(QDataStream &, const SensorInformation &);
QDataStream &operator>>(QDataStream &, SensorInformation &);

The implementation is quite simple, below is an example of serialization:

QDataStream &operator<<(QDataStream &out, const SensorInformation &item)
{
    QDataStream::FloatingPointPrecision prev = out.floatingPointPrecision();
    out.setFloatingPointPrecision(QDataStream::DoublePrecision);
    out << item.m_id
        << item.m_ambientTemperature
        << item.m_objectTemperature
        << item.m_accelerometerX
        << item.m_accelerometerY
        << item.m_accelerometerZ
        << item.m_altitude
        << item.m_light
        << item.m_humidity;
    out.setFloatingPointPrecision(prev);
    return out;
}

When using QDataStream, the benchmark time was 26 milliseconds, which is almost 10 times faster than text JSON. Also, the average message size is only 84 bytes compared to 290. Therefore, if the above limits are acceptable, QDataStream is a good option for the task at hand.

If the project allows you to add additional third-party components, one of the most well-known serialization solutions is the Google Buffer Protocol (protobuf).

To add protobuf to our solution, we need to make a couple of changes. First, protobuf uses IDL to describe data structures or messages. Then, the development of the information sensor:

syntax = "proto2" ;

package serialtest;

message Sensor {
    required string id = 1;
    required double ambientTemperature = 2;
    required double objectTemperature = 3;
    required double accelerometerX = 4;
    required double accelerometerY = 5;
    required double accelerometerZ = 6;
    required double altitude = 7;
    required double light = 8;
    required double humidity = 9;
}

To add a protobuf (protocol) code generator to a qmake project, you need to add an extra compiler step like this:

PROTO_FILE = sensor.proto
protoc.output = $${OUT_PWD}/${QMAKE_FILE_IN_BASE}.pb.cc
protoc.commands = $${PROTO_PATH}/bin/protoc -I=$$relative_path($${PWD}, $${OUT_PWD}) --cpp_out=. ${QMAKE_FILE_NAME}
protoc.variable_out = GENERATED_SOURCES
protoc.input = PROTO_FILE
QMAKE_EXTRA_COMPILERS += protoc

Then, in order to have a comparable benchmark in terms of object size, a generated structure is used as a member for the SensorInformationProto class that inherits QObject, as for the QDataStream and JSON example

class SensorInformationProto : public QObject
{
    Q_OBJECT
    Q_PROPERTY( double ambientTemperature READ ambientTemperature WRITE 
    setAmbientTemperature NOTIFY ambientTemperatureChanged)
[...]

public :
    SensorInformationProto( const std::string &pr);
[...]

    std::string serialize() const ;
[...]

private :
    serialtest::Sensor m_protoInfo;
};

The proto Info serialization function is generated by the protocol, so the step of creating the payload to be passed is as follows:

std::string SensorInformationProto::serialize() const
{
    std::string res;
    m_protoInfo.SerializeToString(&res);
    return res;
}

Note that compared to previous solutions, protobuf uses an STD string. This means you lose the capabilities of QString unless the string is stored as a byte array (manual conversion required). Again, this will slow down the whole process due to parsing.

From a performance standpoint, benchmark results look promising. A benchmark with 10,000 elements takes just 5ms with an average message size of 82 bytes.

As a conclusion, the following table shows the different approaches to serialization:

| | Payload size | Time (ms) |
| json (text) | 346 | 263 |
| json (binary) | 338 | 125 |
| QDataStream | 84 | 26 |
| Protobuf | 82 | 5 |

One promising alternative is CBOR, which is currently being implemented by Thiago Macieira for Qt 5.12. However, as the development process continues, it is still too early to include it in this article. From the discussions, the results look promising, with significant performance over JSON, and with all of its benefits.

The article showed different approaches to serializing data into MQTT message payloads. This can be done exclusively within Qt or with external solutions (such as protobuf). Integrating external solutions into Qt is easy.

It is worth noting that all the serializations that were done with the benchmark were done with a small amount of data in the message, and if the amount of data is larger in size, the results may differ, and different approaches may lead to better results.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
AD

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:50points,
  • Rating points-4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
m

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:20points,
  • Rating points-10
Last comments
ИМ
Игорь МаксимовNov. 23, 2024, 12:51 a.m.
Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii LegotckoiNov. 1, 2024, 2:37 a.m.
Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZEOct. 19, 2024, 8:19 p.m.
Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь МаксимовOct. 5, 2024, 7:51 p.m.
Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas5July 5, 2024, 11:02 p.m.
QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiJune 25, 2024, 3:11 a.m.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey1Nov. 15, 2024, 7:04 p.m.
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProjectJune 4, 2022, 3:49 p.m.
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9AnonimOct. 25, 2024, 9:10 p.m.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Follow us in social networks