Pregunta

Para el almacenamiento en caché que estoy pensando hacer para un próximo proyecto, he estado pensando en la serialización de Java. A saber, ¿debería usarse?

Ahora he escrito previamente serialización y deserialización personalizadas (externalizables) por varias razones en los últimos años. En estos días, la interoperabilidad se ha convertido en un problema aún mayor y puedo prever la necesidad de interactuar con las aplicaciones .Net, así que he pensado en usar una solución independiente de la plataforma.

¿Alguien ha tenido alguna experiencia con el uso de GPB de alto rendimiento? ¿Cómo se compara en términos de velocidad y eficiencia con la serialización nativa de Java? Alternativamente, ¿hay otros esquemas que valga la pena considerar?

¿Fue útil?

Solución

No he comparado Protocol Buffers con la serialización nativa de Java en términos de velocidad, pero para la interoperabilidad, la serialización nativa de Java es un serio no-no. Tampoco será tan eficiente en términos de espacio como Protocol Buffers en la mayoría de los casos. Por supuesto, es algo más flexible en términos de lo que puede almacenar, y en términos de referencias, etc. Protocol Buffers es muy bueno para lo que está destinado, y cuando se ajusta a sus necesidades es genial, pero existen restricciones obvias debido a la interoperabilidad (y otras cosas).

Recientemente publiqué un marco de referencia de Protocol Buffers en Java y .NET. La versión de Java se encuentra en el proyecto principal de Google (en el directorio de benchmarks ), la versión .NET está en mi proyecto de puerto C # . Si desea comparar la velocidad de PB con la velocidad de serialización de Java, puede escribir clases similares y compararlas. Sin embargo, si está interesado en la interoperabilidad, realmente no consideraría la serialización nativa de Java (o la serialización binaria nativa de .NET) un segundo pensamiento.

Sin embargo, hay otras opciones para la serialización interoperable, además de Protocol Buffers: Thrift , JSON y YAML Recuerda, y sin duda hay otros.

EDITAR: De acuerdo, con la interoperabilidad no es tan importante, vale la pena intentar enumerar las diferentes cualidades que desea de un marco de serialización. Una cosa en la que debe pensar es en el control de versiones: esta es otra cosa que PB está diseñada para manejar bien, tanto hacia atrás como hacia adelante (para que el nuevo software pueda leer datos antiguos y viceversa), cuando se apega a las reglas sugeridas, por supuesto :)

Después de haber tratado de ser cauteloso sobre el rendimiento de Java en comparación con la serialización nativa, realmente no me sorprendería encontrar que PB era más rápido de todos modos. Si tiene la oportunidad, use el servidor vm: mis puntos de referencia recientes mostraron que la VM del servidor es más del doble de rápida en la serialización y deserialización de los datos de muestra. Creo que el código PB se adapta muy bien al JIT de VM del servidor :)

Al igual que las cifras de rendimiento de muestra, serializando y deserializando dos mensajes (uno de 228 bytes, uno de 84750 bytes) obtuve estos resultados en mi computadora portátil usando la VM del servidor:

Benchmarking benchmarks.GoogleSize$SizeMessage1 with file google_message1.dat 
Serialize to byte string: 2581851 iterations in 30.16s; 18.613789MB/s 
Serialize to byte array: 2583547 iterations in 29.842s; 18.824497MB/s 
Serialize to memory stream: 2210320 iterations in 30.125s; 15.953759MB/s 
Deserialize from byte string: 3356517 iterations in 30.088s; 24.256632MB/s 
Deserialize from byte array: 3356517 iterations in 29.958s; 24.361889MB/s 
Deserialize from memory stream: 2618821 iterations in 29.821s; 19.094952MB/s 

Benchmarking benchmarks.GoogleSpeed$SpeedMessage1 with file google_message1.dat 
Serialize to byte string: 17068518 iterations in 29.978s; 123.802124MB/s 
Serialize to byte array: 17520066 iterations in 30.043s; 126.802376MB/s 
Serialize to memory stream: 7736665 iterations in 30.076s; 55.93307MB/s 
Deserialize from byte string: 16123669 iterations in 30.073s; 116.57947MB/s 
Deserialize from byte array: 16082453 iterations in 30.109s; 116.14243MB/s
Deserialize from memory stream: 7496968 iterations in 30.03s; 54.283176MB/s 

Benchmarking benchmarks.GoogleSize$SizeMessage2 with file google_message2.dat 
Serialize to byte string: 6266 iterations in 30.034s; 16.826494MB/s 
Serialize to byte array: 6246 iterations in 30.027s; 16.776697MB/s 
Serialize to memory stream: 6042 iterations in 29.916s; 16.288969MB/s 
Deserialize from byte string: 4675 iterations in 29.819s; 12.644595MB/s 
Deserialize from byte array: 4694 iterations in 30.093s; 12.580387MB/s 
Deserialize from memory stream: 4544 iterations in 29.579s; 12.389998MB/s 

Benchmarking benchmarks.GoogleSpeed$SpeedMessage2 with file google_message2.dat 
Serialize to byte string: 39562 iterations in 30.055s; 106.16416MB/s 
Serialize to byte array: 39715 iterations in 30.178s; 106.14035MB/s 
Serialize to memory stream: 34161 iterations in 30.032s; 91.74085MB/s 
Deserialize from byte string: 36934 iterations in 29.794s; 99.98019MB/s 
Deserialize from byte array: 37191 iterations in 29.915s; 100.26867MB/s 
Deserialize from memory stream: 36237 iterations in 29.846s; 97.92251MB/s 

La " velocidad " vs " tamaño " es si el código generado está optimizado para la velocidad o el tamaño del código. (Los datos serializados son los mismos en ambos casos. La versión de "tamaño" se proporciona para el caso en el que tiene muchos mensajes definidos y no desea tomar mucha memoria para el código).

