operaciones modificar fichero existe escribir ejemplos con comprobar binarios binario archivos archivo c++ performance optimization file-io io

modificar - Escribiendo un archivo binario en C++ muy rápido



modificar archivo binario en c (12)

¿Podría usar FILE* lugar, y medir el rendimiento que ha obtenido? Un par de opciones es usar fwrite/write lugar de fstream :

#include <stdio.h> int main () { FILE * pFile; char buffer[] = { ''x'' , ''y'' , ''z'' }; pFile = fopen ( "myfile.bin" , "w+b" ); fwrite (buffer , 1 , sizeof(buffer) , pFile ); fclose (pFile); return 0; }

Si decides usar la write , prueba algo similar:

#include <unistd.h> #include <fcntl.h> int main(void) { int filedesc = open("testfile.txt", O_WRONLY | O_APPEND); if (filedesc < 0) { return -1; } if (write(filedesc, "This will be output to testfile.txt/n", 36) != 36) { write(2, "There was an error writing to testfile.txt/n", 43); return -1; } return 0; }

También te aconsejaría que mires en el memory map . Esa puede ser tu respuesta. Una vez tuve que procesar un archivo de 20GB en otro para almacenarlo en la base de datos, y el archivo ni siquiera se estaba abriendo. Así que la solución para utilizar el mapa de memoria. Aunque lo hice en Python .

Estoy tratando de escribir grandes cantidades de datos en mi SSD (unidad de estado sólido). Y por grandes cantidades me refiero a 80GB.

Busqué soluciones en la web, pero lo mejor que se me ocurrió fue esto:

#include <fstream> const unsigned long long size = 64ULL*1024ULL*1024ULL; unsigned long long a[size]; int main() { std::fstream myfile; myfile = std::fstream("file.binary", std::ios::out | std::ios::binary); //Here would be some error handling for(int i = 0; i < 32; ++i){ //Some calculations to fill a[] myfile.write((char*)&a,size*sizeof(unsigned long long)); } myfile.close(); }

Compilado con Visual Studio 2010 y optimizaciones completas y ejecutado bajo Windows 7, este programa maximiza alrededor de 20 MB / s. Lo que realmente me molesta es que Windows puede copiar archivos de otro SSD a este SSD en algún lugar entre 150 MB / sy 200 MB / s. Así que al menos 7 veces más rápido. Por eso creo que debería poder ir más rápido.

¿Alguna idea de cómo puedo acelerar mi escritura?


Esto hizo el trabajo:

#include <stdio.h> const unsigned long long size = 8ULL*1024ULL*1024ULL; unsigned long long a[size]; int main() { FILE* pFile; pFile = fopen("file.binary", "wb"); for (unsigned long long j = 0; j < 1024; ++j){ //Some calculations to fill a[] fwrite(a, 1, size*sizeof(unsigned long long), pFile); } fclose(pFile); return 0; }

Acabo de programar 8 GB en 36 segundos, lo que equivale a unos 220 MB / s, y creo que eso supera mi SSD. También vale la pena señalar que el código de la pregunta utilizó un núcleo del 100%, mientras que este código solo utiliza el 2-5%.

Muchas gracias a todos.

Actualización : 5 años han pasado. Los compiladores, hardware, bibliotecas y mis requisitos han cambiado. Es por eso que hice algunos cambios en el código e hice algunas mediciones.

Primero el código:

#include <fstream> #include <chrono> #include <vector> #include <cstdint> #include <numeric> #include <random> #include <algorithm> #include <iostream> #include <cassert> std::vector<uint64_t> GenerateData(std::size_t bytes) { assert(bytes % sizeof(uint64_t) == 0); std::vector<uint64_t> data(bytes / sizeof(uint64_t)); std::iota(data.begin(), data.end(), 0); std::shuffle(data.begin(), data.end(), std::mt19937{ std::random_device{}() }); return data; } long long option_1(std::size_t bytes) { std::vector<uint64_t> data = GenerateData(bytes); auto startTime = std::chrono::high_resolution_clock::now(); auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary); myfile.write((char*)&data[0], bytes); myfile.close(); auto endTime = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count(); } long long option_2(std::size_t bytes) { std::vector<uint64_t> data = GenerateData(bytes); auto startTime = std::chrono::high_resolution_clock::now(); FILE* file = fopen("file.binary", "wb"); fwrite(&data[0], 1, bytes, file); fclose(file); auto endTime = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count(); } long long option_3(std::size_t bytes) { std::vector<uint64_t> data = GenerateData(bytes); std::ios_base::sync_with_stdio(false); auto startTime = std::chrono::high_resolution_clock::now(); auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary); myfile.write((char*)&data[0], bytes); myfile.close(); auto endTime = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count(); } int main() { const std::size_t kB = 1024; const std::size_t MB = 1024 * kB; const std::size_t GB = 1024 * MB; for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option1, " << size / MB << "MB: " << option_1(size) << "ms" << std::endl; for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option2, " << size / MB << "MB: " << option_2(size) << "ms" << std::endl; for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option3, " << size / MB << "MB: " << option_3(size) << "ms" << std::endl; return 0; }

