© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB

Сериализация В и Для Qt

Сериализация, Qt, JSON

В первой части серии статей , автор рассмотрел проблему настройки и объединения сообщений и уменьшения их нагрузки в рамках телеметрических датчиков.

Эта часть посвящена полезной нагрузке от сообщений и её оптимизации.

Существует множество методов сериализации объекта с помощью Qt . В первой части серии статей мы использовали JSON . Для этого вся информация о датчике хранится в QJsonObject , а QJsonDocument заботится о потоке значений в 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 имеет несколько преимуществ:

  • текстовый JSON является описательным, что делает его читаемым для людей;
  • и нформация структурирована ;
  • обмен основной информации прост и понятен;
  • JSON позволяет расширять сообщения дополнительными значениями;
  • существует множество решений для восприятия и анализа JSON в облачных решениях.

Однако этот подход имеет некоторые ограничения. Во-первых, создание сообщения JSON может быть тяжелой операцией, занимающей много циклов. Бенчмарк второй части тестирования показывает что сериализация и десериализация 10.000 сообщений занимает около 263 мс. Это может быть не похоже на значительное число сообщений, но в этом контексте время соответствует энергии. Это может существенно воздействовать на датчик который предназначен для его использования без зарядки длительное время.

Другой аспект состоит в том, что полезная нагрузка для сообщения MQTT на обновление датчика составляет 346 байт. Учитывая, что датчик отправляет только восемь дублей и одну ограниченную строку, это может быть потенциально большими затраты вычислительных ресурсов.

внутри комментариев предыдущего поста рекомендовано использовать QJsonDocument : Compact (компакт), который уменьшает размер полезной нагрузки, в среднем до 290 байт

Как это можно улучшить?

Как большинство из вас знает, существует также двоичный JSON, который может уменьшить читаемость, но все другие аспекты по-прежнему актуальны. Главное в проведении бенчмарков  то,  что простой переключатель doc.toJson  в doc.toBinaryData удвоит скорость теста, сократив итерацию бенчмарка до 125 миллисекунд.

Проверив нагрузку , размер сообщений составляет 338 байт, разница почти незаметна. Однако это может измениться в различных сценариях, например, если  добавить больше строк внутри сообщения.

В зависимости от требований возможно добавлять сторонние проекты и  другие возможности.

В случае если проект находится !в рамках мира Qt", весь поток данных определён и не изменяется QDataStream , является возможным вариантом .

Добавляем поддержку для  этого в класс SensorInformation, что требует добавления  двух операторов.

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

Реализация довольно проста, ниже показан пример сериализации:

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;
}

При использовании QDataStream время бенчмарка составило 26 миллисекунд, что почти в 10 раз быстрее чем текстовый JSON. Кроме того, средний размер сообщения составляет всего 84 байта по сравнению с 290. Следовательно, если вышеуказанные ограничения приемлемы, QDataStream, является хорошим вариантом решения поставленной задачи.

Если проект позволяет добавлять дополнительные сторонние компоненты, одним из наиболее известных решений сериализации является протокол буферов Google (protobuf).

Чтобы добавить protobuf в наше решение, нужно сделать пару изменений. Во-первых, protobuf использует IDL для описания структур данных или сообщений. Тогда, разработка сенсора информации:

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;
}

Чтобы добавить генератор кода protobuf (протокол) в проект qmake, необходимо добавить дополнительный шаг компилятора, подобный этому:

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

Затем, чтобы иметь сопоставимый бенчмарк с точки зрения размера объекта, сгенерированна структура используемая в качестве члена для класса SensorInformationProto, который наследует QObject, как и для примера QDataStream и JSON

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;
};

Функция сериализации proto Info генерируется протоколом, поэтому шаг создания передаваемой полезной нагрузки выглядит следующим образом:

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

Обратите внимание, что по сравнению с предыдущими решениями protobuf использует STD строку. Это означает, что вы теряете возможности QString, если строка не хранится в виде массива байтов (требуется ручное преобразование). Опять же, это замедлит весь процесс из-за разбора.

С точки зрения эффективности результаты контрольных показателей выглядят многообещающими. Бенчмарк с 10.000 элементов занимает всего 5 мс со средним размером сообщения 82 байта.

Как вывод, следующая таблица показывает различные подходы к сериализации:

Размер полезной нагрузки

Время (мс)

JSON (текстовый)

346

263

