txt por manejo linea lenguaje leer lectura guardar escritura ejercicios datos binarios archivos archivo c++ performance io ifstream

por - Lectura rápida de archivos de texto en c++



leer archivo txt c++ linea por linea (6)

Actualmente estoy escribiendo un programa en C ++ que incluye la lectura de muchos archivos de texto grandes. Cada uno tiene ~ 400,000 líneas, en casos extremos, 4000 o más caracteres por línea. Solo para probar, leí uno de los archivos usando ifstream y la implementación ofrecida por cplusplus.com. Tomó alrededor de 60 segundos, que es demasiado tiempo. Ahora me preguntaba, ¿hay alguna manera directa de mejorar la velocidad de lectura?

editar: el código que estoy usando es más o menos esto:

string tmpString; ifstream txtFile(path); if(txtFile.is_open()) { while(txtFile.good()) { m_numLines++; getline(txtFile, tmpString); } txtFile.close(); }

edición 2: el archivo que leí tiene solo 82 MB de tamaño. Principalmente dije que podría llegar a 4000 porque pensé que podría ser necesario saber para hacer un almacenamiento en memoria intermedia.

edición 3: Gracias a todos por sus respuestas, pero parece que no hay mucho espacio para mejorar dado mi problema. Tengo que usar readline, ya que quiero contar el número de líneas. Instanciar el ifstream como binario tampoco hizo que la lectura fuera más rápida. Intentaré paralelizarlo tanto como pueda, eso debería funcionar al menos.

Edición 4: Entonces, aparentemente, hay algunas cosas que puedo hacer. ¡Muchas gracias a Sehe por dedicar tanto tiempo a esto, lo aprecio mucho! =)


¿Tienes que leer todos los archivos al mismo tiempo? (al comienzo de su solicitud, por ejemplo)

Si lo hace, considere paralelizar la operación.

De cualquier manera, considere usar secuencias binarias, o read búfer para bloques de datos.


4000 * 400,000 = 1,6 GB si su disco duro no es un SSD, es probable que obtenga ~ 100 MB / s de lectura secuencial. Eso es 16 segundos solo en E / S.

Dado que no detalla el código específico que usa o cómo necesita analizar estos archivos (¿necesita leerlo línea por línea? ¿Tiene el sistema mucha RAM? ¿Podría leer todo el archivo en un gran buffer RAM? y luego analizarlo?) Hay poco que puede hacer para acelerar el proceso.

Los archivos mapeados en memoria no ofrecerán ninguna mejora de rendimiento al leer un archivo secuencialmente. Quizás el análisis manual de fragmentos grandes para líneas nuevas en lugar de usar "getline" ofrecería una mejora.

EDITAR Después de hacer algo de aprendizaje (gracias @sehe). Aquí está la solución mapeada de memoria que probablemente usaría.

#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <errno.h> int main() { char* fName = "big.txt"; // struct stat sb; long cntr = 0; int fd, lineLen; char *data; char *line; // map the file fd = open(fName, O_RDONLY); fstat(fd, &sb); //// int pageSize; //// pageSize = getpagesize(); //// data = mmap((caddr_t)0, pageSize, PROT_READ, MAP_PRIVATE, fd, pageSize); data = mmap((caddr_t)0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); line = data; // get lines while(cntr < sb.st_size) { lineLen = 0; line = data; // find the next line while(*data != ''/n'' && cntr < sb.st_size) { data++; cntr++; lineLen++; } /***** PROCESS LINE *****/ // ... processLine(line, lineLen); } return 0; }


Como alguien con un poco de experiencia en programación competitiva, puedo decirte: al menos para cosas simples como el análisis entero, el costo principal en C es bloquear las transmisiones de archivos (que por defecto está hecho para multi-threading). Utilice las versiones unlocked_stdio lugar ( fgetc_unlocked() , fread_unlocked() ). Para C ++, el conocimiento común es usar std::ios::sync_with_stdio(false) pero no sé si es tan rápido como unlocked_stdio .

Para referencia aquí está mi código de análisis entero estándar. Es mucho más rápido que scanf, como dije principalmente debido a no bloquear la transmisión. Para mí, fue tan rápido como las mejores versiones mmap codificadas a mano o personalizadas que había utilizado anteriormente, sin la demencia de la deuda de mantenimiento.