Como puede ver, para el mensaje más pequeño puede ser muy rápido: más de 500 mensajes pequeños serializados o deserializados por milisegundo . Incluso con el mensaje 87K, tarda menos de un milisegundo por mensaje.

Otros consejos

Un punto de datos más: este proyecto:

http://code.google.com/p/thrift-protobuf-compare /

da una idea del rendimiento esperado para objetos pequeños, incluida la serialización de Java en PB.

Los resultados varían mucho según su plataforma, pero hay algunas tendencias generales.

También puede echar un vistazo a FST , un reemplazo directo para la versión integrada en la serialización JDK que debería ser más rápida y tener una salida más pequeña.

estimaciones en bruto sobre la evaluación comparativa frecuente que he hecho en los últimos años:

100% = enfoques basados ??en binario / estructura (por ejemplo, SBE, fst-structs)

  • inconveniente
  • el posprocesamiento (acumulación de objetos reales en el lado del receptor) puede consumir ventajas de rendimiento y nunca se incluye en los puntos de referencia

~ 10% -35% protobuf & amp; derivados

~ 10% -30% de serializadores rápidos como FST y KRYO

  • conveniente, los objetos deserializados se pueden usar con mayor frecuencia directamente sin código de traducción manual adicional.
  • se puede mejorar el rendimiento (anotaciones, registro de clase)
  • preservar enlaces en el gráfico de objetos (ningún objeto serializado dos veces)
  • puede manejar estructuras cíclicas
  • solución genérica, FST es totalmente compatible con la serialización JDK

~ 2% -15% de serialización JDK

~ 1% -15% JSon rápido (por ejemplo, Jackson)

  • no puede manejar ningún gráfico de objeto, sino solo un pequeño subconjunto de estructuras de datos de Java
  • no hay restauración de referencia

0.001-1% gráfico completo JSon / XML (por ejemplo, JSON.io)

Estos números están destinados a dar una impresión muy aproximada de orden de magnitud. Tenga en cuenta que el rendimiento depende MUCHO de las estructuras de datos que se serializan / comparan. Por lo tanto, los puntos de referencia de clase simple son en su mayoría inútiles (pero populares: por ejemplo, ignorar Unicode, sin colecciones, ...).

ver también

http://java-is-the-new-c.blogspot.de/2014/12/a-persistent-keyvalue-server-in-40.html

http: //java-is-the-new-c.blogspot.de/2013/10/still-using-externalizable-to-get.html

Si está confundiendo entre PB y amp; serialización nativa de Java en velocidad y eficiencia, solo opta por PB.

  • PB fue diseñado para lograr tales factores. Consulte http://code.google.com/apis/protocolbuffers/docs/overview. html
  • Los datos de PB son muy pequeños, mientras que la serialización de Java tiende a replicar un objeto completo, incluida su firma. ¿Por qué siempre obtengo mi nombre de clase, nombre de campo ... serializado, a pesar de que lo sé del revés en el receptor?
  • Piensa en el desarrollo del lenguaje. Se vuelve difícil si un lado usa Java, un lado usa C ++ ...

Algunos desarrolladores sugieren Thrift, pero usaría Google PB porque " creo en google " :-) .. De todos modos, vale la pena echarle un vistazo: http://stuartsierra.com/2008/07/10/thrift-vs -protocol-buffers

¿Qué quiere decir con alto rendimiento? Si desea una serialización de milisegundos, le sugiero que utilice el enfoque de serialización que es más simple. Si desea sub milisegundos, es probable que necesite un formato binario. Si desea mucho menos de 10 microsegundos, es probable que necesite una serialización personalizada.

No he visto muchos puntos de referencia para la serialización / deserialización, pero pocos admiten menos de 200 microsegundos para la serialización / deserialización.

Los formatos independientes de la plataforma tienen un costo (en esfuerzo de su parte y latencia), es posible que deba decidir si desea rendimiento o independencia de la plataforma. Sin embargo, no hay ninguna razón por la que no pueda tener ambas como una opción de configuración entre las que cambiar según sea necesario.

Aquí está la sugerencia del día :-) (acabas de modificar algo en mi cabeza que ahora quiero probar) ...

Si puede obtener la solución de almacenamiento en caché completa a través de esto, podría funcionar: Project Darkstar . Está diseñado como un servidor de juegos de muy alto rendimiento, específicamente para que las lecturas sean rápidas (tan buenas para un caché). Tiene API de Java y C, así que creo (pensé que había pasado mucho tiempo desde que lo vi, y no estaba pensando en esto entonces) que podría guardar objetos con Java y leerlos de nuevo en C y viceversa.

Si nada más le dará algo para leer hoy :-)

Para una serialización amigable con los cables, considere usar la interfaz Externalizable. Utilizado inteligentemente, tendrá un conocimiento íntimo para decidir cómo organizar y desarmar de manera óptima campos específicos. Dicho esto, tendrá que administrar el control de versiones de cada objeto correctamente: es fácil de desmontar, pero volver a ordenar un objeto V2 cuando su código admite V1 se romperá, perderá información o, peor aún, dañará los datos de una manera que sus aplicaciones no pueden procesar correctamente. Si está buscando una ruta óptima, tenga cuidado porque ninguna biblioteca resolverá su problema sin algunos compromisos. En general, las bibliotecas se adaptarán a la mayoría de los casos de uso y vendrán con el beneficio adicional de que se adaptarán y mejorarán con el tiempo sin su aporte, si ha optado por un proyecto de código abierto activo. ¡Y podrían agregar problemas de rendimiento, introducir errores e incluso corregir errores que aún no lo han afectado!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top