Почему копирование файла на C происходит намного быстрее, чем С++?

Я работаю над довольно большим проектом на С++ уже несколько недель. Моя первоначальная цель состояла в том, чтобы использовать этот проект, чтобы узнать о С++ 11 и использовать только чистый код на С++ и избегать ручного распределения и конструкций C. Однако я думаю, что эта проблема заставит меня использовать C для небольшой функции, и я хотел бы знать, почему.

В принципе у меня есть функция сохранения, которая копирует несколько большой двоичный файл в отдельное место, прежде чем я вношу изменения в данные в нем. Сами файлы - это изображения компакт-дисков с максимальным размером около 700 МБ. Вот исходный код С++, который я использовал:

std::ios::sync_with_stdio(false);

std::ifstream in(infile, std::ios::binary);
std::ofstream out(outfile, std::ios::binary);

std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(out));

out.close();
in.close();

Этот код при использовании с файлом 690 МБ занимает чуть менее 4 минут. Я запустил его с несколькими файлами, и это всегда тот же результат; ничего меньше 3 минут. Тем не менее, я также нашел следующий способ, который выполнялся немного быстрее, но все же нигде не так быстро, как C:

std::ios::sync_with_stdio(false);

std::ifstream in(infile, std::ios::binary);
std::ofstream out(outfile, std::ios::binary);

out << in.rdbuf();

out.close();
in.close();

Это заняло 24 секунды, но все равно примерно в 20 раз медленнее C.

Осмотревшись, я обнаружил, что кому-то нужно написать файл размером 80 ГБ и увидеть, что он может писать на полной скорости с помощью C. Я решил попробовать с этим кодом:

FILE *in = fopen(infile, "rb");
FILE *out = fopen(outfile, "wb");

char buf[1024];
int read = 0;

//  Read data in 1kb chunks and write to output file
while ((read = fread(buf, 1, 1024, in)) == 1024)
{
  fwrite(buf, 1, 1024, out);
}

//  If there is any data left over write it out
fwrite(buf, 1, read, out);

fclose(out);
fclose(in);

Результаты были довольно шокирующими. Вот один из тестов, которые у меня есть после многократного запуска нескольких файлов:

File Size: 565,371,408 bytes
C  :   1.539s | 350.345 MB/s
C++:  24.754s | 21.7815 MB/s - out << in.rdbuf()
C++: 220.555s | 2.44465 MB/s - std::copy()

В чем причина этой огромной разницы? Я знаю, что С++ не будет соответствовать производительности простого C, но разница в 348 МБ/с огромна. Что-то мне не хватает?

Edit:

Я компилирую это с помощью Visual Studio 2013 в 64-разрядной ОС Windows 8.1.

Изменить 2:

После прочтения ответа Джона Звинка я решил просто перейти на конкретный маршрут платформы. Поскольку я все еще хотел сделать свою кросс-платформу проекта, я собрал быстрый пример. Я действительно не уверен, работают ли они в других системах помимо Windows, но я могу проверить Linux на более поздний срок. Я не могу проверить OSX, но я думаю, что copyfile выглядит как простая функция, поэтому я предполагаю, что это правильно.

Имейте в виду, что вам нужно сделать одну и ту же логику #ifdef для включения заголовков конкретной платформы.

void copy(std::string infile, std::string outfile)
{
#ifdef _WIN32 || _WIN64
  //  Windows
  CopyFileA(infile.c_str(), outfile.c_str(), false);
#elif __APPLE__
  //  OSX
  copyfile(infile.c_str(), outfile.c_str(), NULL, COPYFILE_DATA);
#elif __linux
  //  Linux
  struct stat stat_buf;
  int in_fd, out_fd;
  offset_t offset = 0;

  in_fd = open(infile.c_str(), O_RDONLY);
  fstat(in_fd, &stat_buf);
  out_fd = open(outfile.c_str(), O_WRONLY | O_CREAT, stat_buf.st_mode);

  sendfile(out_fd, in_fd, &offset, stat_buf.st_size);

  close(out_fd);
  close(in_fd);
#endif
}

Ответ 1

Во-первых, вы должны также сравнить с копированием того же файла с помощью CLI на том же компьютере.

Во-вторых, если вам нужна максимальная производительность, вам нужно использовать API-интерфейс для платформы. В Windows, которая, вероятно, CopyFile/CopyFileEx, на Mac OS это copyfile, а в Linux - sendfile. Некоторые из них (определенно sendfile) предлагают производительность, которая не может быть достигнута с использованием основного переносного материала на C или С++. Некоторые из них (CopyFileEx и copyfile) предлагают дополнительные функции, такие как копирование атрибутов файловой системы и необязательные обратные вызовы.

Вы можете увидеть некоторые контрольные показатели, показывающие, насколько быстрее может быть файл sendfile: Скопировать файл разумным, безопасным и эффективным способом

Наконец, грустно, но верно, что С++ iostreams не так быстро, как C файл ввода-вывода на многих платформах. Если вы заботитесь о производительности, вам может быть лучше использовать функции C. Я столкнулся с этим, когда делаю конкурсы программирования, в которых важна скорость выполнения: использование scanf и printf вместо cin и cout имеет большое значение для многих систем.