La serialización simple, sin complicaciones y cero repeticiones en Scala/Java es similar a la del Pickle de Python.
serialization (5)
¿Hay un enfoque sencillo y sin complicaciones para la serialización en Scala / Java que sea similar al encuadre de Python? Pickle es una solución sencilla que es razonablemente eficiente en el espacio y el tiempo (es decir, no abismal) pero que no se preocupa por la accesibilidad a través del idioma, el control de versiones, etc. y permite la personalización opcional.
De lo que soy consciente:
- La serialización incorporada de Java es infamemente lenta ( [1] , [2] ), inflada y frágil. También tienen que marcar las clases como Serializable --- molesto cuando hay cosas que son claramente serializables pero que no tienen esa anotación (por ejemplo, no muchos autores de Point2D marcan estos Serializable).
- Scala''s BytePickle requiere un montón de repetitivo para cada tipo que desee encurtido, y aun así no funciona con gráficos de objetos (cíclicos) .
- jserial : no se ha mantenido y no parece ser mucho más rápido / más pequeño que la serialización Java predeterminada.
- kryo : No se puede (des) serializar objetos sin 0-arg ctors , que es una limitación severa. (También debe registrar todas las clases que planea serializar, o si no obtiene disminuciones / hinchazones significativas , pero aún así es más rápido que el pickle).
- protostuff : AFAICT, tienes que registrar todas las clases que pretendes serializar de antemano en un "esquema".
Kryo y protostuff son las soluciones más cercanas que he encontrado, pero me pregunto si hay algo más por ahí (o si hay alguna forma de usar estos que debería tener en cuenta). Por favor incluya ejemplos de uso! Lo ideal es también incluir puntos de referencia.
De hecho, creo que sería mejor con kryo (no estoy al tanto de las alternativas que ofrecen menos definición de esquema que los protocolos no binarios). Mencionas que pickle no es susceptible a las ralentizaciones e hinchazones que kryo obtiene sin registrar clases, pero kryo es aún más rápido y menos hinchado que pickle incluso sin registrar clases. Vea el siguiente micro-benchmark (obviamente tómelo con un grano de sal, pero esto es lo que podría hacer fácilmente):
Salmuera Python
import pickle
import time
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
people = [Person("Alex", 20), Person("Barbara", 25), Person("Charles", 30), Person("David", 35), Person("Emily", 40)]
for i in xrange(10000):
output = pickle.dumps(people, -1)
if i == 0: print len(output)
start_time = time.time()
for i in xrange(10000):
output = pickle.dumps(people, -1)
print time.time() - start_time
Salidas 174 bytes y 1.18-1.23 segundos para mí (Python 2.7.1 en Linux de 64 bits)
Scala kryo
import com.esotericsoftware.kryo._
import java.io._
class Person(val name: String, val age: Int)
object MyApp extends App {
val people = Array(new Person("Alex", 20), new Person("Barbara", 25), new Person("Charles", 30), new Person("David", 35), new Person("Emily", 40))
val kryo = new Kryo
kryo.setRegistrationOptional(true)
val buffer = new ObjectBuffer(kryo)
for (i <- 0 until 10000) {
val output = new ByteArrayOutputStream
buffer.writeObject(output, people)
if (i == 0) println(output.size)
}
val startTime = System.nanoTime
for (i <- 0 until 10000) {
val output = new ByteArrayOutputStream
buffer.writeObject(output, people)
}
println((System.nanoTime - startTime) / 1e9)
}
Resultados 68 bytes para mí y 30-40 ms (Kryo 1.04, Scala 2.9.1, Java 1.6.0.26 hotspot JVM en Linux de 64 bits). A modo de comparación, genera 51 bytes y 18-25 ms si registro las clases.
Comparación
Kryo usa aproximadamente el 40% del espacio y el 3% del tiempo como Python pickle cuando no registra clases, y aproximadamente el 30% del espacio y el 2% del tiempo cuando registra clases. Y siempre puede escribir un serializador personalizado cuando desee más control.
La biblioteca de Twitter es simplemente increíble. Utiliza Kryo para la serialización, pero es muy fácil de usar. También es bueno: proporciona un tipo MeatLocker [X] que hace que cualquier X sea serializable.
Otra buena opción es la reciente (2016) **netvl/picopickle**
:
- Pequeño y casi sin dependencia (la biblioteca central solo depende de información).
- Extensibilidad : puede definir sus propios serializadores para sus tipos y puede crear backends personalizados, es decir, puede usar la misma biblioteca para los diferentes formatos de serialización (colecciones, JSON, BSON, etc.); otras partes del comportamiento de serialización como el manejo de nulos también se pueden personalizar.
- Flexibilidad y conveniencia: el formato de serialización predeterminado está bien para la mayoría de los usos, pero se puede personalizar de forma casi arbitraria con el soporte de un conveniente convertidor DSL.
- La serialización estática sin reflexión : sin forma Las macros genéricas se utilizan para proporcionar serializadores para tipos arbitrarios, lo que significa que no se utiliza reflexión.
Por ejemplo:
El pickler basado en Jawn también proporciona funciones adicionales,
readString()
/writeString()
yreadAst()
/writeAst()
, que [de] serializan objetos a cadenas y JSON AST a cadenas, respectivamente:
import io.github.netvl.picopickle.backends.jawn.JsonPickler._
case class A(x: Int, y: String)
writeString(A(10, "hi")) shouldEqual """{"x":10,"y":"hi"}"""
readString[A]("""{"x":10,"y":"hi"}""") shouldEqual A(10, "hi")
Scala ahora tiene Scala-pickling que se comporta tan bien o mejor que Kyro dependiendo del escenario. Consulte las diapositivas 34-39 en this presentación.
Yo recomendaría SBinary . Utiliza implicits que se resuelven en tiempo de compilación, por lo que es muy efectivo y seguro. Viene con soporte integrado para muchos tipos de datos Scala comunes. Tienes que escribir manualmente el código de serialización para tus clases (caso), pero es fácil de hacer.