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
fstream
jelas merupakan pilihan yang baik untuk operasi file.#include <copyfile.h> copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
Jawaban:
Salin file dengan cara yang waras:
Ini sangat sederhana dan intuitif untuk membacanya sepadan dengan biaya tambahan. Jika kita sering melakukannya, lebih baik kembali menggunakan panggilan OS ke sistem file. Saya yakin
boost
memiliki metode menyalin file di kelas sistem file-nya.Ada metode C untuk berinteraksi dengan sistem file:
sumber
copyfile
tidak portabel; Saya pikir ini khusus untuk Mac OS X. Itu pasti tidak ada di Linux.boost::filesystem::copy_file
mungkin merupakan cara paling portabel untuk menyalin file melalui sistem file asli.Dengan C ++ 17 cara standar untuk menyalin file akan termasuk
<filesystem>
header dan menggunakan:Bentuk pertama setara dengan yang kedua dengan
copy_options::none
digunakan sebagai opsi (lihat jugacopy_file
).The
filesystem
perpustakaan pada awalnya dikembangkan sebagaiboost.filesystem
dan akhirnya bergabung untuk ISO C ++ sebagai C ++ 17.sumber
bool copy_file( const std::filesystem::path& from, const std::filesystem::path& to, std::filesystem::copy_options options = std::filesystem::copy_options::none);
?Terlalu banyak!
Buffer cara "ANSI C" berlebihan, karena a
FILE
sudah buffered. (Ukuran buffer internal ini adalah apa yangBUFSIZ
sebenarnya mendefinisikan.)"SENDIRI-BUFFER-C ++ - WAY" akan lambat saat berjalan
fstream
, yang melakukan banyak pengiriman virtual, dan sekali lagi mempertahankan buffer internal atau setiap objek stream. (The "COPY-ALGORITHM-C ++ - WAY" tidak menderita ini, karenastreambuf_iterator
kelas melewati layer stream.)Saya lebih suka "COPY-ALGORITHM-C ++ - WAY", tetapi tanpa membuat sebuah
fstream
, cukup buatstd::filebuf
contoh kosong saat tidak diperlukan pemformatan yang sebenarnya.Untuk kinerja mentah, Anda tidak dapat mengalahkan deskriptor file POSIX. Ini jelek tapi portabel dan cepat pada platform apa pun.
Cara Linux tampaknya sangat cepat - mungkin OS membiarkan fungsinya kembali sebelum I / O selesai? Bagaimanapun, itu tidak cukup portabel untuk banyak aplikasi.
EDIT : Ah, "Linux asli" dapat meningkatkan kinerja dengan interleaving membaca dan menulis dengan I / O yang tidak sinkron. Membiarkan perintah menumpuk dapat membantu driver disk memutuskan kapan yang terbaik untuk dicari. Anda dapat mencoba Boost Asio atau pthreads untuk perbandingan. Adapun "tidak dapat mengalahkan deskriptor file POSIX" ... yah itu benar jika Anda melakukan sesuatu dengan data, tidak hanya menyalin secara membabi buta.
sumber
sendfile()
Menyalin data antara satu deskriptor file dan yang lain. Karena penyalinan ini dilakukan di dalam kernel,sendfile()
lebih efisien daripada kombinasi dariread(2)
danwrite(2)
, yang akan memerlukan transfer data ke dan dari ruang pengguna.": kernel.org/doc/man-pages /online/pages/man2/sendfile.2.htmlfilebuf
objek mentah ?Saya ingin membuat catatan yang sangat penting bahwa metode LINUX menggunakan sendfile () memiliki masalah besar karena tidak dapat menyalin file yang berukuran lebih dari 2GB! Saya telah menerapkannya setelah pertanyaan ini dan sedang mengalami masalah karena saya menggunakannya untuk menyalin file HDF5 yang berukuran banyak GB.
http://man7.org/linux/man-pages/man2/sendfile.2.html
sumber
off64_t
memungkinkan seseorang untuk menggunakan loop untuk menyalin file besar seperti yang ditunjukkan dalam jawaban untuk pertanyaan terkait.Qt memiliki metode untuk menyalin file:
Perhatikan bahwa untuk menggunakan ini Anda harus menginstal Qt (instruksi di sini ) dan memasukkannya ke dalam proyek Anda (jika Anda menggunakan Windows dan Anda bukan administrator, Anda dapat mengunduh Qt di sini ). Lihat juga jawaban ini .
sumber
QFile::copy
sangat lambat karena itu buffering 4k .Qt
. Saya menggunakan5.9.2
dan kecepatannya setara dengan implementasi asli. Btw. melihat kode sumber, Qt tampaknya benar-benar memanggil implementasi asli.Bagi mereka yang suka meningkatkan:
Perhatikan bahwa boost :: filesystem :: path juga tersedia sebagai wpath untuk Unicode. Dan itu juga bisa Anda gunakan
jika Anda tidak suka nama-nama jenis panjang
sumber
Saya tidak yakin apa "cara yang baik" untuk menyalin file, tetapi dengan asumsi "baik" berarti "cepat", saya bisa sedikit memperluas subjek.
Sistem operasi saat ini telah lama dioptimalkan untuk menangani menjalankan salinan file pabrik. Tidak ada kode sedikit pun yang akan mengalahkan itu. Ada kemungkinan bahwa beberapa varian teknik penyalinan Anda akan terbukti lebih cepat dalam beberapa skenario pengujian, tetapi kemungkinan besar itu akan lebih buruk dalam kasus lain.
Biasanya,
sendfile
fungsi tersebut mungkin kembali sebelum penulisan dilakukan, sehingga memberi kesan lebih cepat daripada yang lain. Saya belum membaca kode, tetapi pasti karena ia mengalokasikan buffer khusus, memori perdagangan untuk waktu. Dan alasan mengapa itu tidak akan berfungsi untuk file yang lebih besar dari 2Gb.Selama Anda berurusan dengan sejumlah kecil file, semuanya terjadi di dalam berbagai buffer (runtime C ++ pertama jika Anda gunakan
iostream
, yang internal OS, tampaknya buffer tambahan berukuran file dalam kasussendfile
). Media penyimpanan aktual hanya diakses setelah data yang cukup telah dipindahkan sehingga layak untuk kesulitan memutar hard disk.Saya kira Anda dapat sedikit meningkatkan kinerja dalam kasus-kasus tertentu. Dari atas kepala saya:
copy_file
secara berurutan (meskipun Anda tidak akan melihat perbedaannya selama file tersebut sesuai dengan cache OS)Namun semua itu berada di luar ruang lingkup fungsi penyalinan file tujuan umum.
Jadi menurut pendapat programmer berpengalaman saya, salinan file C ++ harus hanya menggunakan
file_copy
fungsi khusus C ++ 17 , kecuali lebih banyak diketahui tentang konteks di mana salinan file terjadi dan beberapa strategi pintar dapat dirancang untuk mengakali OS.sumber