Ahora el código se compila con Visual Studio 2017 y g ++ 7.2.0 (que ahora es uno de mis requisitos). Dejo correr el código con dos configuraciones:

  • Laptop, Core i7, SSD, Ubuntu 16.04, g ++ Versión 7.2.0 con -std = c ++ 11 -march = Native -O3
  • Desktop, Core i7, SSD, Windows 10, Visual Studio 2017 versión 15.3.1 con / Ox / Ob2 / Oi / Ot / GT / GL / Gy

Lo que dio las siguientes medidas (después de descartar los valores de 1MB, porque eran valores atípicos obvios): Ambas opciones option1 y option3 maximizan mi SSD. No esperaba que esto se viera, porque la opción 2 solía ser el código más rápido en mi máquina en aquel entonces.

TL; DR : Mis medidas indican el uso de std::fstream sobre FILE .


Estoy compilando mi programa en gcc en GNU / Linux y mingw en win 7 y win xp y funcionó bien

puede usar mi programa y para crear un archivo de 80 GB, simplemente cambie la línea 33 a

makeFile("Text.txt",1024,8192000);

cuando salga del programa, el archivo se destruirá y luego verifique el archivo cuando se esté ejecutando

Para tener el programa que desea solo cambie el programa.

firt one es el programa windows y el segundo es para GNU / Linux

http://mustafajf.persiangig.com/Projects/File/WinFile.cpp

http://mustafajf.persiangig.com/Projects/File/File.cpp


Intente lo siguiente, en orden:

  • Tamaño de búfer más pequeño. Escribir ~ 2 MiB a la vez puede ser un buen comienzo. En mi última computadora portátil, ~ 512 KiB fue el punto ideal, pero todavía no he probado mi SSD.

    Nota: He notado que los búferes muy grandes tienden a disminuir el rendimiento. He notado pérdidas de velocidad con el uso de búferes de 16 MiB en lugar de búferes de 512 KiB antes.

  • Use _open (o _topen si desea que Windows sea correcto) para abrir el archivo, luego use _write . Esto probablemente evitará una gran cantidad de almacenamiento en búfer, pero no es seguro.

  • Usando funciones específicas de Windows como WriteFile y WriteFile . Eso evitará cualquier almacenamiento en búfer en la biblioteca estándar.


Intente utilizar las llamadas a API open () / write () / close () y experimente con el tamaño del búfer de salida. Quiero decir, no pasar todo el búfer de "muchos muchos bytes" a la vez, hacer un par de escrituras (es decir, TotalNumBytes / OutBufferSize). OutBufferSize puede ser de 4096 bytes a megabyte.

Otro intento: use WinAPI OpenFile / CreateFile y use este artículo de MSDN para desactivar el almacenamiento en búfer (FILE_FLAG_NO_BUFFERING). Y este artículo de MSDN en WriteFile () muestra cómo obtener el tamaño de bloque para que la unidad sepa el tamaño óptimo del búfer.

De todos modos, std :: ofstream es un envoltorio y puede haber bloqueos en las operaciones de E / S. Tenga en cuenta que atravesar todo el arreglo de N-gigabytes también toma algo de tiempo. Mientras escribe un pequeño búfer, llega al caché y funciona más rápido.


La mejor solución es implementar una escritura asíncrona con doble búfer.

Mira la línea de tiempo:

------------------------------------------------> FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|

La ''F'' representa el tiempo para el llenado del búfer y la ''W'' representa el tiempo para escribir el búfer en el disco. Así que el problema es perder tiempo entre escribir buffers para archivar. Sin embargo, al implementar la escritura en un subproceso separado, puedes comenzar a llenar el siguiente búfer inmediatamente:

------------------------------------------------> (main thread, fills buffers) FF|ff______|FF______|ff______|________| ------------------------------------------------> (writer thread) |WWWWWWWW|wwwwwwww|WWWWWWWW|wwwwwwww|

F - relleno 1er tampón
f - llenando 2º tampón
W - escribiendo el primer búfer al archivo
w - escribiendo 2do buffer al archivo
_ - esperar mientras se completa la operación

