versiones restaurar renombrar rapido programa pasos para mover mas manipular extremecopy directorios copiar como archivos archivo anteriores c++ file-io

c++ - restaurar - Copia un archivo de forma sana, segura y eficiente.



restaurar versiones anteriores de archivos (5)

¡Demasiados!

El búfer de manera "ANSI C" es redundante, ya que un FILE ya está en búfer. (El tamaño de este búfer interno es lo que realmente define BUFSIZ ).

El "OWN-BUFFER-C ++ - WAY" será lento a medida que avanza a través de fstream , que realiza una gran cantidad de envíos virtuales y, de nuevo, mantiene los búferes internos o cada objeto de flujo. (La "COPY-ALGORITHM-C ++ - WAY" no sufre esto, ya que la clase streambuf_iterator pasa por alto la capa de la secuencia.)

Prefiero el "COPY-ALGORITHM-C ++ - WAY", pero sin construir un fstream , simplemente cree las instancias std::filebuf cuando no se necesita un formato real.

Para un rendimiento en bruto, no se pueden superar los descriptores de archivos POSIX. Es feo pero portátil y rápido en cualquier plataforma.

La forma de Linux parece ser increíblemente rápida: ¿tal vez el sistema operativo permita que la función vuelva antes de que se complete la E / S? En cualquier caso, eso no es lo suficientemente portátil para muchas aplicaciones.

EDIT : Ah, "Linux nativo" puede estar mejorando el rendimiento intercalando lecturas y escrituras con E / S asíncronas. Permitir que los comandos se acumulen puede ayudar al controlador del disco a decidir cuándo es mejor buscar. Puedes probar Boost Asio o pthreads para comparar. En cuanto a "no se pueden superar los descriptores de archivos POSIX" ... bueno, eso es cierto si está haciendo algo con los datos, no solo copiando a ciegas.

Busco una buena manera de copiar un archivo (binario o texto). He escrito varias muestras, todo el mundo trabaja. Pero quiero escuchar la opinión de programadores experimentados.

Me faltan buenos ejemplos y busco una forma que funcione con C ++.

ANSI-C-WAY

#include <iostream> #include <cstdio> // fopen, fclose, fread, fwrite, BUFSIZ #include <ctime> using namespace std; int main() { clock_t start, end; start = clock(); // BUFSIZE default is 8192 bytes // BUFSIZE of 1 means one chareter at time // good values should fit to blocksize, like 1024 or 4096 // higher values reduce number of system calls // size_t BUFFER_SIZE = 4096; char buf[BUFSIZ]; size_t size; FILE* source = fopen("from.ogv", "rb"); FILE* dest = fopen("to.ogv", "wb"); // clean and more secure // feof(FILE* stream) returns non-zero if the end of file indicator for stream is set while (size = fread(buf, 1, BUFSIZ, source)) { fwrite(buf, 1, size, dest); } fclose(source); fclose(dest); end = clock(); cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "/n"; cout << "CPU-TIME START " << start << "/n"; cout << "CPU-TIME END " << end << "/n"; cout << "CPU-TIME END - START " << end - start << "/n"; cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "/n"; return 0; }

POSIX-WAY (K&R usa esto en "El lenguaje de programación C", más bajo nivel)

#include <iostream> #include <fcntl.h> // open #include <unistd.h> // read, write, close #include <cstdio> // BUFSIZ #include <ctime> using namespace std; int main() { clock_t start, end; start = clock(); // BUFSIZE defaults to 8192 // BUFSIZE of 1 means one chareter at time // good values should fit to blocksize, like 1024 or 4096 // higher values reduce number of system calls // size_t BUFFER_SIZE = 4096; char buf[BUFSIZ]; size_t size; int source = open("from.ogv", O_RDONLY, 0); int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644); while ((size = read(source, buf, BUFSIZ)) > 0) { write(dest, buf, size); } close(source); close(dest); end = clock(); cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "/n"; cout << "CPU-TIME START " << start << "/n"; cout << "CPU-TIME END " << end << "/n"; cout << "CPU-TIME END - START " << end - start << "/n"; cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "/n"; return 0; }

KISS-C ++ - Streambuffer-WAY

