ฉันค้นหาวิธีที่ดีในการคัดลอกไฟล์ (ไบนารีหรือข้อความ) ฉันเขียนหลายตัวอย่างทุกคนทำงานได้ แต่ฉันต้องการฟังความคิดเห็นของโปรแกรมเมอร์ที่มีประสบการณ์
ฉันพลาดตัวอย่างที่ดีและค้นหาวิธีการทำงานกับ 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 ใช้สิ่งนี้ใน "ภาษาการเขียนโปรแกรม C", ระดับต่ำมากขึ้น)
#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;
}
คัดลอกขั้นตอนวิธีการ c ++ - WAY
#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;
}
ตัวเอง BUFFER-C ++ - 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);
// 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 // ต้องการเคอร์เนล> = 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;
}
สิ่งแวดล้อม
- GNU / LINUX (Archlinux)
- เคอร์เนล 3.3
- GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
- ใช้ RUNLEVEL 3 (ผู้ใช้หลายคน, เครือข่าย, เทอร์มินัล, ไม่มี GUI)
- INTEL SSD-Postville 80 GB เติมได้สูงสุด 50%
- คัดลอก OGG-VIDEO-FILE ขนาด 270 MB
ขั้นตอนในการทำซ้ำ
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
ผลลัพธ์ (ใช้ CPU TIME)
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
ขนาดไฟล์ไม่เปลี่ยนแปลง
sha256sum พิมพ์ผลลัพธ์เดียวกัน
ไฟล์วิดีโอยังคงเล่นได้
คำถาม
- คุณต้องการวิธีใด
- คุณรู้วิธีแก้ปัญหาที่ดีกว่าหรือไม่?
- คุณเห็นข้อผิดพลาดในรหัสของฉันหรือไม่?
คุณรู้เหตุผลในการหลีกเลี่ยงทางออกหรือไม่?
FSTREAM (KISS, Streambuffer)
ฉันชอบอันนี้มากเพราะมันสั้นและเรียบง่าย เท่าที่ฉันรู้ตัวดำเนินการ << ถูกโอเวอร์โหลดสำหรับ rdbuf () และไม่แปลงอะไรเลย แก้ไข?
ขอบคุณ
ปรับปรุง 1
ผมเปลี่ยนแหล่งที่มาในทุกตัวอย่างในทางที่ว่าเปิดและปิดอธิบายไฟล์คือรวมไว้ในวัดของนาฬิกา () ไม่มีการเปลี่ยนแปลงที่สำคัญอื่น ๆ ในซอร์สโค้ด ผลลัพธ์จะไม่เปลี่ยนแปลง! ฉันใช้เวลาในการตรวจสอบผลลัพธ์อีกครั้ง
อัปเดต 2
ANSI C ตัวอย่างเปลี่ยนแปลง: เงื่อนไขของwhile-loopไม่ได้เรียกfeof ()อีกต่อไปแทนที่จะฉันย้ายfread ()เข้าสู่เงื่อนไข ดูเหมือนว่าโค้ดจะรัน 10,000 นาฬิกาเร็วขึ้น
การวัดมีการเปลี่ยนแปลง: ผลลัพธ์ในอดีตถูกบัฟเฟอร์เสมอเพราะฉันทำซ้ำบรรทัดคำสั่งเก่าrm to.ogv && sync && time ./programสำหรับแต่ละโปรแกรมสองสามครั้ง ตอนนี้ฉันรีบูตระบบสำหรับทุกโปรแกรม ผลลัพธ์ที่ไม่มีข้อผิดพลาดเป็นเรื่องใหม่และไม่แปลกใจเลย ผลลัพธ์ที่ไม่มีข้อผิดพลาดไม่ได้เปลี่ยนแปลงอย่างแท้จริง
หากฉันไม่ลบสำเนาเก่าโปรแกรมจะตอบสนองต่างกัน การเขียนทับไฟล์ที่บัฟเฟอร์ที่มีอยู่นั้นเร็วขึ้นด้วย POSIX และ SENDFILE โปรแกรมอื่น ๆ ทั้งหมดจะช้าลง บางทีตัวเลือกตัดหรือสร้างอาจมีผลกระทบต่อพฤติกรรมนี้ แต่การเขียนทับไฟล์ที่มีอยู่ด้วยสำเนาเดียวกันไม่ใช่กรณีการใช้งานจริง
การทำสำเนาด้วยcpจะใช้เวลาบัฟเฟอร์ 0.44 วินาทีและบัฟเฟอร์ไม่ถูกบัฟเฟอร์ 0.30 วินาที ดังนั้นซีพีเป็นเล็กน้อยช้ากว่าตัวอย่าง POSIX ดูดีสำหรับฉัน
บางทีฉันอาจเพิ่มตัวอย่างและผลลัพธ์ของmmap ()และcopy_file()
จาก boost :: filesystem
อัปเดต 3
ฉันใส่สิ่งนี้ลงในหน้าบล็อกและขยายออกไปเล็กน้อย รวมถึงsplice ()ซึ่งเป็นฟังก์ชั่นระดับต่ำจากเคอร์เนล Linux บางทีตัวอย่างเพิ่มเติมกับ 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
เป็นตัวเลือกที่ดีสำหรับการทำงานของไฟล์