2010-03-18 68 views
16

Para algunos trabajos gráficos, necesito leer una gran cantidad de datos lo más rápido posible y me gustaría leer y escribir directamente las estructuras de datos en el disco. Básicamente tengo una carga de modelos 3D en varios formatos de archivo que tardan demasiado en cargarse, así que quiero escribirlos en su formato "preparado" como un caché que se cargará mucho más rápido en las ejecuciones posteriores del programa.Lectura y escritura de vectores C++ en un archivo

¿Es seguro hacerlo así? ¿Mis preocupaciones están relacionadas directamente con la lectura de los datos del vector? He eliminado la comprobación de errores, he codificado 4 como el tamaño del int y así sucesivamente para poder dar un breve ejemplo de trabajo, sé que es un código incorrecto, mi pregunta es si es seguro en C++ leer una matriz completa de estructuras directamente en un vector como este? Creo que es así, pero C++ tiene tantas trampas y un comportamiento indefinido cuando comienzas a ir a un nivel bajo y lidias directamente con la memoria en bruto como esta.

Me doy cuenta de que los formatos y tamaños de números pueden cambiar entre plataformas y compiladores, pero esto solo será leído y escrito por el mismo programa de compilación para almacenar en caché los datos que puedan ser necesarios en una ejecución posterior del mismo programa.

#include <fstream> 
#include <vector> 

using namespace std; 

struct Vertex 
{ 
    float x, y, z; 
}; 

typedef vector<Vertex> VertexList; 

int main() 
{ 
    // Create a list for testing 
    VertexList list; 
    Vertex v1 = {1.0f, 2.0f, 3.0f}; list.push_back(v1); 
    Vertex v2 = {2.0f, 100.0f, 3.0f}; list.push_back(v2); 
    Vertex v3 = {3.0f, 200.0f, 3.0f}; list.push_back(v3); 
    Vertex v4 = {4.0f, 300.0f, 3.0f}; list.push_back(v4); 

    // Write out a list to a disk file 
    ofstream os ("data.dat", ios::binary); 

    int size1 = list.size(); 
    os.write((const char*)&size1, 4); 
    os.write((const char*)&list[0], size1 * sizeof(Vertex)); 
    os.close(); 


    // Read it back in 
    VertexList list2; 

    ifstream is("data.dat", ios::binary); 
    int size2; 
    is.read((char*)&size2, 4); 
    list2.resize(size2); 

    // Is it safe to read a whole array of structures directly into the vector? 
    is.read((char*)&list2[0], size2 * sizeof(Vertex)); 

} 
+6

Trate de evitar el uso de constantes mágicas: 'os.write (y size1, sizeof (size1))' es mejor que la hardcoding 4 allí. Lo mismo vale para la lectura. –

+2

@David, trate de evitar no leer la pregunta antes de hacer comentarios;) –

+3

@Poita_ :) Sé que el cambio ha sido solo para la compactación, pero el hecho es que '4' es apenas un poco más pequeño que' sizeof (int) ', y el último siempre debe ser preferido, incluso en fragmentos de código sintetizados. –

Respuesta

19

Como dice Laurynas, std::vector está garantizado que es contiguo, por lo que debería funcionar, pero es potencialmente no portátil.

En la mayoría de los sistemas, sizeof(Vertex) será 12, pero no es raro que la estructura sea acolchada, por lo que sizeof(Vertex) == 16. Si tuviera que escribir los datos en un sistema y luego leer ese archivo en otro, no hay garantía de que funcione correctamente.

+0

Y leer/escribir los bytes rellenos solo lo desacelerará. Escribiría un operador muy simple << (ostream ..) y leería un flotador a la vez (conceptualmente). – Jan

+2

Sin mencionar hacer que tus archivos sean un 33% más grandes en este caso. –

+0

@Jan Leer (presumiblemente) un archivo de texto y convertir los números en flotantes va a ser más costoso que leer un archivo binario directamente, incluso si es más grande. El archivo de texto también sería más grande, a menos que todos los valores tuvieran menos de cuatro dígitos. – KeithB

8

std::vector se garantiza que es continuo en la memoria, por lo que, sí.

1

