Ich suche nach einer guten Möglichkeit, eine Datei (Binärdatei oder Text) zu kopieren. Ich habe mehrere Beispiele geschrieben, jeder arbeitet. Aber ich möchte die Meinung erfahrener Programmierer hören.
Ich vermisse gute Beispiele und suche einen Weg, der mit C ++ funktioniert.
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 verwendet dies in "The C Programming Language", niedriger)
#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 ++ - WEG
#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 ++ - WEG
#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 // erfordert 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;
}
Umgebung
- GNU / LINUX (Archlinux)
- Kernel 3.3
- GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
- Verwenden von RUNLEVEL 3 (Mehrbenutzer, Netzwerk, Terminal, keine GUI)
- INTEL SSD-Postville 80 GB, gefüllt bis zu 50%
- Kopieren Sie eine 270 MB OGG-VIDEO-DATEI
Schritte zum Reproduzieren
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
Ergebnisse (verwendete CPU-ZEIT)
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
Die Dateigröße ändert sich nicht.
sha256sum druckt die gleichen Ergebnisse.
Die Videodatei kann weiterhin abgespielt werden.
Fragen
- Welche Methode würden Sie bevorzugen?
- Kennen Sie bessere Lösungen?
- Sehen Sie Fehler in meinem Code?
Kennen Sie einen Grund, eine Lösung zu vermeiden?
FSTREAM (KISS, Streambuffer)
Ich mag dieses wirklich, weil es sehr kurz und einfach ist. Soweit ich weiß, ist der Operator << für rdbuf () überladen und konvertiert nichts. Richtig?
Vielen Dank
Update 1
Ich habe die Quelle in allen Beispielen so geändert, dass das Öffnen und Schließen der Dateideskriptoren in die Messung von clock () einbezogen wird . Es gibt keine weiteren wesentlichen Änderungen im Quellcode. Die Ergebnisse haben sich nicht geändert! Ich habe auch Zeit genutzt , um meine Ergebnisse zu überprüfen.
Update 2
ANSI C-Beispiel geändert: Der Zustand der while-Schleife ruft nicht mehr feof () auf, sondern ich habe fread () in den Zustand verschoben . Es sieht so aus, als würde der Code jetzt 10.000 Uhren schneller laufen.
Messung geändert: Die früheren Ergebnisse wurden immer gepuffert, da ich die alte Befehlszeile rm to.ogv && sync && time ./program für jedes Programm einige Male wiederholt habe . Jetzt starte ich das System für jedes Programm neu. Die ungepufferten Ergebnisse sind neu und zeigen keine Überraschung. Die ungepufferten Ergebnisse haben sich nicht wirklich geändert.
Wenn ich die alte Kopie nicht lösche, reagieren die Programme anders. Das Überschreiben einer vorhandenen gepufferten Datei ist mit POSIX und SENDFILE schneller, alle anderen Programme sind langsamer. Möglicherweise wirken sich die abgeschnittenen oder erstellten Optionen auf dieses Verhalten aus. Das Überschreiben vorhandener Dateien mit derselben Kopie ist jedoch kein realer Anwendungsfall.
Das Durchführen der Kopie mit cp dauert 0,44 Sekunden ungepuffert und 0,30 Sekunden gepuffert. So cp ist ein wenig langsamer als die POSIX - Probe. Sieht gut aus für mich.
Vielleicht füge ich auch Beispiele und Ergebnisse von mmap () und copy_file()
von boost :: filesystem hinzu.
Update 3
Ich habe dies auch auf eine Blog-Seite gestellt und ein wenig erweitert. Einschließlich splice () , einer Low-Level-Funktion des Linux-Kernels. Vielleicht folgen weitere Beispiele mit Java.
http://www.ttyhoney.com/blog/?page_id=69
#include <copyfile.h> copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
fstream
ist definitiv eine gute Option für Dateioperationen.