#include <iostream> #include <fstream> #include <ctime> using namespace std; int main() { clock_t start, end; start = clock(); ifstream source("from.ogv", ios::binary); ofstream dest("to.ogv", ios::binary); dest << source.rdbuf(); source.close(); dest.close(); end = clock(); cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "/n"; cout << "CPU-TIME START " << start << "/n"; cout << "CPU-TIME END " << end << "/n"; cout << "CPU-TIME END - START " << end - start << "/n"; cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "/n"; return 0; }

COPY-ALGORITHM-C ++ - CAMINO

#include <iostream> #include <fstream> #include <ctime> #include <algorithm> #include <iterator> using namespace std; int main() { clock_t start, end; start = clock(); ifstream source("from.ogv", ios::binary); ofstream dest("to.ogv", ios::binary); istreambuf_iterator<char> begin_source(source); istreambuf_iterator<char> end_source; ostreambuf_iterator<char> begin_dest(dest); copy(begin_source, end_source, begin_dest); source.close(); dest.close(); end = clock(); cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "/n"; cout << "CPU-TIME START " << start << "/n"; cout << "CPU-TIME END " << end << "/n"; cout << "CPU-TIME END - START " << end - start << "/n"; cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "/n"; return 0; }

OWN-BUFFER-C ++ - CAMINO

#include <iostream> #include <fstream> #include <ctime> using namespace std; int main() { clock_t start, end; start = clock(); ifstream source("from.ogv", ios::binary); ofstream dest("to.ogv", ios::binary); // file size source.seekg(0, ios::end); ifstream::pos_type size = source.tellg(); source.seekg(0); // allocate memory for buffer char* buffer = new char[size]; // copy file source.read(buffer, size); dest.write(buffer, size); // clean up delete[] buffer; source.close(); dest.close(); end = clock(); cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "/n"; cout << "CPU-TIME START " << start << "/n"; cout << "CPU-TIME END " << end << "/n"; cout << "CPU-TIME END - START " << end - start << "/n"; cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "/n"; return 0; }

LINUX-WAY // requiere kernel> = 2.6.33

#include <iostream> #include <sys/sendfile.h> // sendfile #include <fcntl.h> // open #include <unistd.h> // close #include <sys/stat.h> // fstat #include <sys/types.h> // fstat #include <ctime> using namespace std; int main() { clock_t start, end; start = clock(); int source = open("from.ogv", O_RDONLY, 0); int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644); // struct required, rationale: function stat() exists also struct stat stat_source; fstat(source, &stat_source); sendfile(dest, source, 0, stat_source.st_size); close(source); close(dest); end = clock(); cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "/n"; cout << "CPU-TIME START " << start << "/n"; cout << "CPU-TIME END " << end << "/n"; cout << "CPU-TIME END - START " << end - start << "/n"; cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "/n"; return 0; }

Ambiente

  • GNU / LINUX (Archlinux)
  • Kernel 3.3
  • GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
  • Uso de RUNLEVEL 3 (multiusuario, red, terminal, sin GUI)
  • INTEL SSD-Postville 80 GB, llenado hasta un 50%
  • Copie un archivo de video de 270 MB OGG

pasos para reproducir

1. $ rm from.ogg 2. $ reboot # kernel and filesystem buffers are in regular 3. $ (time ./program) &>> report.txt # executes program, redirects output of program and append to file 4. $ sha256sum *.ogv # checksum 5. $ rm to.ogg # remove copy, but no sync, kernel and fileystem buffers are used 6. $ (time ./program) &>> report.txt # executes program, redirects output of program and append to file

Resultados (CPU TIME utilizado)

Program Description UNBUFFERED|BUFFERED ANSI C (fread/frwite) 490,000|260,000 POSIX (K&R, read/write) 450,000|230,000 FSTREAM (KISS, Streambuffer) 500,000|270,000 FSTREAM (Algorithm, copy) 500,000|270,000 FSTREAM (OWN-BUFFER) 500,000|340,000 SENDFILE (native LINUX, sendfile) 410,000|200,000

El tamaño del archivo no cambia.
sha256sum imprimir los mismos resultados.
El archivo de video todavía se puede reproducir.