Este enfoque con swaps de búfer es muy útil cuando llenar un búfer requiere un cálculo más complejo (por lo tanto, más tiempo). Siempre implemento una clase CSequentialStreamWriter que oculta la escritura asíncrona en el interior, por lo que para el usuario final la interfaz solo tiene funciones de escritura.

Y el tamaño del búfer debe ser un múltiplo del tamaño del clúster de disco. De lo contrario, terminará con un rendimiento deficiente al escribir un solo búfer en 2 grupos de discos adyacentes.

Escribiendo el último búfer.
Cuando llama a la función de escritura por última vez, debe asegurarse de que el búfer actual se está llenando y que también se escriba en el disco. Por lo tanto, CSequentialStreamWriter debería tener un método separado, digamos Finalizar (final buffer flush), que debería escribir en el disco la última parte de los datos.

Manejo de errores.
Mientras el código comienza a llenar el segundo búfer, y el primero se escribe en un subproceso independiente, pero la escritura falla por algún motivo, el subproceso principal debe ser consciente de ese error.

------------------------------------------------> (main thread, fills buffers) FF|fX| ------------------------------------------------> (writer thread) __|X|

Asumamos que la interfaz de un CSequentialStreamWriter tiene la función de escritura devuelve bool o lanza una excepción, por lo tanto, al tener un error en un subproceso separado, debe recordar ese estado, por lo que la próxima vez que llame a Escribir o Finalizar en el subproceso principal, el método volverá. Falso o lanzará una excepción. Y realmente no importa en qué punto dejaste de llenar un búfer, incluso si escribiste algunos datos adelante después del error, lo más probable es que el archivo esté dañado e inútil.


No veo ninguna diferencia entre std :: stream / FILE / device. Entre buffering y no buffering.

También tenga en cuenta:

  • Las unidades SSD "tienden" a disminuir la velocidad (tasas de transferencia más bajas) a medida que se llenan.
  • Las unidades SSD "tienden" a disminuir la velocidad (tasas de transferencia más bajas) a medida que envejecen (debido a los bits que no funcionan).

Estoy viendo el código ejecutado en 63 segundos.
Por lo tanto, una tasa de transferencia de: 260M / s (mi SSD parece un poco más rápida que la tuya).

64 * 1024 * 1024 * 8 /*sizeof(unsigned long long) */ * 32 /*Chunks*/ = 16G = 16G/63 = 260M/s

No obtengo ningún aumento moviéndome a ARCHIVO * desde std :: fstream.

#include <stdio.h> using namespace std; int main() { FILE* stream = fopen("binary", "w"); for(int loop=0;loop < 32;++loop) { fwrite(a, sizeof(unsigned long long), size, stream); } fclose(stream); }

Así que el flujo de C ++ está funcionando tan rápido como lo permita la biblioteca subyacente.

Pero creo que es injusto comparar el sistema operativo con una aplicación que se construye sobre el sistema operativo. La aplicación no puede hacer suposiciones (no sabe que las unidades son SSD) y, por lo tanto, utiliza los mecanismos de archivo del sistema operativo para la transferencia.

Mientras que el sistema operativo no necesita hacer ninguna suposición. Puede indicar los tipos de unidades involucradas y utilizar la técnica óptima para transferir los datos. En este caso una transferencia directa de memoria a memoria. Intente escribir un programa que copie 80G de una ubicación en memoria a otra y vea qué tan rápido es.

Editar

Cambié mi código para usar las llamadas de nivel inferior:
es decir, sin almacenamiento en búfer.

#include <fcntl.h> #include <unistd.h> const unsigned long long size = 64ULL*1024ULL*1024ULL; unsigned long long a[size]; int main() { int data = open("test", O_WRONLY | O_CREAT, 0777); for(int loop = 0; loop < 32; ++loop) { write(data, a, size * sizeof(unsigned long long)); } close(data); }

Esto no hizo ninguna diferencia.

NOTA : Mi unidad es una unidad SSD. Si tiene una unidad normal, puede ver una diferencia entre las dos técnicas anteriores. Pero como esperaba que el almacenamiento en búfer no y el almacenamiento en búfer (cuando se escriben trozos grandes más grandes que el tamaño del búfer) no hacen diferencia

Edición 2:

¿Has probado el método más rápido para copiar archivos en C ++?

int main() { std::ifstream input("input"); std::ofstream output("ouptut"); output << input.rdbuf(); }


Si copia algo del disco A al disco B en el explorador, Windows emplea DMA. Eso significa que para la mayor parte del proceso de copia, la CPU básicamente no hará más que decirle al controlador del disco dónde colocar y obtener datos, eliminando todo un paso en la cadena y uno que no está en absoluto optimizado para mover grandes cantidades de datos - y me refiero a hardware.

