Protobuf против JSON в мире Ruby
26 апреля 2023 г.В моем текущем проекте я работаю с protobuf не только для GRPC, но и как сообщение RabbitMQ формат. Хотя преимущества protobuf не ограничиваются его скоростью, мне было интересно, действительно ли он такой быстрый по сравнению с библиотеками JSON, особенно в рубиновый мир. Я решил сделать несколько тестов, чтобы проверить это, но сначала я хочу добавить краткое введение в каждый формат.
Что такое протобуф?
Это быстрая и компактная кроссплатформенная система обмена сообщениями, разработанная с учетом прямой и обратной совместимости. Он состоит из языка определения и компиляторов для конкретного языка.
Он отлично работает с небольшими объектными данными, имеет отличную обратную и прямую совместимость, быстр (мы еще не уверены) и более компактен, чем, например, JSON, но имеет некоторые ограничения, такие как отсутствие поддержки прямого сравнения( вам нужно десериализовать объекты для сравнения).
Он не сжат, и некоторые определенные форматы могут работать лучше для своих данных (например, JPEG). Это не самоописание.
Дополнительные сведения см. в официальной документации.
Что такое JSON
JSON – это сокращение от обозначения объектов JavaScript. Текстовый формат данных, который изначально использовался в JavaScript, но позже получил широкое распространение в качестве формата связи не только между приложениями JS и серверной частью, но даже между микросервисами и имеет множество других применений.
Он использует строки в качестве ключей и имеет строку, число, логическое значение, объект, массив и nul в качестве доступных типов для значения. Главное его преимущество в том, что он удобочитаем, а также довольно легко сериализуется и анализируется для языка программирования.
Дополнительные сведения см. на сайте.
Контрольные показатели
Я подобрал три популярные библиотеки Ruby JSON. Это Oj, Yajl и стандартная библиотека JSON. Для protobuf я использую стандартный google protoc с google ruby gem.
Я измерю различные конкретные типы полезной нагрузки, чтобы увидеть, какой тип данных покажет наибольшую разницу, если это сложная полезная нагрузка со смесью типов полей.
Вы можете увидеть весь код здесь https://github.com/alexstaro/proto-vs-json.< /p>
Настройка эталона
В качестве оборудования я использую ноутбук с AMD Ryzen 3 PRO 5450U и 16 ГБ оперативной памяти ddr4.
В качестве операционной системы я использую Ubuntu 22.10 kinetic.
Ruby версии 3.2.1 был установлен через asdf.
Для бенчмаркинга я использую гем тест/ips (https://github.com/evanphx/benchmark-ips) р>
Настройка выглядит так:
Benchmark.ips do |x|
x.config(time: 20, warmup: 5)
x.report('Yajl encoding') do
Yajl::Encoder.encode(data)
end
...
x.compare!
end
Только целые числа
Мы начнем только с целых чисел. Цифры для JSON довольно сложны, поэтому мы ожидаем, что protobuf будет далеко от других конкурентов.
Тестовые данные:
data = {
field1: 2312345434234,
field2: 31415926,
field3: 43161592,
field4: 23141596,
field5: 61415923,
field6: 323423434343443,
field7: 53141926,
field8: 13145926,
field9: 323423434343443,
field10: 43161592
}
Результаты сравнения:
protobuf encoding: 4146929.7 i/s
Oj encoding: 1885092.0 i/s - 2.20x slower
standard JSON encoding: 505697.5 i/s - 8.20x slower
Yajl encoding: 496121.7 i/s - 8.36x slower
Нет сомнений, что protobuf — абсолютный победитель, но что, если сделать тест более приближенным к реальному сценарию — мы почти всегда создаем прото-сообщения только для сериализации.
Что произойдет, если мы переместим инициализацию модели в тест?
Вот результаты:
protobuf encoding: 4146929.7 i/s
Oj encoding: 1885092.0 i/s - 2.20x slower
standard JSON encoding: 505697.5 i/s - 8.20x slower
Yajl encoding: 496121.7 i/s - 8.36x slower
protobuf with model init: 489658.0 i/s - 8.47x slower
Результат не так очевиден. Я ожидал, что кодирование с инициализацией сообщения будет медленнее, но не самым медленным.
Проверим десериализацию:
protobuf parsing: 737979.5 i/s
Oj parsing: 448833.9 i/s - 1.64x slower
standard JSON parsing: 297127.2 i/s - 2.48x slower
Yajl parsing: 184361.1 i/s - 4.00x slower
Здесь нет никаких сюрпризов.
С точки зрения размера полезной нагрузки protobuf почти в 4 раза компактнее по сравнению с json:
JSON payload bytesize 201
Protobuf payload bytesize 58
Только двойники
Ожидается, что двойники будут самой сложной полезной нагрузкой для JSON, давайте проверим это.
Наша полезная нагрузка:
data = {
field1: 2312.345434234,
field2: 31415.926,
field3: 4316.1592,
field4: 23141.596,
field5: 614159.23,
field6: 3234234.34343443,
field7: 53141.926,
field8: 13145.926,
field9: 323423.434343443,
field10: 43161.592
}
Результат:
protobuf encoding: 4814662.9 i/s
protobuf with model init: 444424.1 i/s - 10.83x slower
Oj encoding: 297152.0 i/s - 16.20x slower
Yajl encoding: 160251.9 i/s - 30.04x slower
standard JSON encoding: 158724.3 i/s - 30.33x slower
Protobuf намного быстрее даже при инициализации модели. Давайте проверим десериализацию:
Comparison:
protobuf parsing: 822226.6 i/s
Oj parsing: 395411.3 i/s - 2.08x slower
standard JSON parsing: 241438.7 i/s - 3.41x slower
Yajl parsing: 157235.7 i/s - 5.23x slower
По-прежнему никаких сюрпризов.
и размер полезной нагрузки:
JSON payload bytesize 211
Protobuf payload bytesize 90
Не в четыре раза, но все равно заметно.
Только строки
Ожидается, что строки будут проще для JSON, давайте проверим это.
полезная нагрузка:
data = {
field1: "2312.345434234",
field2: "31415.926",
field3: "4316.1592",
field4: "23141.596",
field5: "614159.23",
field6: "3234234.34343443",
field7: "53141.926",
field8: "13145.926",
field9: "323423.434343443",
field10: "43161.592"
}
Результаты стенда:
Comparison:
protobuf encoding: 3990298.3 i/s
oj encoder: 1848941.3 i/s - 2.16x slower
yajl encoder: 455222.0 i/s - 8.77x slower
standard JSON encoding: 444245.6 i/s - 8.98x slower
protobuf with model init: 368818.3 i/s - 10.82x slower
Десериализация:
Comparison:
protobuf parser: 631262.5 i/s
oj parser: 378697.6 i/s - 1.67x slower
standard JSON parser: 322923.5 i/s - 1.95x slower
yajl parser: 187593.4 i/s - 3.37x slower
Размер полезной нагрузки:
JSON payload bytesize 231
Protobuf payload bytesize 129
Целочисленный массив
Несмотря на то, что мы разделили скамейку целых чисел, интересно, как protobuf обрабатывает коллекции.
Вот данные:
data = {
field1: [
2312345434234, 31415926, 43161592, 23141596, 61415923, 323423434343443, 53141926, 13145926, 323423434343443, 43161592
]
}
Стенд сериализации:
Comparison:
protobuf encoding: 4639726.6 i/s
oj encoder: 2929662.1 i/s - 1.58x slower
standard JSON encoding: 699299.2 i/s - 6.63x slower
yajl encoder: 610215.5 i/s - 7.60x slower
protobuf with model init: 463057.9 i/s - 10.02x slower
Стенд десериализации:
Comparison:
oj parser: 1190763.1 i/s
protobuf parser: 760307.3 i/s - 1.57x slower
standard JSON parser: 619360.4 i/s - 1.92x slower
yajl parser: 414352.4 i/s - 2.87x slower
Честно говоря, результаты десериализации здесь довольно неожиданны.
Давайте проверим размер полезной нагрузки:
JSON payload bytesize 121
Protobuf payload bytesize 50
Массив двойников
Я решил проверить, имеет ли массив двойников одинаковое поведение.
данные:
data = {
field1: [
2312.345434234, 31415.926, 4316.1592, 23141.596, 614159.23, 3234234.34343443,
53141.926, 13145.926, 323423.434343443, 43161.592
]
}
Сериализация:
Comparison:
protobuf encoding: 7667558.9 i/s
protobuf with model init: 572563.4 i/s - 13.39x slower
Oj encoding: 323818.1 i/s - 23.68x slower
Yajl encoding: 183763.3 i/s - 41.73x slower
standard JSON encoding: 182332.3 i/s - 42.05x slower
Десериализация:
Comparison:
Oj parsing: 953384.6 i/s
protobuf parsing: 883899.0 i/s - 1.08x slower
standard JSON parsing: 452799.0 i/s - 2.11x slower
Yajl parsing: 356091.2 i/s - 2.68x slower
Мы получили похожие результаты. Кажется, у protobuf есть проблемы с массивами.
Размер полезной нагрузки:
JSON payload bytesize 131
Protobuf payload bytesize 82
Сложная полезная нагрузка
В качестве «сложной» полезной нагрузки я смоделировал некоторые пользовательские данные с сообщениями и комментариями к этим сообщениям, чтобы сделать его более похожим на реальное приложение.
data = {
user_id: 12345,
username: 'johndoe',
email: 'johndoe@example.com',
date_joined: '2023-04-01T12:30:00Z',
is_active: true,
profile: {
full_name: 'John Doe',
age: 30,
address: '123 Main St, Anytown, USA',
phone_number: '+1-555-123-4567'
},
posts: [
{
post_id: 1,
title: 'My first blog post',
content: 'This is the content of my first blog post.',
date_created: '2023-04-01T14:00:00Z',
likes: 10,
tags: ['blog', 'first_post', 'welcome'],
comments: [
{
comment_id: 101,
author: 'Jane',
content: 'Great first post!',
date_created: '2023-04-01T15:00:00Z',
likes: 3
},
...
]
},
...
]
}
Результаты:
Comparison:
protobuf encoding: 1038246.0 i/s
Oj encoding: 296018.6 i/s - 3.51x slower
Yajl encoding: 125909.6 i/s - 8.25x slower
protobuf with model init: 119673.2 i/s - 8.68x slower
standard JSON encoding: 115773.4 i/s - 8.97x slower
Comparison:
protobuf parsing: 291605.9 i/s
Oj parsing: 76994.7 i/s - 3.79x slower
standard JSON parsing: 64823.6 i/s - 4.50x slower
Yajl parsing: 34936.4 i/s - 8.35x slower
И размер полезной нагрузки:
JSON payload bytesize 1700
Protobuf payload bytesize 876
Мы видим здесь ожидаемое поведение в первую очередь с чистой кодировкой protobuf, однако, если мы посмотрим на наш «реальный» пример, мы увидим, что это не быстрее, чем стандартная кодировка JSON.
Заключение
Если вы переходите с JSON на Protobuf только из-за скорости, возможно, оно того не стоит.
Причиной использования Protobuf должно быть прекрасное определение схемы для обмена данными на разных языках, а не повышение производительности.
Основное изображение для этой статьи было создано с помощью генератора AI-изображений через подсказку "язык программирования".
Оригинал