【问题标题】:Why is std::fstream faster than WriteFile on Windows为什么 std::fstream 在 Windows 上比 WriteFile 快
【发布时间】:2021-12-22 10:30:19
【问题描述】:

我尝试了各种不同的标志组合,例如 FILE_FLAG_NO_BUFFERING 和 FILE_FLAG_OVERLAPPED,但 fstream::write 仍然优于 Windows API 版本。

std::fstream 是否使用了内部缓冲或其他技巧,还是我只是搞砸了什么?

#include <Windows.h>
#include <chrono>
#include <fstream>
#include <iostream>

std::string createTempFileName()
{
    char buf[800];
    tmpnam_s(buf, sizeof(buf));
    return buf;
}

using namespace std::chrono;

int main()
{
    std::uint64_t count = 1 << 23;

    std::cout << "test fstream\n";
    {
        auto start = steady_clock::now();
        auto path = createTempFileName();
        std::fstream fs(path, std::ios_base::out | std::ios_base::in | std::ios_base::trunc | std::ios_base::binary);
        for (std::uint64_t i = 0; i < count; i++)
            fs.write((char*)&i, sizeof(i));
        fs.close();
        DeleteFile(path.c_str());
        auto end = steady_clock::now();
        std::cout << "fstream: Elapsed time in milliseconds : " << duration_cast<milliseconds>(end - start).count() << " ms\n";
    }

    std::cout << "test WriteFile\n";
    {
        auto start = steady_clock::now();
        auto path = createTempFileName();
        HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL);
        for (std::uint64_t i = 0; i < count; i++)
            WriteFile(file, &i, sizeof(i), NULL, NULL);
        CloseHandle(file);
        DeleteFile(path.c_str());
        auto end = steady_clock::now();
        std::cout << "WriteFile: Elapsed time in milliseconds : " << duration_cast<milliseconds>(end - start).count() << " ms\n";
    }
}

【问题讨论】:

  • 快速查看source 表明MSVC 的fstream 使用fwrite()。所以也许下面的问答可以提供一些见解:stackoverflow.com/questions/14290337/…
  • @Frank,我在写这个问题之前看到了那个帖子,但它并没有真正帮助,无论如何谢谢=)
  • 您的 PC 上的结果是什么?你有什么电脑?你有什么磁盘?为什么“NO_BUFFERING”要快?为什么阅读链接的文章没有帮助?
  • 有一个关于what makes a good performance question的元讨论。恕我直言,这不是一个好的性能问题。它缺少所有与性能相关的信息。
  • 这看起来像是责备工具类问题的变体。在 Windows 上写入文件的方法并不多。 fwrite() 很可能在下面使用WriteFile()。真正的问题应该是我做错了什么?

标签: c++ windows


【解决方案1】:

我想显而易见的答案是查看 Visual Studio 附带的源代码,但既然您选择不这样做,那么让我们使用我们的神奇思维帽来代替。

在 Windows 上写入文件的记录方法并不多,C/C++ 运行时将使用一些 Windows API 进行写入。按受欢迎程度排名,我猜我们正在查看WriteFile、内存映射文件和IStreamIStream 是 COM 并且级别太高。当您不知道最终大小时,内存映射文件很烦人,因此WriteFile 是最有可能的候选者。

WriteFile 在用户模式下做了一些小工作,但最终会以上下文切换到内核模式而告终。

在您的循环中,您每次仅写入 8 个字节,因此 CPU 时间的很大一部分将用于切换内核模式以及内核(和存储硬件)为此所做的任何缓冲文件句柄并不能阻止这一点。

您正在将FILE_FLAG_NO_BUFFERING 标志传递给CreateFile,但未能完成使这些写入对齐所需的任何工作,并且您未能检查WriteFile 的返回值!它可能会失败,并且就我们所知,整个测试可能无效。

C/C++ 运行时通常愿意选择速度而不是大小/内存使用,这里的神奇词是缓冲。在这种特定情况下,即使是一个很小的 ​​16 字节缓冲区也可能会使您的速度几乎翻倍。您可以尝试通过以下方式关闭此缓冲:

std::fstream fs;
fs.rdbuf()->pubsetbuf(NULL, 0);
fs.open(...

【讨论】:

  • 我尝试创建一个缓冲区并一次将其全部写入(并且只计时写入输出的调用,而不是循环、文件创建等)。出于某种原因,fstream 始终快得多。 fstream 的源代码相当混乱,但它似乎最终调用了fwrite。我现在没有fwrite 的源代码。 fs.rdbuf()-&gt;pubsetbuf(NULL, 0); 没有改变结果。
  • 您是否删除了 FILE_FLAG_NO_BUFFERING?您是否尝试过将 WriteFile 测试放在 fstream 测试之前?是否添加了错误检查?
  • 在 OP 的代码中,如果删除 FILE_FLAG_NO_BUFFERINGWriteFile 的执行时间会飞涨(减慢 20 倍)。但是在我对WriteFile 的一次调用的外部缓冲版本中,如果我删除FILE_FLAG_NO_BUFFERING,执行速度将等同于fstream。有点奇怪。
  • 我猜fwrite 做了更智能的缓冲。我也猜想WriteFile 可以通过仔细注意参数进行调整以超越它。不过,我没有任何证据支持这些猜测。
  • FILE_FLAG_NO_BUFFERING 用于高级用途,如果 I/O 未扇区对齐,谁知道会发生什么,可能是未定义的行为。
猜你喜欢
  • 2015-01-07
  • 1970-01-01
  • 2012-12-26
  • 2014-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-22
  • 2012-04-04
相关资源
最近更新 更多