Saya mencari cara yang baik untuk menyalin file (biner atau teks). Saya telah menulis beberapa sampel, semua orang bekerja. Tapi saya ingin mendengar pendapat programmer berpengalaman.
Saya kehilangan contoh yang bagus dan mencari cara yang berfungsi dengan 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 menggunakan ini dalam "Bahasa pemrograman C", level lebih rendah)
#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 ++ - CARA
#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;
}
SENDIRI-BUFFER-C ++ - CARA
#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 // membutuhkan 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;
}
Lingkungan Hidup
- GNU / LINUX (Archlinux)
- Kernel 3.3
- GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
- Menggunakan RUNLEVEL 3 (Multiuser, Jaringan, Terminal, tanpa GUI)
- INTEL SSD-Postville 80 GB, diisi hingga 50%
- Salin 270 MB OGG-VIDEO-FILE
Langkah-langkah mereproduksi
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
Hasil (CPU TIME digunakan)
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
Ukuran file tidak berubah.
sha256sum mencetak hasil yang sama.
File video masih dapat diputar.
Pertanyaan
- Metode apa yang Anda inginkan?
- Apakah Anda tahu solusi yang lebih baik?
- Apakah Anda melihat kesalahan dalam kode saya?
Apakah Anda tahu alasan untuk menghindari solusi?
FSTREAM (KISS, Streambuffer)
Saya sangat suka yang ini, karena sangat pendek dan sederhana. Sejauh yang saya tahu operator << kelebihan beban untuk rdbuf () dan tidak mengubah apa pun. Benar?
Terima kasih
Pembaruan 1
Saya mengubah sumber dalam semua sampel dengan cara itu, bahwa buka dan tutup deskriptor file termasuk dalam pengukuran clock () . Tidak ada perubahan signifikan lainnya dalam kode sumber. Hasilnya tidak berubah! Saya juga menggunakan waktu untuk memeriksa ulang hasil saya.
Pembaruan 2
sampel ANSI C berubah: Kondisi while-loop tidak memanggil feof lagi () alih-alih saya pindah ketakutan () ke dalam kondisi. Sepertinya, kode itu sekarang berjalan 10.000 jam lebih cepat.
Pengukuran berubah: Hasil sebelumnya selalu buffer, karena saya mengulangi baris perintah lama rm to.ogv && sync && time ./program untuk setiap program beberapa kali. Sekarang saya reboot sistem untuk setiap program. Hasil unbuffered adalah baru dan tidak menunjukkan kejutan. Hasil unbuffered tidak benar-benar berubah.
Jika saya tidak menghapus salinan lama, program bereaksi berbeda. Menimpa file yang ada buffered lebih cepat dengan POSIX dan SENDFILE, semua program lain lebih lambat. Mungkin opsi memotong atau membuat berdampak pada perilaku ini. Tetapi menimpa file yang sudah ada dengan salinan yang sama bukan kasus penggunaan dunia nyata.
Melakukan salinan dengan cp membutuhkan 0,44 detik tanpa buffer dan 0,30 detik tanpa buffer. Jadi cp sedikit lebih lambat dari sampel POSIX. Terlihat bagus untukku.
Mungkin saya menambahkan juga sampel dan hasil mmap () dan copy_file()
dari boost :: filesystem.
Perbarui 3
Saya telah menempatkan ini juga di halaman blog dan sedikit diperluas. Termasuk splice () , yang merupakan fungsi tingkat rendah dari kernel Linux. Mungkin lebih banyak sampel dengan Java akan mengikuti.
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
jelas merupakan pilihan yang baik untuk operasi file.