2008-11-06 4 views
6

Tengo que lidiar con archivos de texto muy grandes (2 GB), es obligatorio leerlos/escribirlos línea por línea. Escribir 23 millones de líneas utilizando ofstream es muy lento, así que, al principio, traté de acelerar el proceso escribiendo grandes trozos de líneas en un búfer de memoria (por ejemplo, 256 MB o 512 MB) y luego escribí el búfer en el archivo . Esto no funcionó, el rendimiento es más o menos el mismo. Tengo el mismo problema al leer los archivos. Sé que las operaciones de E/S están protegidas por el sistema de E/S STL y esto también depende de la política del programador del disco (administrada por el sistema operativo, en mi caso, Linux).¿Cuándo construir su propio sistema de almacenamiento intermedio para E/S (C++)?

¿Alguna idea sobre cómo mejorar el rendimiento?

PD: He estado pensando en utilizar un proceso hijo de fondo (o un hilo) para leer/escribir los fragmentos de datos mientras el programa está procesando datos pero no sé (principalmente en el caso del subproceso) si esto será digno

+0

Ver esta respuesta para leer desde una archivo directamente en el búfer de cadena de caracteres: http://stackoverflow.com/questions/132358/how-to-read-file-content-into-istringstream#138645 –

+0

No mencionó qué velocidades está viendo. Según las unidades físicas, no espere que las escrituras sostenidas excedan mucho más de 40-60 MB/s. Use dd para comparar el rendimiento de la escritura. Utilice unidades seccionadas (como RAID 0, RAID 1 + 0) para aumentar el rendimiento (hasta ~ 4 unidades antes de que el bus cierre). –

Respuesta

10

A 2 GB es bastante grande, y hay que estar al tanto de todas las posibles áreas que pueden actuar como cuellos de botella:

  • El disco duro en sí
  • La interfaz de disco duro (IDE/SATA/RAID/Biblioteca USB?)
  • sistema operativo/sistema de archivos
  • C/C++
  • Su código

me gustaría empezar haciendo algunas mediciones:

  • ¿Cuánto tiempo toma el código para leer/escribir un archivo de 2 GB,
  • ¿Qué tan rápido puede el 'dd' comando de lectura y escritura en el disco? Ejemplo ...

    dd if=/dev/zero bs=1024 count=2000000 of=file_2GB

  • ¿Cuánto tiempo se tarda en escribir/leer utilizando sólo grande fwrite()/fread() llama a

Asumiendo que su disco es capaz de leer/escribir en alrededor 40Mb/s (que es probablemente una cifra realista para comenzar), su archivo de 2GB no puede correr más rápido que unos 50 segundos.

¿Cuánto tiempo lleva realmente?

Hola Roddy, utilizando fstream leer método con 1,1 GB de archivos y grandes tampones (128.255 o 512 MB) que tarda unos 43-48 segundos y es el mismo usando getline fstream (línea por línea) . cp tarda casi 2 minutos en copiar el archivo .

En ese caso, estás obligado por hardware. cp tiene que leer y escribir, y estará buscando ida y vuelta en la superficie del disco como loco cuando lo hace. Entonces, como verá, será más del doble de malo que el simple caso de "lectura".

Para mejorar la velocidad, lo primero que probaría es un disco duro más rápido o una SSD.

¿No ha dicho qué es la interfaz del disco? SATA es prácticamente la opción más fácil/rápida. También (punto obvio, esto ...) asegurarse de que el disco está físicamente en la misma máquina que su código se ejecuta, si no estás a la red de ruedas ...

+1

Si está cumpliendo con las limitaciones de hardware, pasar a una unidad ligeramente más rápida no será tan útil como mudarse a unidades con rayas. Además, ¿por qué usar _cp_ para esto? En su lugar use dd if =/dev/zero of =/path y simplemente pruebe el rendimiento de escritura. Experimenta con blocksizes (bs = 4K bs = 32K) para ver cómo eso afecta la velocidad. –

5

Quizás deba buscar en los archivos mapeados en la memoria.

comprueba a esta biblioteca: Boost.Interprocess

+0

MMFs habría sido mi sugerencia también. +1 por mencionar el apoyo de Boost para eso. – OregonGhost

8

También sugeriría archivos mapeados en memoria pero si usted va a utilizar impulso creo boost::iostreams::mapped_file es un partido mejor que el impulso :: entre procesos.

archivo
+0

No estaba al tanto de eso. –

0

Si va a amortiguar el archivo usted mismo, a continuación, Aconsejaría algunas pruebas utilizando E/S sin búfer (setvbuf en un archivo que has abierto puede desactivar el búfer de la biblioteca).

Básicamente, si usted va a amortiguar sí mismo, desea desactivar el almacenamiento en búfer de la biblioteca, ya que sólo va a causar dolor. No sé si hay alguna forma de hacerlo para STL I/O, por lo que recomiendo bajar a la E/S de nivel C.

3

Es sólo una idea, pero evitar el uso de std :: endl ya que esto obligará a una escalera antes de que el buffer está lleno. Use '\ n' en su lugar para una nueva línea.

+0

Sí, tienes razón. Buen punto :) – Bocaballena

2

No utilizar nuevas para asignar el almacenamiento intermedio de esa manera:

Probar: std :: vector <>

unsigned int  buffer_size = 64 * 1024 * 1024; // 64 MB for instance. 
std::vector<char> data_buffer(buffer_size); 
_file->read(&data_buffer[0], buffer_size); 

También lea el artículo sobre using underscore in identifier names:. Tenga en cuenta que su código está bien, pero.

+0

He usado new y char * solo para hacerlo lo más rápido posible. Este código estaba en un método de clase, en mi estilo personal uso el guión bajo para identificar las variables de los miembros de la clase, mientras que las variables locales del método no tienen prefijos. – Bocaballena

1

Usando getline(), puede ser ineficaz porque puede ser necesario varias veces de tamaño re el búfer de cadena como datos se añaden a la misma desde la memoria de flujo. Puede que esto sea más eficiente mediante la pre-dimensionamiento de la cadena:

También se puede ajustar el tamaño de las iostreams búfer ya sea muy grande o NULL (para no tamponada)

// Unbuffered Accesses: 
fstream file; 
file.rdbuf()->pubsetbuf(NULL,0); 
file.open("PLOP"); 

// Larger Buffer 
std::vector<char> buffer(64 * 1024 * 1024); 
fstream   file; 
file.rdbuf()->pubsetbuf(&buffer[0],buffer.size()); 
file.open("PLOP"); 

std::string line; 
line.reserve(64 * 1024 * 1024); 

while(getline(file,line)) 
{ 
    // Do Stuff. 
} 
+0

La clase usa un búfer char * que está asociado con el streambuf de un istringstream. Cargué los datos brutos del archivo directamente en el búfer y uso el stringstream para formatearlo más tarde, pero eso no mejoró el rendimiento. Por si acaso lo intenté con ifstream y pubsetbuf, pero es más lento. ¿Por qué? – Bocaballena

Cuestiones relacionadas