为什么在二进制模式下fwrite会这么慢?

时间:2016-12-14 20:31:00

标签: c++ qt optimization io

我有一个大约2GB的原始二进制数据的缓冲区存储在QByteArray中,我试图以最快的方式编写。因为QFile明显变慢了,所以我回过头来讨论C / C ++风格的写作,我几年没做过这样的事情,所以我很生气。

示例代码:

QFile in("D:/input.las");
in.open(QIODevice::ReadOnly);
QByteArray junk = in.readAll();
in.close();

QFile test1("D:/samplelas/bigset2/out/_test1.las");
test1.open(QIODevice::WriteOnly | QIODevice::Truncate);
if(test1.isOpen())
{
    QElapsedTimer t;
    t.start();
    test1.write(junk);
    test1.close();
    qDebug("Round\t%'i\tTest QFile\ttook\t%'i", i+1, t.elapsed());
}

FILE* test2 = fopen("D:/_test2.las", "wb");
if(test2)
{
    QElapsedTimer t;
    t.start();
    fwrite(junk.constData(), sizeof(char), junk.size(), test2);
    fclose(test2);
    qDebug("Round\t%'i\tTest wb\ttook\t%'i", i+1, t.elapsed());
}

FILE* test3 = fopen("D:/_test3.las", "w");
if(test3)
{
    QElapsedTimer t;
    t.start();
    fwrite(junk.constData(), sizeof(char), junk.size(), test3);
    fclose(test3);
    qDebug("Round\t%'i\tTest w\ttook\t%'i", i+1, t.elapsed());
}

我注意到使用"w"代替"wb"会损坏输出,因为"换行符"在我的Windows机器上解释了字符(菜鸟错误)。尽管如此,结果很有希望,所以我再次尝试用#34; wb"代替。

我非常惊讶地发现二进制模式慢了5-10倍,我无法弄清楚原因。如果有的话,非二进制模式写入应该更快,因为它不被解释,只是原始数据。

我错过了什么?

Edit1:在MSVC2010,Windows 7x64 Pro上使用Qt 4.8.6在发布模式下进行测试。

编辑2:添加了QFile测试用例并澄清了该问题的解释。

1 个答案:

答案 0 :(得分:0)

也许我完全错了,但我看到(基于你的代码)性能差异的原因是这些行中fwrite函数参数的混淆:

fwrite(junk.constData(), sizeof(char), junk.size(), <file>);

让我们回顾一下这个函数的定义:

size_t fwrite (const void* buff, size_t size, size_t count, FILE* stream);

其中:

  • size是要编写的块大小。
  • count是要写的块数量
  • stream在这种情况下,是要写入的文件。
  • 该函数返回size_t,其中包含成功写入的块数。

现在,这些参数的含义完全取决于fwrite实现代码。在某些情况下(如FreeBSD的libc),实现只需执行total = size*count之类的操作,并进行一次系统调用以编写整个缓冲区。但在另一个实现中(如微软),它是一个不同的故事:

文字模式中的含义

fwrite将:

  • size个字节的块复制到BUFSIZ个字节大小的内部缓冲区(通常为512,如果用户未更改)
  • 转换所有换行符
  • 进行一次系统调用以写入内部BUFSIZ缓冲区,当它已满时。
  • 如果无法完成系统调用,则返回已经写入的块数。
  • 重新执行,直到写入count块或发生错误。

在你的代码中:fwrite会生成aprox。 (junk.size()/ 512)系统调用。

二进制模式中的含义

fwrite将:

  • 从1到count次迭代进行循环:
  • 在每次迭代中,它将进行一次系统调用,将一个size字节块写入该文件。
  • 如果无法完成系统调用,则返回已经写入的块数。
  • 重新执行,直到写入count块或发生错误。

在你的代码中:fwrite将比文本模式多出512倍的系统调用(为什么它只慢10倍?这是另一个问题)。