I
Ilya_M06 червня 2018 р. 16:07

Серіалізація В та Для Qt

У першій части серії статей , автор розглянув проблему налаштування та об'єднання повідомлень та зменшення їх навантаження у рамках телеметричних датчиків

Ця частина присвячена корисному навантаженню від повідомлень та її оптимізації.


Існує безліч методів серіалізації об'єкта за допомогою 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 |
| Протобуф | 82 | 5 |

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

У статті були показані різні підходи до серіалізації даних до корисних даних повідомлень MQTT. Це може бути зроблено виключно всередині Qt або за допомогою зовнішніх рішень (наприклад, protobuf). Інтеграція зовнішніх рішень у Qt проста.

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

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

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

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах