【问题标题】:Copying QFile contents to another QFile, what's the optimal way?将 QFile 内容复制到另一个 QFile,最佳方法是什么?
【发布时间】:2013-08-18 14:58:30
【问题描述】:

我需要将QFile 分块复制到另一个QFile,所以我不能使用QFile::copy。这是最原始的实现:

bool CFile::copyChunk(int64_t chunkSize, const QString &destFolder)
{
    if (!_thisFile.isOpen())
    {
        // Initializing - opening files
        _thisFile.setFileName(_absoluteFilePath);
        if (!_thisFile.open(QFile::ReadOnly))
            return false;

        _destFile.setFileName(destFolder + _thisFileName);
        if (!_destFile.open(QFile::WriteOnly))
            return false;
    }

    if (chunkSize < (_thisFile.size() - _thisFile.pos()))
    {
        QByteArray data (chunkSize, 0);
        _thisFile.read(data.data(), chunkSize);
        return _destFile.write(data) == chunkSize;
    }
}

从这个片段中不清楚,但我只打算将一个二进制文件作为一个整体复制到另一个位置,只是以块的形式,这样我就可以为大文件提供进度回调和取消工具。

另一个想法是使用内存映射。我是不是该?如果是这样,那么我应该只映射源文件并仍然使用_destFile.write,还是应该同时映射两者并使用memcpy

我想这个问题与 Qt 并没有真正的联系,我认为答案应该对任何支持内存映射的文件 I/O API 都是通用的。

【问题讨论】:

    标签: c++ qt file file-io memory-mapping


    【解决方案1】:

    好的,好的,如果它必须是内存映射解决方案。这是一个:

    QFile source("/tmp/bla1.bin");
    source.open(QIODevice::ReadOnly);
    QFile destination("/tmp/bla2.bin");
    destination.open(QIODevice::ReadWrite);
    destination.resize(source.size());
    uchar *data = destination.map(0,destination.size());
    if(!data){
        qDebug() << "Cannot map";
        exit(-1);
    }
    QByteArray buffer;
    int chunksize = 200;
    int var = 0;
    do{
        var = source.read((char *)(data), chunksize);
        data += var;
    }while(var > 0);
    destination.unmap(data);
    destination.close();
    

    这只会将目标文件映射到内存中。我怀疑映射源文件也会有很大的不同。但这是用于具体测量的东西,而不是假设。

    另一个问题是您是否可以一次将整个文件映射到内存中。不断地取消映射和重新映射肯定会降低性能。即使你使用 Qt。像内存映射这样的函数在不同的平台上往往表现得令人不安,例如。您映射到内存的最大文件大小可能不同。

    【讨论】:

    • mmap'ing 文件而不是目标文件有很好的技术原因。那是因为这样做,你保存了一个数据副本(从内核缓冲区/映射read首先进入提供给系统调用的用户空间数据缓冲区)。您还可以通过madvise(MADV_SEQUENTIAL) 提示系统积极预取/预读(比让系统自行发现预读更好)。您可以通过写入多个页面大小 (sysconf(_SC_PAGESIZE)) 来避免对部分块进行不必要的块清除 - 以较小的单位进行复制是没有意义的。
    • @FranH,谢谢。我真的必须尝试一下。幸运的是,很容易更改上面的代码来映​​射源文件。内核空间 -> 用户空间复制开销听起来是合理的。多个页面大小也是如此,但当然,是否可以考虑这一点取决于要求。
    • 哇,我的文件管理器项目已经 2 岁了?!无论如何,我终于可以在 Windows 上测试性能,我正在映射源文件和目标文件,然后使用 memcpy 复制数据。块大小为 5 MB,如果文件较小,则为整个文件。大文件没有区别,但映射对小文件产生了巨大的加速,超过 4 倍。 4x 用于 400 字节的文件。如果有人感兴趣,这里是来源:github.com/VioletGiraffe/file-commander/blob/…
    【解决方案2】:

    最佳方法是什么,总是在旁观者的眼中。这里至少有一种更短的方法:

    QFile source("/tmp/bla1.bin");
    source.open(QIODevice::ReadOnly);
    QFile destination("/tmp/bla2.bin");
    destination.open(QIODevice::WriteOnly);
    QByteArray buffer;
    int chunksize = 200; // Whatever chunk size you like
    while(!(buffer = source.read(chunksize)).isEmpty()){
        destination.write(buffer);
    }
    destination.close();
    source.close();
    

    还有内存映射……我尽量远离这样的事情。我不太确定它们是如何独立于平台的。

    【讨论】:

    • 这就是我们使用 Qt 的原因——为了不被特定于平台的东西所困扰。最坏的情况-我们得到与您的答案相同的行为,即模拟内存映射的临时缓冲区。无论如何,我不确定你为什么要这样回答,因为它与我已经编写的代码相同,它没有回答我的问题。
    • 和你写的代码一样吗?不完全是。我不需要“chunkSize
    • 顺便说一句...您的“我认为答案应该适用于任何支持内存映射的文件 I/O API。”在我写这个答案的时候被编辑了。所以,对不起。
    • 好提示,谢谢。但我仍然认为内存映射是提高性能的方式。
    【解决方案3】:

    使用这个 QFile::map() 方法:

    QFile fs("Sourcefile.bin");
    
    fs.open(QFile::ReadOnly);
    
    QFile fd("Destinationfile.bin");
    
    fd.open(QFile::WriteOnly);
    
    fd.write((char*) fs.map(0, fs.size()), fs.size()); //Copies all data
    
    fd.close();
    fs.close();
    

    【讨论】:

    • 我知道如何拨打QFile::map。问题是最好的方法是什么,考虑到复制是在 chunks 中完成的——你的回答完全忽略了这一点。标记为“不是答案”。
    • @VioletGiraffe 标志不适用于错误答案
    • fd.write((char*) fs.map(iPos, chunksize), chunksize);将 fs.size() 更改为您的块大小,它将开始在卡盘中复制而无需缓冲。添加 iPos 以移动到下一个块。
    • @UmNyobe:那么“不是答案”标志是什么?
    • @Krishna:请再次阅读问题。你甚至不想回答它。
    猜你喜欢
    • 2012-03-25
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多