int readint(void) { int n, c; n = getchar_unlocked() - ''0''; while ((c = getchar_unlocked()) > '' '') n = 10*n + c-''0''; return n; }

(Nota: Este solo funciona si hay exactamente un carácter que no sea un dígito entre dos enteros).

Y, por supuesto, evitar la asignación de memoria si es posible ...


Neil Kirk, desafortunadamente no puedo responder a su comentario (no tengo suficiente reputación) pero hice una prueba de rendimiento en ifstream, una secuencia de cuerdas y el rendimiento, leyendo un archivo de texto línea por línea, es exactamente el mismo.

std::stringstream stream; std::string line; while(std::getline(stream, line)) { }

Esto lleva 1426 ms en un archivo de 106 MB.

std::ifstream stream; std::string line; while(ifstream.good()) { getline(stream, line); }

Esto lleva 1433ms en el mismo archivo.

El siguiente código es más rápido en su lugar:

const int MAX_LENGTH = 524288; char* line = new char[MAX_LENGTH]; while (iStream.getline(line, MAX_LENGTH) && strlen(line) > 0) { }

Esto lleva 884ms en el mismo archivo. Es un poco complicado ya que tiene que establecer el tamaño máximo de su búfer (es decir, la longitud máxima para cada línea en el archivo de entrada).


Use Random file access o use el binary mode . para secuenciales, esto es grande pero aún depende de lo que estás leyendo.


Actualizaciones: asegúrese de verificar las actualizaciones (sorprendentes) debajo de la respuesta inicial

Los archivos de memoria mapeados me han servido bien 1 :

#include <boost/iostreams/device/mapped_file.hpp> // for mmap #include <algorithm> // for std::find #include <iostream> // for std::cout #include <cstring> int main() { boost::iostreams::mapped_file mmap("input.txt", boost::iostreams::mapped_file::readonly); auto f = mmap.const_data(); auto l = f + mmap.size(); uintmax_t m_numLines = 0; while (f && f!=l) if ((f = static_cast<const char*>(memchr(f, ''/n'', l-f)))) m_numLines++, f++; std::cout << "m_numLines = " << m_numLines << "/n"; }

Esto debería ser bastante rápido.

Actualizar

En caso de que lo ayude a probar este enfoque, aquí hay una versión que usa mmap directamente en lugar de usar Boost: verlo en vivo en Coliru

#include <algorithm> #include <iostream> #include <cstring> // for mmap: #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> const char* map_file(const char* fname, size_t& length); int main() { size_t length; auto f = map_file("test.cpp", length); auto l = f + length; uintmax_t m_numLines = 0; while (f && f!=l) if ((f = static_cast<const char*>(memchr(f, ''/n'', l-f)))) m_numLines++, f++; std::cout << "m_numLines = " << m_numLines << "/n"; } void handle_error(const char* msg) { perror(msg); exit(255); } const char* map_file(const char* fname, size_t& length) { int fd = open(fname, O_RDONLY); if (fd == -1) handle_error("open"); // obtain file size struct stat sb; if (fstat(fd, &sb) == -1) handle_error("fstat"); length = sb.st_size; const char* addr = static_cast<const char*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0u)); if (addr == MAP_FAILED) handle_error("mmap"); // TODO close fd at some point in time, call munmap(...) return addr; }

Actualizar

El último poco de rendimiento que pude sacar de esto lo encontré al ver la fuente de GNU coreutils wc . Para mi sorpresa, utilizando el siguiente código (simplificado en gran medida) adaptado de wc ejecuta en aproximadamente el 84% del tiempo tomado con el archivo mapeado de memoria anterior:

static uintmax_t wc(char const *fname) { static const auto BUFFER_SIZE = 16*1024; int fd = open(fname, O_RDONLY); if(fd == -1) handle_error("open"); /* Advise the kernel of our access pattern. */ posix_fadvise(fd, 0, 0, 1); // FDADVICE_SEQUENTIAL char buf[BUFFER_SIZE + 1]; uintmax_t lines = 0; while(size_t bytes_read = read(fd, buf, BUFFER_SIZE)) { if(bytes_read == (size_t)-1) handle_error("read failed"); if (!bytes_read) break; for(char *p = buf; (p = (char*) memchr(p, ''/n'', (buf + bytes_read) - p)); ++p) ++lines; } return lines; }

1 ver, por ejemplo, el punto de referencia aquí: cómo analizar flotaciones separadas por espacios en C ++ rápidamente?