2010-09-03 29 views
15

Buenos días a todos,serialización C++ más rápida?

Estoy buscando una técnica de serialización binaria muy rápida para C++. Solo necesito serializar los datos contenidos en los objetos (sin punteros, etc.). Me gustaría que sea tan rápido como posible. Si es específico del hardware x86, es aceptable.

Estoy familiarizado con los métodos de C para hacer esto. Como prueba, he bench marcado un par de técnicas. Descubrí que el método C es un 40% más rápido que el mejor método C++ que implementé.

¿Alguna sugerencia sobre cómo mejorar el método C++ (o las bibliotecas que hacen esto)? ¿Hay algo bueno disponible para los archivos mapeados en memoria?

Gracias

// c style writes 
{ 
    #pragma pack(1) 
    struct item 
    { 
     uint64_t off; 
     uint32_t size; 
    } data; 
    #pragma pack 

    clock_t start = clock(); 

    FILE* fd = fopen("test.c.dat", "wb"); 
    for (long i = 0; i < tests; i++) 
    { 
     data.off = i; 
     data.size = i & 0xFFFF; 
     fwrite((char*) &data, sizeof(data), 1, fd); 
    } 
    fclose(fd); 

    clock_t stop = clock(); 

    double d = ((double)(stop-start))/ CLOCKS_PER_SEC; 
    printf("%8.3f seconds\n", d); 
} 

Alrededor de 1,6 segundos para pruebas = 10000000

// c++ style ofstream writes 

// define a DTO class 
class test 
{ 
public: 
    test(){} 

    uint64_t off; 
    uint32_t size; 

    friend std::ostream& operator<<(std::ostream& stream, const test& v); 
}; 

// write to the stream 
std::ostream& operator<<(std::ostream &stream, const test& v) 
{ 
    stream.write((char*)&v.off, sizeof(v.off)); 
    stream.write((char*)&v.size, sizeof(v.size)); 
    return stream; 
} 

{ 
    test data; 

    clock_t start = clock(); 

    std::ofstream out; 
    out.open("test.cpp.dat", std::ios::out | std::ios::trunc | std::ios::binary); 
    for (long i = 0; i < tests; i++) 
    { 
     data.off = i; 
     data.size = i & 0xFFFF; 
     out << data; 
    } 
    out.close(); 

    clock_t stop = clock(); 

    double d = ((double)(stop-start))/ CLOCKS_PER_SEC; 
    printf("%8.3f seconds\n", d); 
} 

alrededor de 2,6 segundos para pruebas = 10000000

+10

Esto no es una serialización, es un volcado de memoria. No funciona si el diseño de la memoria de sus objetos cambia, o si pasa de una plataforma big-endian a una little-endian. –

+1

Ese código no es igual. En el operador << debe simplemente << v.off/v.size y no hacer lo que ha hecho. Tampoco empaquetaste la clase de prueba, ni truncaste el archivo en C, y en C++ hiciste dos llamadas de escritura, una para cada miembro, mientras que en C escribiste toda la estructura a la vez. – Puppy

+0

Hasta donde entiendo, las transmisiones en C++ son bastante lentas. (intercambio es su capacidad de tipeo). –

Respuesta

1

¿Hay alguna manera usted puede tomar ventaja de las cosas que permanecen ¿mismo?

Quiero decir, estás tratando de ejecutar "test.c.dat" tan rápido como puedas, ¿no? ¿Puedes aprovechar el hecho de que el archivo no cambia entre tus intentos de serialización? Si intenta serializar el mismo archivo, una y otra vez, puede optimizar en función de esto. Puedo hacer que el primer intento de serialización tome la misma cantidad de tiempo que el tuyo, más un pequeño extra para otro control, y luego si tratas de volver a ejecutar la serialización en la misma entrada, puedo hacer que mi segunda ejecución sea mucho más rápida que la primera vez.

Entiendo que esto puede ser solo un ejemplo cuidadosamente elaborado, pero parece que se centra en hacer que el idioma logre su tarea lo más rápido posible, en lugar de hacer la pregunta "¿Necesito lograr esto de nuevo?" ¿Cuál es el contexto de este enfoque?

Espero que esto sea útil.

-Brian J. Stinar-

+0

Se usará como una base de datos de configuración. El código que escribí fue simplemente para probar la sobrecarga de los métodos. Buena idea sin embargo. – Jay

5

Si la tarea que se va a realizar es realmente la serialización, puede consultar el Protocol Buffers de Google. Brindan una rápida serialización de las clases de C++. El sitio también menciona algunas bibliotecas alternativas, p. boost.serialization (solo para indicar que los búferes de protocolo los superan en la mayoría de los casos, por supuesto ;-)

+1

Protocol Buffers (por mucho que me encanta) no es realmente serialización, es más para el envío de mensajes. La diferencia es que para el búfer de protocolo se define una clase de Mensaje, mientras que en la serialización no hay representación intermediaria. –

+0

Pensando un poco más al respecto, podría usar la clase protobuf para retener sus datos dentro de la clase real, de esta manera usted podría usar protobuf para guardar datos y codificar/decodificar mientras oculta este hecho a sus usuarios. –

1

Gran parte del rendimiento dependerá de los búferes de memoria y de cómo llene los bloques de memoria antes de escribir en el disco. Y hay algunos trucos para hacer las transmisiones estándar de C++ un poco más rápido, como std::ios_base::sync_with_stdio (false);

