파일 (이진 또는 텍스트)을 복사하는 좋은 방법을 찾고 있습니다. 몇 가지 샘플을 작성했는데 모두 작동합니다. 그러나 노련한 프로그래머의 의견을 듣고 싶습니다.
좋은 예제가 누락되었고 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;
}
COPY-ALGORITHM-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;
}
자신의 버퍼 C ++ 방법
#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 / 리눅스 (Archlinux)
- 커널 3.3
- GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, 코어 유틸리티 8.16
- RUNLEVEL 3 사용 (다중 사용자, 네트워크, 터미널, GUI 없음)
- 인텔 SSD-Postville 80GB, 최대 50 % 채워짐
- 270MB OGG-VIDEO-FILE 복사
재현 단계
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
모든 샘플에서 소스를 변경하여 파일 설명 자의 열기 및 닫기가 clock () 측정에 포함됩니다 . 그것들은 소스 코드에서 다른 중요한 변화가 아닙니다. 결과는 변하지 않습니다! 또한 결과를 다시 확인 하는 데 시간 을 사용 했습니다 .
업데이트 2
ANSI C 샘플이 변경되었습니다. while 루프 의 조건은 더 이상 feof () 를 호출하지 않고 대신 fread () 를 조건으로 이동했습니다 . 코드가 10,000 클럭 더 빠르게 실행되는 것처럼 보입니다.
측정 변경 : 이전 명령 행 rm을 .ogv && sync && time ./program 각 프로그램에 대해 몇 번 반복했기 때문에 이전 결과는 항상 버퍼링 되었습니다. 이제 모든 프로그램에 대해 시스템을 재부팅합니다. 버퍼링되지 않은 결과는 새로운 것이며 놀라운 것은 아닙니다. 버퍼링되지 않은 결과는 실제로 변경되지 않았습니다.
이전 사본을 삭제하지 않으면 프로그램이 다르게 반응합니다. POSIX 및 SENDFILE을 사용하면 버퍼링 된 기존 파일을 덮어 쓰는 것이 빠르며 다른 모든 프로그램은 느려집니다. 옵션이 잘 리거나 생성 될 경우이 동작에 영향을 줄 수 있습니다. 그러나 동일한 사본으로 기존 파일을 덮어 쓰는 것은 실제 사용 사례가 아닙니다.
cp로 복사를 수행하면 버퍼링되지 않은 0.44 초 및 버퍼링 된 0.30 초가 소요됩니다. 따라서 cp 는 POSIX 샘플보다 약간 느립니다. 나를 위해 잘 보인다.
아마도 mmap () 및 copy_file()
boost :: filesystem 의 샘플과 결과도 추가 할 수 있습니다 .
업데이트 3
나는 이것을 블로그 페이지에도 넣고 약간 확장했습니다. Linux 커널의 하위 수준 함수 인 splice () 포함 아마도 자바가 더 많은 샘플이 뒤따를 것입니다.
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
확실히 파일 작업에 좋은 옵션입니다.