Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)

698 views

Published on

В эпоху распределённых архитектур и микросервисов как никогда актуальными становятся вопросы — как эффективно сериализовать и передать данные. Большинство решает данный вопрос просто — используют стандартный, универсальный и всем понятный формат JSON. Другие же, ориентируясь на производительность, ищут в интернете бенчмарки и выбирают protobuf или msgpack.

Мы протестировали разные реализации статически (thrift, protocol buffers) и динамически (json, msgpack) типизированных протоколов для python; сравнили их производительность в разных сценариях, возможности, внутреннее устройство, удобство разработки.

Я расскажу о результатах нашего исследования, особенностях "приготовления" библиотек и выявленных подводных камнях.

Published in: Engineering
  • Be the first to comment

Сравнение форматов и библиотек сериализации / Антон Рыжов (Qrator Labs)

  1. 1. Сравнение форматов и библиотек сериализации Антон Рыжов, Qrator Labs
  2. 2. Задача Сериализовать данные: ● Без потерь ● Компактно ● Быстро ● Удобно
  3. 3. JSON ✔ Привычно ✔ Удобно ✔ Человекочитаемо [   {     "very": "simple"   } ]
  4. 4. JSON ✔ Привычно ✔ Удобно ✔ Человекочитаемо ✗ Не компактно [   {     "very": "simple"   },   {     "very": "big"   } ]
  5. 5. JSON — компактность 7              1 byte (1:1)→
  6. 6. JSON — компактность 7              1 byte (1:1)→ 65535          5 bytes (5:2)→ 1000 000 000   10 bytes (10:4)→
  7. 7. JSON — компактность 3 / 2   1.5 (3 byte)→
  8. 8. JSON — компактность 3 / 2   1.5 (3 byte)→ 1 / 3   0.3333333333333333 (18 bytes)→
  9. 9. JSON — компактность Hello   "Hello" (+2 bytes)→
  10. 10. JSON — компактность Hello   "Hello" (+2 bytes)→      "u0420u0418u0422" (×3)РИТ →
  11. 11. JSON — компактность Hello   "Hello" (+2 bytes)→      "u0420u0418u0422" (×3)РИТ →      " " ( )РИТ → РИТ опция
  12. 12. JSON — компактность [   {"userId": 1, "userName": "John Smith"},   {"userId": 2, "userName": "Jane Dow"} ]
  13. 13. JSON — компактность [   {"userId": 1, "userName": "John Smith"},   {"userId": 2, "userName": "Jane Dow"} ]
  14. 14. JSON — компактность [   "keys": ["userId", "userName"],   "values": [     [1, "John Smith"],     [2, "Jane Dow"]   ] ]
  15. 15. JSON — целостность dict(1: "foo")   { "1": "foo" }→
  16. 16. JSON — целостность dict(1: "foo")   { "1": "foo" }→ object = { "type_id": 5 } types = { "5": "Type name" }
  17. 17. JSON — целостность Python   JSON:→ { 1: "foo", "1": "bar" }   ?→ PHP   JSON:→ Array(1=> "foo", "1"=> "bar")   ?→
  18. 18. JSON — целостность Python   JSON:→ { 1: "foo", "1": "bar" }   {"1": "bar", "1": "foo"}→ PHP   JSON:→ Array(1=> "foo", "1"=> "bar")   {"1": "bar"}→
  19. 19. JSON — целостность Python   JSON:→ { 1: "foo", "1": "bar" }   {"1": "bar", "1": "foo"}→ PHP   → PHP: Array(1=> "foo", "1"=> "bar")   → Array(1=> "bar")
  20. 20. JSON — binary {"message": "0KDQmNCiKys="}  +30%
  21. 21. JSON — binary {"message": "0KDQmNCiKys="}  +30% {"message_bin": "0KDQmNCiKys="} {"message": { "type": "bin", "value": "0KDQmNCiKys="}}
  22. 22. Альтернативы Схема данных Binary Ключи map BSON ✗ ✓ str Msgpack ✗ ✓ any Google Protobuf ✓ ✓ ✗ Apache Thrift ✓ ✓ any Apache Avro ✓ ✓ str
  23. 23. Зачем схема? ✔Описывает данные
  24. 24. Зачем схема? ✔Описывает данные ✔Уменьшает дублирование
  25. 25. Зачем схема? ✔Описывает данные ✔Уменьшает дублирование ✔Валидирует данные
  26. 26. Зачем схема? ✔Описывает данные ✔Уменьшает дублирование ✔Валидирует данные ✔Приводит типы
  27. 27. Зачем схема? ✔Описывает данные ✔Уменьшает дублирование ✔Валидирует данные ✔Приводит типы ✗ Надо писать
  28. 28. Зачем схема? ✔Описывает данные ✔Уменьшает дублирование ✔Валидирует данные ✔Приводит типы ✗ Надо писать ✗ … а иногда ещё и компилировать
  29. 29. Protobuf ✔ Старое проверенное решение ✔ Разработан Google ✔ Хорошие результаты в тестах в интернете ✔ Хорошая документация
  30. 30. Protobuf ✔ Старое проверенное решение ✔ Разработан Google ✔ Хорошие результаты в тестах в интернете ✔ Хорошая документация ✗ Не всё можно описать – Список списков, map
  31. 31. Protobuf — схема message Point {   required int32 x = 1;   required int32 y = 2; } message PointsList {   repeated Point points = 1; }
  32. 32. Thrift ● Более новый формат ● Разработал Facebook, отдал в Apache ● Документация — справочник типов и BNF ● Есть “Thrift: The Missing Guide” ● RPC-клиент/сервер
  33. 33. Thrift — схема struct PointsList {     1: required list< list<i32> > points, }
  34. 34. Apache Avro ● Ещё более новый формат ● Разработан для Apache Hadoop ● Документация на схему — подробная ● Кодогенерации нет
  35. 35. Avro — схема {     "namespace": "test.avro",     "type": "array",     "items": {         "type": "array",         "items": "int"     } }
  36. 36. Размер integer: «0»; 1-, 2-, 4-byte Msgpack Avro Protobuf JSON Thrift BSON* 0 2 4 6 8 10 12
  37. 37. Размер float: «0», 2.5, 1/3, 1e9/3 Avro Protobuf Msgpack Thrift BSON* JSON 0 2 4 6 8 10 12 14 16 18 20
  38. 38. Размер string: "", "Z"×10, "Я"×10 Msgpack Avro Protobuf JSON Thrift BSON* 0 5 10 15 20 25 30 35
  39. 39. Размер string: "Z"×1000, "Я"×1000 Msgpack Avro Protobuf Thrift BSON* JSON 0 500 1000 1500 2000 2500
  40. 40. Размер 0, 10 bytes Avro Msgpack Protobuf Thrift JSON BSON* 0 5 10 15 20 25
  41. 41. Размер 1000 bytes Avro Msgpack Protobuf Thrift BSON* JSON 0 200 400 600 800 1000 1200 1400 1600
  42. 42. Размер array [0], [10] Avro Msgpack Protobuf JSON Thrift BSON* 0 5 10 15 20 25
  43. 43. Размер map [0], [10] Msgpack Avro Protobuf* JSON BSON Thrift 0 2 4 6 8 10 12 14 16 18 20
  44. 44. Размер struct vs map Msgpack Avro Protobuf* BSON JSON Thrift 0 20 40 60 80 100 120 140 160 180 200
  45. 45. Топ по размеру 1.Apache Avro 2.Msgpack 3.Google Protobuf 4.Apache Thrift 5.JSON 6.BSON
  46. 46. Методика бенчмарка Характерные объекты — строки, байты, словари разных размеров, массивы Замер времени на сериализацию и десериализацию каждой библиотекой в python2 и python3 Сравнение данных до и после Железо: Intel(R) Xeon(R) CPU E5-2683 v4 @ 2.10GHz 64 Gb RAM
  47. 47. Без схемы Поддержка Установка Реализация Генерация кода uJSON 2, 3 pip C-ext ✗ BSON 2, 3 pip Python ✗ Msgpack 2, 3 pip C-ext ✗
  48. 48. Без схемы — затраченное время msgpack ujson json bson encode py2.7 decode py2.7 encode py3.5 decode py3.5
  49. 49. Protobuf Поддержка Установка Реализация Генерация кода protobuf 2, 2to3 make + pip Python ✓ cprotobuf 3 protobuf + pip C-ext ✓ protobuf3 3 protobuf + pip Python ✓
  50. 50. Protobuf — пример кода protobuf, cprotobuf data = test_pb2.PointsList(     points=[         test_pb2.Point(x=1, y=2),         test_pb2.Point(x=3, y=4),     ] ).SerializeToString() pl = test_pb2.PointsList() pl.ParseFromString(data) print(pl.points[0].x)
  51. 51. Protobuf — пример для protobuf3 pl = test_pb2.PointsList() po = test_pb2.Point() po.x = 1; po.y = 2 pl.points.append(po) data = pl.encode_to_bytes() pl = test_pb2.PointsList() pl.parse_from_bytes(data) print(pl.points[0].x)
  52. 52. Protobuf — затраченное время cprotobuf* google_proto protobuf3* encode py2.7 decode py2.7 encode py3.5 decode py3.5
  53. 53. Thrift Поддержка Установка Реализация Генерация кода (apache) thrift 2 make + pip Python ✓ (facebook) Thrift 2, 3 ☠nightmare☠ Python ✓ thriftpy 2, 3 pip Cython ✗ thriftrw 2, 3 pip Cython ✗
  54. 54. (Apache) thrift — пример кода data = serialize(     test_ttypes.PointsList([(1, 2)]) ) points = deserialize(     test_ttypes.PointsList(),     points_list, ).points print(points[0][0])
  55. 55. (facebook)Thrift, thriftpy, thriftrw Совместим с (Apache) thrift по API
  56. 56. Thrift — затраченное время thriftrw fb_thrift** apache_thrift* thriftpy+cython* thriftpy* Py2.7 Py3.5
  57. 57. Thrift — затраченное время thriftrw fb_thrift** apache_thrift* thriftpy+cython* thriftpy* encode py2.7 decode py2.7 encode py3.5 decode py3.5
  58. 58. Avro Поддержка Установка Реализация Генерация кода (apache) avro 2, 3 2 pip 3 src Python ✗ fastavro 2, 3 pip Cython ✗ pyavroc 2, 3 shell + make libavro + wrapper ✗
  59. 59. (Apache) avro — пример упаковки schema = avro.schema.parse(open('points_list.avsc',  'r').read()) io_stream = io.BytesIO() avro.io.DatumWriter(schema).write(     [(1, 2), (3, 4)],     avro.io.BinaryEncoder(io_stream), ) data = io_stream.getvalue()
  60. 60. (Apache) avro — пример распаковки schema = avro.schema.parse(open('points_list.avsc',  'r').read()) io_stream = io.BytesIO(data) points = avro.io.DatumReader(schema).read(     avro.io.BinaryDecoder(io_stream), ) print(points[0][0])
  61. 61. fastavro — пример упаковки schema = json.loads(open('points_list.avsc', 'r').read()) io_stream = io.BytesIO() fastavro.schemaless_writer(     io_stream,     schema,     [(1, 2), (3, 4)], ) return io_stream.getvalue()
  62. 62. fastavro — пример распаковки schema = json.loads(open('points_list.avsc', 'r').read()) io_stream = io.BytesIO(points_list) points = fastavro.schemaless_reader(     io_stream,     schema, ) print(points[0][0])
  63. 63. pyavroc — пример кода schema = open('specs/points_list.avsc', 'r').read() points_writer = _pyavroc.AvroSerializer(schema) data = points_writer.serialize(     [(1, 2), (3, 4)], ) points_reader = _pyavroc.AvroDeserializer(schema) points = points_reader.deserialize(data) print(points[0][0])
  64. 64. Avro — затраченное время pyavroc* fastavro avro encode py2.7 decode py2.7 encode py3.5 decode py3.5
  65. 65. Топ — затраченное время pyavroc* msgpack cprotobuf* thriftrw ujson fastavro thriftpy+cython* Py2.7 Py3.5
  66. 66. Общие рекомендации ● Следить за новыми разработками ● Рассматривать альтернативы ● Референсная библиотека — не всегда самая лучшая ● Альтернативные — бывают ещё хуже ● Не верить бенчмаркам в интернете (В том числе этому) ● Кодогенерация только мешает
  67. 67. Всё ar@qrator.net https://github.com/QratorLabs/ritfest2016

×