paint-brush
Protobuf vs JSON en The Ruby Worldpor@alexstaro
848 lecturas
848 lecturas

Protobuf vs JSON en The Ruby World

por Oleksandr Starodubtsev10m2023/04/26
Read on Terminal Reader

Demasiado Largo; Para Leer

Protobuf es un sistema de mensajes multiplataforma rápido y compacto. Consiste en un lenguaje de definición y compiladores específicos del lenguaje. Tiene una gran compatibilidad hacia adelante y hacia atrás, es rápido (aún no estamos seguros) y es más compacto que JSON, por ejemplo. No está comprimido y algunos formatos específicos pueden funcionar mejor para sus datos.
featured image - Protobuf vs JSON en The Ruby World
Oleksandr Starodubtsev HackerNoon profile picture
0-item
1-item

En mi proyecto actual, trabajo con protobuf no solo para GRPC, sino también como formato de mensaje RabbitMQ . Si bien las ventajas de protobuf no se limitan a su velocidad, me preguntaba si realmente es tan rápido en comparación con las bibliotecas JSON , especialmente en el mundo ruby. Decidí hacer algunos puntos de referencia para comprobarlo, pero primero quiero añadir una breve introducción a cada formato.

¿Qué es protobuf?

Es un sistema de mensajes multiplataforma rápido y compacto, diseñado teniendo en cuenta la compatibilidad hacia adelante y hacia atrás. Consiste en un lenguaje de definición y compiladores específicos del lenguaje.

Funciona perfectamente para datos similares a objetos pequeños, tiene una gran compatibilidad hacia adelante y hacia atrás, es rápido (aún no estamos seguros) y es más compacto que JSON, por ejemplo, pero tiene algunas limitaciones como no admitir la comparación directa (debe deserializar objetos para comparar).


No está comprimido y algunos formatos específicos pueden funcionar mejor para sus datos (por ejemplo, JPEG). No es autodescriptivo.


Consulte los documentos oficiales para obtener más detalles.

¿Qué es JSON?

JSON es una abreviatura de notación de objetos de JavaScript . Un formato de datos basado en texto, que se usó originalmente en JavaScript, pero luego se difundió ampliamente como un formato de comunicación no solo entre las aplicaciones JS y el backend, sino incluso entre microservicios y tiene muchos otros usos.


Utiliza cadenas como claves y tiene una cadena, un número, un booleano, un objeto, una matriz y nul como tipos disponibles para el valor. La principal ventaja de esto es que es legible por humanos y bastante fácil de serializar y analizar para el lenguaje de programación.


Vea el sitio para más detalles.

Puntos de referencia

Recogí tres bibliotecas populares de Ruby JSON. Son Oj, Yajl y la biblioteca JSON estándar. Para protobuf, uso google protoc estándar con google ruby gem.


Mediré diferentes tipos específicos de carga útil para ver qué tipo de datos mostraremos la mayor diferencia, siempre que sea una carga útil compleja con una combinación de tipos de campo.


Puedes ver todo el código aquí https://github.com/alexstaro/proto-vs-json .

Configuración de referencia

Como hardware uso laptop con AMD Ryzen 3 PRO 5450U y 16gb de ram ddr4.

Como sistema operativo uso Ubuntu 22.10 kinetic.


Ruby versión 3.2.1 se instaló a través de asdf.


Para la evaluación comparativa, uso benchmark/ips gem ( https://github.com/evanphx/benchmark-ips )


La configuración se ve así:

 Benchmark.ips do |x| x.config(time: 20, warmup: 5) x.report('Yajl encoding') do Yajl::Encoder.encode(data) end ... x.compare! end

Solo números enteros

Comenzaremos solo con números enteros. Los números son bastante difíciles para JSON, por lo que esperamos que protobuf esté lejos de otros competidores.


Los datos de prueba:

 data = { field1: 2312345434234, field2: 31415926, field3: 43161592, field4: 23141596, field5: 61415923, field6: 323423434343443, field7: 53141926, field8: 13145926, field9: 323423434343443, field10: 43161592 }

Resultados de referencia:

 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


No hay duda de que protobuf es un ganador absoluto, pero ¿qué pasa si hacemos la prueba más cercana al escenario del mundo real? Casi siempre creamos protomensajes solo para serialización.


¿Qué pasaría si movemos la inicialización del modelo bajo el punto de referencia?


Aquí están los resultados:

 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


El resultado no es tan obvio. Esperaba que la codificación con la inicialización del mensaje fuera más lenta pero no la más lenta.


Verifiquemos la deserialización:

 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

No hay sorpresas aquí.


En términos de tamaño de carga útil, protobuf es casi 4 veces más compacto en comparación con json:

 JSON payload bytesize 201 Protobuf payload bytesize 58

Solo dobles

Se espera que los dobles sean las cargas útiles más difíciles para JSON, veamos esto.


Nuestra carga útil:


 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 }


Resultado:

 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 es mucho más rápido incluso con la inicialización del modelo. Comprobemos la deserialización:

 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

Todavía no hay sorpresas aquí.


y el tamaño de la carga útil:

 JSON payload bytesize 211 Protobuf payload bytesize 90


No cuatro veces, pero aún notable.

Solo cadenas

Se espera que las cadenas sean más fáciles para JSON, veamos esto.

carga útil:


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


Resultados de banco:

 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


Deserialización:

 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


El tamaño de la carga útil:

 JSON payload bytesize 231 Protobuf payload bytesize 129

Matriz de enteros

A pesar de que hemos separado el banco de enteros, es interesante cómo protobuf maneja las colecciones.

Aquí están los datos:


 data = { field1: [ 2312345434234, 31415926, 43161592, 23141596, 61415923, 323423434343443, 53141926, 13145926, 323423434343443, 43161592 ] }


Banco de serialización:

 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


Banco de deserialización:

 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


Para ser honesto, los resultados de la deserialización son bastante inesperados aquí.

Verifiquemos el tamaño de la carga útil:

 JSON payload bytesize 121 Protobuf payload bytesize 50

Matriz de dobles

Decidí comprobar si una matriz de dobles comparte el mismo comportamiento.

datos:

 data = { field1: [ 2312.345434234, 31415.926, 4316.1592, 23141.596, 614159.23, 3234234.34343443, 53141.926, 13145.926, 323423.434343443, 43161.592 ] }


Publicación por entregas:

 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


Deserialización:

 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

Tenemos resultados similares aquí. Parece que protobuf tiene algunos problemas con las matrices.


Tamaño de la carga útil:

 JSON payload bytesize 131 Protobuf payload bytesize 82

Carga útil compleja

Como una carga útil "compleja", me burlé de algunos datos de usuario con publicaciones y comentarios para esas publicaciones para que se parezca más a una aplicación de la vida real.

 data = { user_id: 12345, username: 'johndoe', email: '[email protected]', 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 }, ... ] }, ... ] }


Los resultados:

 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


Y tamaño de la carga útil:

 JSON payload bytesize 1700 Protobuf payload bytesize 876


Vemos aquí el comportamiento esperado con codificación protobuf pura en primer lugar, sin embargo, si observamos nuestro ejemplo del "mundo real", vemos que no es más rápido que la codificación JSON estándar.

Conclusión

Si está cambiando de JSON a Protobuf solo por la velocidad, es posible que no valga la pena.

La razón para usar Protobuf debería ser la increíble definición de esquema entre idiomas para el intercambio de datos, no un aumento del rendimiento.


La imagen principal de este artículo fue generada porAI Image Generator de HackerNoon a través del "lenguaje de programación" de solicitud.