ofstream::operator<< (streambuf) - медленный способ копирования файла

Мне нужен кроссплатформенный, без внешней библиотеки, способ копирования файла. В моем первом проходе я придумал (обработка ошибок опущена):

char buffer[LEN];
ifstream src(srcFile, ios::in | ios::binary);
ofstream dest(destFile, ios::out | ios::binary);

while (!src.eof()) {
  src.read(buffer, LEN);
  dest.write(buffer, src.gcount());
}

Это работало хорошо, и я точно знал, что он делал.

Затем я нашел сообщение о stackru (извините, не могу найти ссылку прямо сейчас), в котором говорится, что я могу заменить весь приведенный выше код на:

dest << src.rdbuf();

Что приятно и компактно, но многое скрывает в том, что он делает. Это также оказывается очень медленным, потому что реализация ofstream::operator<< (streambuf) перемещает вещи по 1 символу за раз (используя snetxc () / sputc ()).

Есть ли способ сделать этот метод быстрее? Есть ли недостаток у моего оригинального метода?

Обновление: есть что-то неэффективное в использовании оператора<< (streambuf) в Windows. Цикл.read () /. Write () всегда работает лучше, чем оператор <<.

Кроме того, изменение размера буфера в приведенном выше коде не влияет на размер операций чтения и записи на жесткий диск. Для этого вам нужно установить буферы с помощью stream.rdbuf()->pubsetbuf().

3 ответа

Решение

Интересно, если ваш fstream небуферизован по умолчанию. GCC 4.5.2 по умолчанию использует внутренний буфер, но я не думаю, что этого требует стандарт. Вы пытались использовать pubsetbuf (см. Ниже), чтобы установить буфер для ваших входных / выходных потоков.

Быстрый тест в моей системе, если я установил LEN на 0 (и, следовательно, небуферизованный), потребовалось 10 секунд, чтобы скопировать файл размером 1 МБ. С буфером 4 КБ это завершилось менее чем за секунду.

#include <iostream>
#include <fstream>

int main() {
  using namespace std;
  const char* srcFile = "test.in";
  const char* destFile = "test.out";

  ifstream src;
  ofstream dest;

  const int LEN=8192;
  char buffer_out[LEN];
  char buffer_in[LEN];
  if (LEN) {
    src.rdbuf()->pubsetbuf(buffer_in, LEN );
    dest.rdbuf()->pubsetbuf(buffer_out, LEN);
  } else {
    src.rdbuf()->pubsetbuf(NULL, 0 );
    dest.rdbuf()->pubsetbuf(NULL, 0);
  }
  src.open(srcFile, ios::in | ios::binary);
  dest.open(destFile, ios::out | ios::binary);
  dest << src.rdbuf();

}

Попробуйте вместо этого использовать API-интерфейс C stdio, он часто может быть быстрее во многих реализациях (см. Этот поток для некоторых чисел), хотя и не всегда. Например:

// Error checking omitted for expository purposes
char buffer[LEN];
FILE *src = fopen(srcFile, "rb");
FILE *dest = fopen(destFile, "wb");

int n;
while ((n = fread(buffer, 1, LEN, src)) > 0)
{
    fwrite(buffer, 1, n, dest);
}

fclose(src);
fclose(dest);

Конечно src.rdbuf метод медленнее. Он делает чтение и запись одновременно. Если вы не копируете на другой жесткий диск или в другую форму сети или подключенного хранилища, это будет медленнее, чем чтение блока, а затем запись блока.

Тот факт, что код компактен, не делает его быстрее.


Поскольку вы не можете перегрузить operator<< для std::filebuf (так как он уже перегружен), вы ничего не можете сделать. Лучше просто использовать метод, который работает достаточно хорошо.

Другие вопросы по тегам