Preguntas

  • ¿Qué método preferirías?
  • ¿Conoces mejores soluciones?
  • ¿Ves algún error en mi código?
  • ¿Conoces una razón para evitar una solución?

  • FSTREAM (KISS, Streambuffer)
    Realmente me gusta este, porque es muy corto y simple. Hasta donde sé, el operador << está sobrecargado para rdbuf () y no convierte nada. ¿Correcto?

Gracias

Actualización 1
Cambié la fuente en todas las muestras de esa manera, para que la apertura y el cierre de los descriptores de archivo se incluyan en la medición del reloj () . No hay otros cambios significativos en el código fuente. ¡Los resultados no han cambiado! También usé el tiempo para verificar mis resultados.

Actualización 2
La muestra de ANSI C cambió: la condición del bucle while no llama a más feof (), en cambio moví fread () a la condición. Parece que, el código corre ahora 10,000 relojes más rápido.

Medida cambiada: los resultados anteriores siempre estaban almacenados en búfer, porque repetí la antigua línea de comando rm para.ogv && sync && time ./program para cada programa unas cuantas veces. Ahora reinicio el sistema para cada programa. Los resultados no amortiguados son nuevos y no muestran sorpresas. Los resultados no amortiguados no cambiaron realmente.

Si no borro la copia antigua, los programas reaccionan de manera diferente. Sobrescribir un archivo existente almacenado en búfer es más rápido con POSIX y SENDFILE, todos los demás programas son más lentos. Tal vez las opciones truncadas o creadas tengan un impacto en este comportamiento. Pero sobrescribir los archivos existentes con la misma copia no es un caso de uso del mundo real.

Realizar la copia con cp toma 0.44 segundos sin búfer y 0.30 segundos en búfer. Así que cp es un poco más lento que el de POSIX. Se ve bien para mi

Quizás añada también muestras y resultados de mmap () y copy_file() de boost :: filesystem.

Actualización 3
He puesto esto también en una página de blog y lo extendí un poco. Incluyendo splice () , que es una función de bajo nivel del kernel de Linux. Tal vez más muestras con Java seguirán. http://www.ttyhoney.com/blog/?page_id=69


Con C ++ 17, la forma estándar de copiar un archivo será incluir el encabezado <filesystem> y usar:

bool copy_file( const std::filesystem::path& from, const std::filesystem::path& to); bool copy_file( const std::filesystem::path& from, const std::filesystem::path& to, std::filesystem::copy_options options);

La primera forma es equivalente a la segunda con copy_options::none usado como opciones (vea también copy_file ).

La biblioteca del filesystem se desarrolló originalmente como boost.filesystem y finalmente se fusionó con ISO C ++ a partir de C ++ 17.


Copia un archivo de una manera sana:

#include <fstream> int main() { std::ifstream src("from.ogv", std::ios::binary); std::ofstream dst("to.ogv", std::ios::binary); dst << src.rdbuf(); }

Esto es tan simple e intuitivo de leer que vale la pena el costo adicional. Si lo estuviéramos haciendo mucho, mejor recurrir a las llamadas del sistema operativo al sistema de archivos. Estoy seguro de que boost tiene un método de copia de archivo en su clase de sistema de archivos.

Existe un método en C para interactuar con el sistema de archivos:

#include <copyfile.h> int copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);


Qt tiene un método para copiar archivos:

#include <QFile> QFile::copy("originalFile.example","copiedFile.example");

Tenga en cuenta que para usar esto debe instalar Qt (instrucciones here ) e incluirlo en su proyecto (si está usando Windows y no es un administrador, puede descargar Qt here ). También vea esta respuesta .


Quiero hacer la nota muy importante de que el método LINUX que usa sendfile () tiene un problema importante, ya que no puede copiar archivos de más de 2GB de tamaño. Lo había implementado siguiendo esta pregunta y tenía problemas porque lo estaba usando para copiar archivos HDF5 que tenían muchos GB de tamaño.

http://man7.org/linux/man-pages/man2/sendfile.2.html

sendfile () transferirá a lo sumo 0x7ffff000 (2,147,479,552) bytes, devolviendo el número de bytes realmente transferidos. (Esto es cierto tanto en sistemas de 32 bits como de 64 bits).