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 добился определенного прогресса в этой области.