JSON (двоичный)

338

125

QDataStream

84

26

Protobuf

82

5

Одной из перспективных альтернатив является CBOR, который в настоящее время реализуется Thiago Macieira для Qt 5.12. Однако, поскольку процесс разработки продолжается, еще слишком рано его включать его в эту статью. Из обсуждений результаты выглядят многообещающими,  со значительным производительностью по сравнению с JSON,  и со всеми его преимуществами.

В статье были показаны различные подходы к сериализации данных в полезные данные сообщений MQTT. Это может быть сделано исключительно внутри Qt или с помощью внешних решений (например, protobuf). Интеграция внешних решений в Qt проста.

Стоить отметить, что все варианты сериализации которые были проведены с помощью бенчмарка , проводились с малым количеством данных в сообщении, и если число данных будет больше по размеру, результаты могут отличаться, и различные подходы могут привести к лучшим результатам.

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
20 августа 2018 г. 9:14
nayk1982

C++ - Тест 001. Первая программа и типы данных

  • Результат 86баллов,
  • Очки рейтинга6
20 августа 2018 г. 9:07
nayk1982

Qt - Тест 001. Сигналы и слоты

  • Результат 84баллов,
  • Очки рейтинга4
19 августа 2018 г. 10:43
Виктор Попов

Qt - Тест 001. Сигналы и слоты

  • Результат 78баллов,
  • Очки рейтинга2
Последние комментарии
20 августа 2018 г. 17:02
Евгений Ереметько

Qt/C++ - Урок 027. Полиморфизм в Qt на примере геометрических фигур в QGraphicsScene

Добрый день, начал только изучать Qt C++. Никак не могу понять, как удалять последний созданный элемент. Заранее спасибо.
17 августа 2018 г. 15:47
Евгений_Канусовский@1981

PyQt5 - Урок 003. QSystemTrayIcon - Как свернуть приложение в трей

Решение проблемы нашел в интернете)) Лечится так:File - Settings - Project:{name_my_project} - Project Interpreter - устанавливаем нужную нам версию интерпретатора(python 3.6.2 например) -...
16 августа 2018 г. 17:20
Евгений_Канусовский@1981

PyQt5 - Урок 003. QSystemTrayIcon - Как свернуть приложение в трей

Добрый вечер Евгений и форумчане! Не подскажите почему в при запуске данного кода в PyCharm выдаётся сообщение: "ModuleNotFoundError: No module named 'PyQt5'"?
10 августа 2018 г. 13:40
Alex

Работа с триггерными функциями в PostgreSQL

Приветствую! Если вы создаете новую таблицу, почему бы просто не сделать вьюху ? Просто от одного названия "триггер" как-то не хочется его использовать, а уж кода сколько писа...
Сейчас обсуждают на форуме
20 августа 2018 г. 13:18
LittleTux

Странное поведение сингелтона, а может быть, и не в нем проблема...

Лучше возвращать ссылку на экземпляр класса:PaletteUtils& PaletteUtils::instance(){ static PaletteUtils _instance; return _instance;}и если уж делать singleton, то хорошо было ...
20 августа 2018 г. 6:45
LittleTux

Как правильно сбросить позицию touchscreen в 0, как это делается с курсором QCursor::setPos(0,0)?

Ранее не сталкивался с разработкой под устройства с touchscreen, но вот наступило такое время... и возникла проблема: есть у нас mainWidget, на нем лежит stackwidget, в котором есть пару видже...
19 августа 2018 г. 12:38
Alex

Подключение карты через плагин OSM. С localhost Qt/QML

наткнулся на возможное решение, конкретно для geoserver'a http://localhost/gwc/tms/1.0.0/gis:service@EPSG%3A900913@png/{z}/{x}/{-y}.png
17 августа 2018 г. 20:35
Чарльз Грин

Как вывести видео на 2 QVideoWidget?

Есть прога, в ней qvideowidget предпросмотр, а нужно, чтоб с этого же плеера видео выводилось и на второй монитор одновременно и управлялось одними эл. управления. Подскажите пожалуйста как эт...
17 августа 2018 г. 8:52
nayk1982

Помогите разобраться с версиями библиотек, компиляторов короче запутался с этим Qt

https://www.qt.io/download-qt-installer   - Качайте Online инсталлятор под нужную ОС и устанавливайте через него нужные версии библиотек.

Рекомендуемые страницы