QT - операция копирования QFile чрезвычайно медленная

Я разрабатываю приложение, которое должно копировать множество файлов из одной папки в другую, используя QT (5.6.1)

Для этого я использую метод QFile::copy(). Это хорошо работает, за исключением одного: он чрезвычайно медленный. Выполняется более чем в два раза больше времени, когда одна и та же операция копирования выполняется с помощью обозревателя Windows.

Удивляясь, почему это было, я ворвался в исходный код QT, и нашел это в qfile.cpp, что выглядит актуальным:

char block[4096];
qint64 totalRead = 0;
while(!atEnd()) {
    qint64 in = read(block, sizeof(block));
    if (in <= 0)
        break;
    totalRead += in;
    if(in != out.write(block, in)) {
        close();
        d->setError(QFile::CopyError, tr("Failure to write block"));
        error = true;
        break;
    }
}

Итак, из того, что я понимаю, операция копирования использует 4096-байтовый буфер. Это очень мало для операции копирования и вполне может быть причиной проблемы. Так что я сделал, изменил размер буфера на:

char block[4194304]; // 4MB buffer

Затем я перестроил всю библиотеку QT, чтобы включить это изменение. Однако вся модификация полностью нарушила метод. Теперь, когда мое приложение пытается вызвать QFile:: Copy(), операция немедленно прерывается (метод даже не запускается, останавливается перед первой строкой в ​​соответствии с отладчиком QtCreator). Отладчик говорит мне:

The inferior stopped because it received a signal from the Operating System.

Signal name :
SIGSEGV
Signal meaning :
Segmentation fault

Мой С++ немного ржавый, но я не понимаю, как просто изменить размер выделения массива может полностью нарушить метод... может ли кто-нибудь помочь либо:

1) Расскажите, почему QFile: Copy() настолько медленный (я что-то упускаю? Это не только на моем ПК, протестированном на нескольких разных машинах). И действительно ли коллаптик - это код, который я написал выше или что-то еще? 2) Расскажите, почему это одно изменение полностью нарушает QFile

Ответ 1

Хорошо, изменение размера буфера не принесло пользы, так как это, по-видимому, является просто резервным в случае, если производная функция engine()->copy() терпит неудачу. Я не знаю точно, как работает эта функция, и я не хочу тратить время на изменение основных классов ядра QT, чтобы сделать эту работу.

В конце концов, поскольку мой проект должен был запускаться только в Windows, я в конечном итоге использовал собственную функцию копирования Win32. Поэтому я заменил свой вызов на:

QFile::copy(src, dest);

с:

CopyFileExW((LPCWSTR)src.utf16(), (LPCWSTR)dest.utf16(), 0, this, 0, 0);

Обратите внимание, что для этого вызова вам необходимо #include "windows.h".

Ответ 2

Причина, по которой ваше изменение нарушило QFile, заключается в том, что буфер 4M не будет помещаться в стек (размер стека по умолчанию обычно составляет примерно 1M). Быстрое исправление:

std::vector<char> vec(4*1024*1024);
char *block = &vec.front();

Вектор выделит большой буфер в куче (и позаботится об освобождении от работы, когда вы закончите), и просто укажите block в начале вектора.

Я думаю, что ваш анализ того, почему копия медленная, находится на месте.

Ответ 3

Это больше не проблема с более новой версией Qt (я использую 5.9.2). Пожалуйста, посмотрите QFileSystemEngine::copyFile() в https://code.woboq.org/qt5/qtbase/src/corelib/io/qfilesystemengine_win.cpp.html В коде используется встроенная функция CopyFile2. Также мое тестирование подтвердило, что QFile::copy() находится на одном уровне с собственной реализацией в Windows. Кажется, Qt добился определенного прогресса в этой области.