【问题标题】:QT - QFile copy operation extremely slowQT - QFile 复制操作极慢
【发布时间】:2017-01-21 23:17:27
【问题描述】:

我正在开发一个应用程序,它需要使用 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

我的 c++ 有点生疏了,但我不明白如何仅仅更改数组的分配大小就可以完全破坏方法...任何人都可以提供帮助:

1) 告诉我为什么 QFile:Copy() 这么慢(我错过了什么吗?它不仅在我的 PC 上,在几台不同的机器上测试过)。罪魁祸首实际上是我在上面发布的代码还是完全是其他东西? 2) 告诉我为什么一个更改完全破坏了 QFile

【问题讨论】:

  • 在 qtbase (tests/benchmarks/corelib/io/qfile) 中有一个基准测试,它尝试使用不同的块大小在 Win32 上读取文件。我不确定为什么普遍选择 4K。也许这取决于硬盘技术?您可以尝试运行基准测试(readBigFile_Win32 测试功能)并检查吗?
  • 在 Windows 上,最好的办法是使用 CopyFileEx,请参阅带有进度指示的 this complete example :)

标签: c++ qt qfile


【解决方案1】:

对于较新版本的 Qt(我使用的是 5.9.2),这似乎不再是问题。请查看QFileSystemEngine::copyFile() 中的https://code.woboq.org/qt5/qtbase/src/corelib/io/qfilesystemengine_win.cpp.html 代码使用本机函数CopyFile2。此外,我的测试证实 QFile::copy() 与 Windows 上的本机实现相当。似乎 Qt 在这方面取得了一些进展。

【讨论】:

    【解决方案2】:

    好吧,更改缓冲区大小并没有什么好处,因为这显然只是派生函数engine()-&gt;copy() 失败的后备方案。我不确切知道该函数是如何工作的,也不想浪费时间修改核心 QT 引擎类以使其工作。

    最后,由于我的项目只应该在 Windows 上运行,我最终使用了本机 Win32 复制功能。所以我把我的电话改为:

    QFile::copy(src, dest);
    

    与:

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

    请注意,您必须#include "windows.h" 才能使此调用生效。

    【讨论】:

    • 也看看我的回答。似乎使用较新版本的Qt 框架QFile::copy() 与本机实现一样快。
    【解决方案3】:

    您的更改破坏了 QFile 的原因是 4M 缓冲区不适合堆栈(默认堆栈大小通常类似于 1M)。一个快速的解决方法是:

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

    向量将在堆上分配大缓冲区(并在完成后处理释放),您只需将 block 指向向量的前面。

    我认为您对复制速度慢的原因的分析是正确的。

    【讨论】:

    • 谢谢,我也想过,但没想到栈居然这么小,等QT源码重建后试试。可能需要一段时间 :) 至于原因:可能是这样,但我也注意到该代码仅在“d->engine()->copy(newName)”失败时运行。这也需要检查。
    猜你喜欢
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 2013-07-10
    相关资源
    最近更新 更多