Si esto se utiliza para el almacenamiento en caché por el mismo código, no veo ningún problema con esto. He usado esta misma técnica en múltiples sistemas sin problemas (todo basado en Unix). Como precaución adicional, es posible que desee escribir una estructura con valores conocidos al principio del archivo y verificar que se muestre como correcta. También es posible que desee registrar el tamaño de la estructura en el archivo. Esto ahorrará mucho tiempo de depuración en el futuro si el relleno cambia alguna vez.

+0

Sí Escribiría un encabezado en el archivo para asegurarme de que solo está leyendo lo que esperaba. – jcoder

9

Puede que esté interesado en la biblioteca Boost.Serialization. Sabe cómo guardar/cargar contenedores STL desde/hacia el disco, entre otras cosas. Puede ser exagerado para su ejemplo simple, pero podría ser más útil si realiza otros tipos de serialización en su programa.

He aquí algunos ejemplos de código que hace lo que está buscando:

#include <algorithm> 
#include <fstream> 
#include <vector> 
#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 
#include <boost/serialization/vector.hpp> 

using namespace std; 

struct Vertex 
{ 
    float x, y, z; 
}; 

bool operator==(const Vertex& lhs, const Vertex& rhs) 
{ 
    return lhs.x==rhs.x && lhs.y==rhs.y && lhs.z==rhs.z; 
} 

namespace boost { namespace serialization { 
    template<class Archive> 
    void serialize(Archive & ar, Vertex& v, const unsigned int version) 
    { 
     ar & v.x; ar & v.y; ar & v.z; 
    } 
} } 

typedef vector<Vertex> VertexList; 

int main() 
{ 
    // Create a list for testing 
    const Vertex v[] = { 
     {1.0f, 2.0f, 3.0f}, 
     {2.0f, 100.0f, 3.0f}, 
     {3.0f, 200.0f, 3.0f}, 
     {4.0f, 300.0f, 3.0f} 
    }; 
    VertexList list(v, v + (sizeof(v)/sizeof(v[0]))); 

    // Write out a list to a disk file 
    { 
     ofstream os("data.dat", ios::binary); 
     boost::archive::binary_oarchive oar(os); 
     oar << list; 
    } 

    // Read it back in 
    VertexList list2; 

    { 
     ifstream is("data.dat", ios::binary); 
     boost::archive::binary_iarchive iar(is); 
     iar >> list2; 
    } 

    // Check if vertex lists are equal 
    assert(list == list2); 

    return 0; 
} 

Tenga en cuenta que tenía que implementar una función serialize para su Vertex en el espacio de nombres boost::serialization. Esto le permite a la biblioteca de serialización saber cómo serializar los miembros Vertex.

He navegado a través del código fuente boost::binary_oarchive y parece que lee/escribe los datos de la matriz vectorial sin procesar directamente desde/hacia el almacenamiento intermedio de la secuencia. Entonces debería ser bastante rápido.

+0

Gracias. Tal vez exagerado para lo que necesito, pero seguro lo investigaré – jcoder

2

Otra alternativa a la lectura y escritura explícita de su vector<> desde y hacia un archivo es reemplazar el asignador subyacente por uno que asigne memoria de un archivo mapeado en la memoria. Esto le permitiría evitar una copia relacionada de lectura/escritura intermedia. Sin embargo, este enfoque tiene algunos gastos generales. A menos que su archivo sea muy grande, puede no tener sentido para su caso particular. Haga un perfil como de costumbre para determinar si este enfoque es adecuado.

También hay algunas advertencias sobre este enfoque que se manejan muy bien en la biblioteca Boost.Interprocess. De particular interés para usted puede ser su allocators and containers.

3

Acabo de encontrarme con este mismo problema.

En primer lugar, estas declaraciones están rotos

os.write((const char*)&list[0], size1 * sizeof(Vertex)); 
is.read((char*)&list2[0], size2 * sizeof(Vertex)); 

Hay otras cosas en la estructura de datos de vectores, por lo que esto hará que su nuevo vector se llenan con la basura.

Solución:
Cuando escriba su vector en un archivo, no se preocupe por el tamaño de su clase Vertex, solo escriba directamente el vector completo en la memoria.

os.write((const char*)&list, sizeof(list)); 

Y a continuación, puede leer todo el vector en memoria a la vez

is.seekg(0,ifstream::end); 
long size2 = is.tellg(); 
is.seekg(0,ifstream::beg); 
list2.resize(size2); 
is.read((char*)&list2, size2); 
+0

Nevermind. Acabo de probar tu solución. Funciona bien – mortonjt

Cuestiones relacionadas