Lo que haces involucra mucho a la CPU. Quiero señalarle la parte "Algunos cálculos para completar una []". Lo cual creo que es esencial. Genera un [], luego copia de un [] a un búfer de salida (eso es lo que hace fstream :: write), luego genera nuevamente, etc.

¿Qué hacer? Multihilo! (Espero que tengas un procesador multi-core)

  • tenedor.
  • Utilice un hilo para generar un [] datos
  • Use el otro para escribir datos de un [] al disco
  • Necesitará dos matrices a1 [] y a2 [] y cambiar entre ellas
  • Necesitará algún tipo de sincronización entre sus hilos (semáforos, cola de mensajes, etc.)
  • Use funciones de nivel inferior, sin búfer, como la función WriteFile mencionada por Mehrdad

Si desea escribir rápidamente en secuencias de archivos, puede hacer que el flujo de lectura sea más grande:

wfstream f; const size_t nBufferSize = 16184; wchar_t buffer[nBufferSize]; f.rdbuf()->pubsetbuf(buffer, nBufferSize);

Además, cuando se escriben muchos datos en archivos, a veces es más rápido extender el tamaño del archivo de forma lógica en lugar de hacerlo físicamente, esto se debe a que al extender un archivo lógicamente, el sistema de archivos no pone a cero el nuevo espacio antes de escribir en él. También es inteligente extender el archivo lógicamente más de lo que realmente necesita para evitar muchas extensiones de archivos. La extensión de archivos lógicos se admite en Windows llamando a SetFileValidData o xfsctl con XFS_IOC_RESVSP64 en sistemas XFS.


Trate de usar archivos asignados en memoria.


Yo sugeriría intentar la asignación de archivos . mmap en el pasado, en un entorno UNIX, y me impresionó el alto rendimiento que podía alcanzar


fstream s no son más lentos que C streams, pero usan más CPU (especialmente si el almacenamiento en búfer no está configurado correctamente). Cuando una CPU se satura, limita la velocidad de E / S.

Al menos la implementación de MSVC 2015 copia 1 carácter a la vez al búfer de salida cuando no se configura un búfer de flujo (consulte streambuf::xsputn ). Así que asegúrate de establecer un buffer de flujo (> 0) .

Puedo obtener una velocidad de escritura de 1500 MB / s (la velocidad máxima de mi M.2 SSD) con fstream utilizando este código:

#include <iostream> #include <fstream> #include <chrono> #include <memory> #include <stdio.h> #ifdef __linux__ #include <unistd.h> #endif using namespace std; using namespace std::chrono; const size_t sz = 512 * 1024 * 1024; const int numiter = 20; const size_t bufsize = 1024 * 1024; int main(int argc, char**argv) { unique_ptr<char[]> data(new char[sz]); unique_ptr<char[]> buf(new char[bufsize]); for (size_t p = 0; p < sz; p += 16) { memcpy(&data[p], "BINARY.DATA.....", 16); } unlink("file.binary"); int64_t total = 0; if (argc < 2 || strcmp(argv[1], "fopen") != 0) { cout << "fstream mode/n"; ofstream myfile("file.binary", ios::out | ios::binary); if (!myfile) { cerr << "open failed/n"; return 1; } myfile.rdbuf()->pubsetbuf(buf.get(), bufsize); // IMPORTANT for (int i = 0; i < numiter; ++i) { auto tm1 = high_resolution_clock::now(); myfile.write(data.get(), sz); if (!myfile) cerr << "write failed/n"; auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count()); cout << tm << " ms/n"; total += tm; } myfile.close(); } else { cout << "fopen mode/n"; FILE* pFile = fopen("file.binary", "wb"); if (!pFile) { cerr << "open failed/n"; return 1; } setvbuf(pFile, buf.get(), _IOFBF, bufsize); // NOT important auto tm1 = high_resolution_clock::now(); for (int i = 0; i < numiter; ++i) { auto tm1 = high_resolution_clock::now(); if (fwrite(data.get(), sz, 1, pFile) != 1) cerr << "write failed/n"; auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count()); cout << tm << " ms/n"; total += tm; } fclose(pFile); auto tm2 = high_resolution_clock::now(); } cout << "Total: " << total << " ms, " << (sz*numiter * 1000 / (1024.0 * 1024 * total)) << " MB/s/n"; }

Probé este código en otras plataformas (Ubuntu, FreeBSD) y no noté diferencias de velocidad de E / S, pero sí una diferencia de uso de CPU de aproximadamente 8: 1 ( fstream usó 8 veces más CPU ). Así que uno puede imaginar, si yo tuviera un disco más rápido, la escritura fstream se ralentizaría antes que la versión stdio .