Tôi tìm kiếm một cách tốt để sao chép một tập tin (nhị phân hoặc văn bản). Tôi đã viết một số mẫu, mọi người đều làm việc. Nhưng tôi muốn nghe ý kiến của các lập trình viên dày dạn kinh nghiệm.
Tôi thiếu các ví dụ hay và tìm kiếm cách làm việc với C ++.
AN-C-CÁCH
#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 sử dụng điều này trong "Ngôn ngữ lập trình C", mức độ thấp hơn)
#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-CÁCH
#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;
}
SAO CHÉP-ALGORITHM-C ++ - CÁCH
#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;
}
RIÊNG-BUFFER-C ++ - CÁCH
#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 // yêu cầu 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;
}
Môi trường
- GNU / LINUX (Archlinux)
- Hạt nhân 3.3
- GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
- Sử dụng RUNLEVEL 3 (Multiuser, Network, Terminal, không có GUI)
- INTEL SSD-Postville 80 GB, chiếm tới 50%
- Sao chép một tập tin OGG-VIDEO-FILE 270 MB
Các bước để tái sản xuất
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
Kết quả (CPU TIME được sử dụng)
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
Kích thước tập tin không thay đổi.
sha256sum in kết quả tương tự.
Các tập tin video vẫn có thể chơi được.
Câu hỏi
- Bạn thích phương pháp nào?
- Bạn có biết giải pháp tốt hơn?
- Bạn có thấy bất kỳ sai lầm trong mã của tôi?
Bạn có biết một lý do để tránh một giải pháp?
FSTREAM (KISS, Streambuffer)
Tôi thực sự thích cái này, vì nó thực sự ngắn và đơn giản. Theo như tôi biết thì toán tử << bị quá tải cho rdbuf () và không chuyển đổi bất cứ thứ gì. Chính xác?
Cảm ơn
Cập nhật 1
Tôi đã thay đổi nguồn trong tất cả các mẫu theo cách đó, rằng mở và đóng của bộ mô tả tệp được bao gồm trong phép đo đồng hồ () . Chúng không có thay đổi đáng kể nào khác trong mã nguồn. Kết quả không thay đổi! Tôi cũng đã sử dụng thời gian để kiểm tra lại kết quả của mình.
Cập nhật 2
mẫu ANSI C đã thay đổi: Điều kiện của vòng lặp while không gọi bất kỳ lệnh nào nữa () thay vào đó tôi chuyển fread () vào điều kiện. Có vẻ như, mã chạy bây giờ 10.000 đồng hồ nhanh hơn.
Phép đo đã thay đổi: Các kết quả trước đây luôn được đệm, bởi vì tôi đã lặp lại dòng lệnh cũ rm to.ogv && sync && time ./program cho mỗi chương trình một vài lần. Bây giờ tôi khởi động lại hệ thống cho mọi chương trình. Các kết quả không có bộ đệm là mới và không có gì bất ngờ. Các kết quả không có bộ đệm đã không thực sự thay đổi.
Nếu tôi không xóa bản sao cũ, các chương trình sẽ phản ứng khác nhau. Ghi đè một tệp hiện có được đệm nhanh hơn với POSIX và SENDFILE, tất cả các chương trình khác đều chậm hơn. Có thể các tùy chọn cắt bớt hoặc tạo ra có tác động đến hành vi này. Nhưng ghi đè các tệp hiện có cùng một bản sao không phải là trường hợp sử dụng trong thế giới thực.
Thực hiện sao chép với cp mất 0,44 giây không có bộ đệm và 0,30 giây được đệm. Vì vậy, cp chậm hơn một chút so với mẫu POSIX. Có vẻ tốt cho tôi.
Có lẽ tôi cũng thêm các mẫu và kết quả của mmap () và copy_file()
từ boost :: filesystem.
Cập nhật 3
Tôi cũng đã đặt nó trên một trang blog và mở rộng nó ra một chút. Bao gồm splice () , là một hàm cấp thấp từ nhân Linux. Có thể nhiều mẫu với Java sẽ làm theo.
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
chắc chắn là một lựa chọn tốt cho các hoạt động tập tin.