Pero en mi humilde opinión, el mundo no necesita otra implementación de serialización.Aquí hay algunas que otras personas sostienen que es posible que desee ver en:

  • Boost: Rápido, variada biblioteca de C++ que incluye la serialización
  • protobuf: Fast multiplataforma, serialización entre lenguajes con C++ módulo
  • thrift: multi-plataforma flexible, serialización entre lenguajes de los módulos C++
+5

Muéstreme un paquete de serialización que sea útil en un entorno restringido con uso de memoria determinista y le mostraré el único paquete de serialización que necesitará. Hasta entonces, es un poco engañoso decir que no necesitamos otro paquete de serialización cuando los requisitos de serialización de todos son contradictorios. – MSN

+0

Miré boost. Salta a través de todo tipo de aros para serializar cualquier objeto y solo necesito POD. ¿Por qué pagar extra que no necesita? – Jay

+1

@Jay: si solo necesitas soporte para POD, ¿por qué no utilizas tu enfoque C? – jalf

12

Hay sólo muy pocos casos de la vida real, donde lo que importa en todas. Solo se serializa para hacer que sus objetos sean compatibles con algún tipo de recurso externo. Disco, red, etcétera. El código que transmite los datos serializados en el recurso es siempre órdenes de magnitud más lentos que el código necesario para serializar el objeto. Si hace que el código de serialización sea dos veces más rápido, ha hecho que la operación general no sea más de un 0,5% más rápida, más o menos. Eso no vale ni el riesgo ni el esfuerzo.

Mida tres veces, corte una vez.

+0

Excelente punto. Gracias – Jay

2

Bueno, si quieres la serialización más rápida posible, entonces puedes simplemente escribir tu propia clase de serialización y darle los métodos para serializar cada uno de los tipos de POD.

Cuanta menos seguridad traiga, más rápido se ejecutará y más difícil será la depuración, sin embargo, solo hay un número fijo de unidades integradas para que pueda enumerarlas.

class Buffer 
{ 
public: 
    inline Buffer& operator<<(int i); // etc... 
private: 
    std::deque<unsigned char> mData; 
}; 

debo admitir que no entiendo su problema:

  • ¿Qué es lo que realmente quiere hacer con el mensaje serializado?
  • ¿Lo está guardando para más adelante?
  • ¿Tiene que preocuparse por la compatibilidad hacia adelante/hacia atrás?

Puede haber mejores enfoques que la serialización.

+0

Voy a continuar la información en el disco. Solo se cargará en la misma máquina en la que se guarda. Estaba considerando poner una versión número en los objetos por lo que sería más capaz de lidiar con los cambios. Si conoce mejores enfoques estaría encantado de saber de ellos. – Jay

+0

El control de versiones es imprescindible, de lo contrario, está asombrado. porque versionar cada estructura individual hará que las cosas sean un poco más costosas y no es fácil mantener una sola versión. También sugiero usar algunos marcadores de 'sincronización' de un lugar a otro y tal vez un código CRC para verificar la integridad de los datos (en caso el archivo se corrompe). Comenté la respuesta de Thorsten77 sobre protobuf, parece que podría ayudar mucho. –

0

Tanto su C como su código C++ probablemente estarán dominados (en el tiempo) por archivos E/S. Recomendaría utilizar archivos mapeados en memoria al escribir sus datos y dejar el almacenamiento en búfer de E/S en el sistema operativo. Boost.Interprocess podría ser una alternativa.

+0

archivos mapeados en memoria es muy específico para el sistema operativo. – Jay

1

Porque yo/que es más probable que sea el cuello de botella de un formato compacto puede ayudar S. Por curiosidad probé el siguiente esquema de Colfer compilado como colf -s 16 C.

package data 

    type item struct { 
      off uint64 
      size uint32 
    } 

...con una prueba de C comparable:

clock_t start = clock(); 

    data_item data; 
    void* buf = malloc(colfer_size_max); 

    FILE* fd = fopen("test.colfer.dat", "wb"); 
    for (long i = 0; i < tests; i++) 
    { 
     data.off = i; 
     data.size = i & 0xFFFF; 
     size_t n = data_item_marshal(&data, buf); 
     fwrite(buf, n, 1, fd); 
    } 
    fclose(fd); 

    clock_t stop = clock(); 

los resultados son bastante decepcionantes en SSD a pesar del hecho de que el tamaño de serie es 40% más pequeño en comparación con los vertederos struct primas.

colfer took 0.520 seconds 
    plain took 0.320 seconds 

Dado que el código generado es pretty fast parece poco probable que va a ganar nada con bibliotecas de serialización.

0

Para responder realmente a esta pregunta, la razón por la cual la versión de C++ es lenta es que llama al ostream.write muchas veces, lo que provoca una gran cantidad de comprobaciones de estado innecesarias. Puede crear un buffer simple y usar solo un write y verá la diferencia.

Si su disco/red es lo suficientemente rápido como para no convertirse en el cuello de botella, flatbufferscapnproto son excelentes opciones para manejar esto por usted.

De lo contrario, protobuf, xxx-compact ... cualquiera que use codificación varint probablemente pueda serializar estos datos a un cuarto del tamaño original. HPS de la comunidad informática científica es también una gran opción para este tipo de datos altamente estructurados y probablemente el más rápido en velocidad y el más pequeño en tamaño de mensaje en este caso debido a su esquema de codificación.

Cuestiones relacionadas