Я ищу хороший способ скопировать файл (двоичный или текстовый). Я написал несколько образцов, каждый работает. Но я хочу услышать мнение опытных программистов.
Я упустил хорошие примеры и ищу способ, который работает с C ++.
ANSI-C ПУТЬ
#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-АЛГОРИТМ-C ++ - ПУТЬ
#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 ++ - ПУТЬ
#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 (многопользовательский, сеть, терминал, без графического интерфейса)
- INTEL SSD-Postville 80 ГБ, заполненный до 50%
- Скопируйте OGG-видеофайл на 270 МБ
Действия по воспроизведению
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
Результаты (время процессора используется)
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 to.ogv && sync && time ./program для каждой программы несколько раз. Теперь я перезагружаю систему для каждой программы. Небуферизованные результаты являются новыми и не вызывают удивления. Небуферизованные результаты не изменились.
Если я не удаляю старую копию, программы реагируют по-разному. Перезапись существующего буферизованного файла выполняется быстрее с помощью POSIX и SENDFILE, все остальные программы работают медленнее. Возможно, параметры усечения или создания влияют на это поведение. Но перезапись существующих файлов одной и той же копией не является реальным вариантом использования.
Выполнение копирования с помощью cp занимает 0,44 секунды без буферизации и 0,30 секунды с буферизацией. Таким образом, cp немного медленнее, чем образец POSIX. Выглядит хорошо для меня.
Может быть, я добавлю также примеры и результаты mmap () и copy_file()
из boost :: filesystem.
Обновление 3
Я также разместил это на странице блога и немного расширил. Включая splice () , которая является функцией низкого уровня из ядра Linux. Может быть, последует больше примеров с Java.
http://www.ttyhoney.com/blog/?page_id=69
fstream
безусловно, хороший вариант для файловых операций.#include <copyfile.h> copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
Ответы:
Скопируйте файл разумным способом:
Это так просто и интуитивно понятно, что стоит дополнительных затрат. Если бы мы делали это много, лучше прибегнуть к обращениям ОС к файловой системе. Я уверен, что
boost
есть метод копирования файла в своем классе файловой системы.Существует метод C для взаимодействия с файловой системой:
источник
copyfile
не переносимый; Я думаю, что это специфично для Mac OS X. Это, безусловно, не существует в Linux.boost::filesystem::copy_file
это, вероятно, самый переносимый способ копирования файла через собственную файловую систему.В C ++ 17 стандартный способ скопировать файл будет включать
<filesystem>
заголовок и использование:Первая форма эквивалентна второй с
copy_options::none
использованием в качестве параметров (см. Такжеcopy_file
).filesystem
Библиотека была первоначально разработана какboost.filesystem
и , наконец , объединились в ISO C ++ от C ++ 17.источник
bool copy_file( const std::filesystem::path& from, const std::filesystem::path& to, std::filesystem::copy_options options = std::filesystem::copy_options::none);
?Слишком много!
Буфер пути «ANSI C» является избыточным, поскольку a
FILE
уже буферизован. (Размер этого внутреннего буфера - то, чтоBUFSIZ
фактически определяет.)«OWN-BUFFER-C ++ - WAY» будет медленным во время прохождения
fstream
, которое выполняет большую часть виртуальной диспетчеризации и снова поддерживает внутренние буферы или каждый объект потока. («COPY-ALGORITHM-C ++ - WAY» не страдает этим, посколькуstreambuf_iterator
класс обходит уровень потока.)Я предпочитаю «COPY-ALGORITHM-C ++ - WAY», но не
fstream
создавая, просто создайте голыеstd::filebuf
экземпляры, когда фактическое форматирование не требуется.Для повышения производительности вы не можете превзойти дескрипторы файлов POSIX. Это некрасиво, но портативно и быстро на любой платформе.
Путь Linux кажется невероятно быстрым - возможно, ОС позволила функции вернуться до завершения ввода-вывода? В любом случае, этого недостаточно для многих приложений.
РЕДАКТИРОВАТЬ : Ах, "родной Linux" может улучшить производительность, чередуя чтения и записи с асинхронным вводом-выводом. Скопление команд может помочь драйверу диска решить, когда лучше искать. Вы можете попробовать Boost Asio или pthreads для сравнения. Что касается «не может превзойти дескрипторы файлов POSIX»… что ж, это правда, если вы что-то делаете с данными, а не просто слепо копируете.
источник
sendfile()
Копирует данные между одним файловым дескриптором и другим. Поскольку это копирование выполняется в ядре,sendfile()
оно более эффективно, чем комбинацияread(2)
иwrite(2)
, что потребовало бы передачи данных в пространство пользователя и из него.»: kernel.org/doc/man-pages /online/pages/man2/sendfile.2.htmlfilebuf
объектов?Я хочу сделать очень важное замечание, что метод LINUX, использующий sendfile (), имеет большую проблему в том, что он не может копировать файлы размером более 2 ГБ! Я реализовал его после этого вопроса и столкнулся с проблемами, потому что использовал его для копирования файлов HDF5, размер которых был много ГБ.
http://man7.org/linux/man-pages/man2/sendfile.2.html
источник
off64_t
позволяет использовать цикл для копирования больших файлов, как показано в ответе на связанный вопрос.Qt имеет метод для копирования файлов:
Обратите внимание, что для его использования вы должны установить Qt (инструкции здесь ) и включить его в свой проект (если вы используете Windows и не являетесь администратором, вы можете скачать Qt здесь ). Также смотрите этот ответ .
источник
QFile::copy
Смехотворно медленный из-за 4К буферизации .Qt
. Я использую5.9.2
и скорость находится на одном уровне с родной реализацией. Btw. Взглянув на исходный код, Qt, кажется, фактически вызывает нативную реализацию.Для тех, кто любит повышение:
Обратите внимание, что boost :: filesystem :: path также доступен как wpath для Unicode. И что вы могли бы также использовать
если вам не нравятся эти длинные имена
источник
Я не совсем уверен, что такое «хороший способ» копирования файла, но предполагая, что «хороший» означает «быстрый», я мог бы немного расширить тему.
Современные операционные системы уже давно оптимизированы для работы с копией обычного файла. Никакой умный кусок кода не побьет это. Возможно, что некоторые варианты ваших методов копирования окажутся быстрее в некотором тестовом сценарии, но они, скорее всего, будут хуже в других случаях.
Как правило,
sendfile
функция, вероятно, возвращается до фиксации записи, создавая впечатление, что она быстрее, чем остальные. Я не читал код, но это, безусловно, потому что он выделяет свой собственный выделенный буфер, торгуя память на время. И причина, почему это не будет работать для файлов больше, чем 2 ГБ.Пока вы имеете дело с небольшим количеством файлов, все происходит внутри различных буферов (в первую очередь, если вы используете среду выполнения C ++
iostream
, это внутренние операционные системы, по-видимому, дополнительный буфер размером с файл в случаеsendfile
). К фактическим носителям доступа можно получить доступ только после того, как будет перемещено достаточное количество данных, чтобы стоить проблемы вращения жесткого диска.Я полагаю, вы могли бы немного улучшить производительность в конкретных случаях. С верхней части моей головы:
copy_file
откроете три файла одновременно, чем вызовете два последовательных (хотя вы вряд ли заметите разницу, если файл помещается в кэш ОС)Но все это выходит за рамки функции копирования файлов общего назначения.
Так что, по мнению моего, возможно, опытного программиста, копия файла C ++ должна просто использовать
file_copy
специальную функцию C ++ 17 , если не известно больше о контексте, в котором происходит копирование файла, и могут быть разработаны некоторые умные стратегии, чтобы перехитрить ОС.источник