【问题标题】:C++ ifstream::read slow due to memcpy由于 memcpy,C++ ifstream::read 很慢
【发布时间】:2012-05-06 15:56:16
【问题描述】:

最近我决定优化我正在做的一些文件读取,因为正如大家所说,将大量数据读取到缓冲区然后使用它比使用大量小读取要快。而且我的代码现在当然要快得多,但是在进行了一些分析之后,memcpy 似乎占用了很多时间。

我的代码的要点是...

ifstream file("some huge file");
char buffer[0x1000000];
for (yada yada) {
    int size = some arbitrary size usually around a megabyte;
    file.read(buffer, size);
    //Do stuff with buffer
}

我正在使用 Visual Studio 11,在分析我的代码后,它显示 ifstream::read() 最终调用 xsgetn(),它从内部缓冲区复制到我的缓冲区。这个操作占用了80%以上的时间!排在第二位的是uflow(),它占用了 10% 的时间。

有什么办法可以绕过这种复制吗?我可以以某种方式告诉ifstream 将我需要的大小直接缓冲到我的缓冲区中吗? C 风格的FILE* 也使用这样的内部缓冲区吗?

更新:由于人们告诉我使用 cstdio...我已经做了一个基准测试。

编辑:不幸的是,旧代码充满了失败(它甚至没有读取整个文件!)。你可以在这里看到它:http://pastebin.com/4dGEQ6S7

这是我的新基准:

const int MAX = 0x10000;
char buf[MAX];
string fpath = "largefile";
int main() {
    {
        clock_t start = clock();
        ifstream file(fpath, ios::binary);
        while (!file.eof()) {
            file.read(buf, MAX);
        }
        clock_t end = clock();
        cout << end-start << endl;
    }
    {
        clock_t start = clock();
        FILE* file = fopen(fpath.c_str(), "rb");
        setvbuf(file, NULL, _IOFBF, 1024);
        while (!feof(file)) {
            fread(buf, 0x1, MAX, file);
        }
        fclose(file);
        clock_t end = clock();
        cout << end-start << endl;
    }
    {
        clock_t start = clock();
        HANDLE file = CreateFile(fpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, NULL, NULL);
        while (true) {
            DWORD used;
            ReadFile(file, buf, MAX, &used, NULL);
            if (used < MAX) break;
        }
        CloseHandle(file);
        clock_t end = clock();
        cout << end-start << endl;
    }
    system("PAUSE");
}

时间是:
185
80
78

嗯...看起来使用 C 风格的 fread 比 ifstream::read 快。同样,使用windows ReadFile 仅提供微不足道的优势(我查看了代码,fread 基本上是ReadFile 的包装器)。看来我还是要改用 fread 了。

编写一个真正正确测试这些东西的基准是令人困惑的。

结论:使用&lt;cstdio&gt;&lt;fstream&gt; 快。 fstream 较慢的原因是因为 c++ 流有自己的内部缓冲区。这会在您读/写时导致额外的复制,并且这种复制占 fstream 所花费的全部额外时间。更令人震惊的是,额外花费的时间比实际读取文件的时间还要长。

【问题讨论】:

  • xsgetn 通常是实际文件 IO 完成的地方。
  • cstdio 一路,加上一个基于您的访问模式的自定义类(用于预取、内存中延迟写入等)
  • 您有机会尝试使用 C 文件 API 吗? iostreams 库速度非常慢。
  • 您为什么不使用可用的最佳解决方案而不是人为限制?最好的解决方案是内存映射文件,除非你想自己编写繁琐的跨平台实现,Boost.Interprocess 拥有最好的跨平台内存映射文件实现。如果标准 C++ 有这个可用,它不会在 Boost 中

标签: c++ performance visual-c++ fstream memcpy


【解决方案1】:

我能以某种方式告诉 ifstream 直接缓冲我需要的大小吗 进入我的缓冲区?

是的,这就是pubsetbuf() 的用途。

但如果您在读取文件时担心复制,请考虑内存映射,boost 有一个 portable implementation

【讨论】:

  • 我已经研究过使用 pubsetbuf 但是我将如何将特定大小的块读取到该缓冲区中?
【解决方案2】:

如果你想加快文件 I/O,我建议你使用好的 ol'&lt;cstdio&gt;,因为它可以大大优于 C++。

【讨论】:

  • 请问为什么?我知道它避免了很多虚函数调用和类开销,但是 cstdio 在内部缓冲区方面做了什么?
  • @retep998:类没有开销。虚函数。问题不在于缓冲。 xsgetn 正在做文件 IO。但它也在做一些事情,比如为角色调用codecvt
  • @repet998:那是因为您按字符阅读。分块阅读。
  • 这只是错误的。在 C++ 中使用 istream::read 和编译器优化的惯用解决方案与 C 解决方案一样快。例如insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html 我自己用 VS2013 确认了这些结果。
  • @user904963 您一次将整个文件读入内存,尝试作为基准读取一个非常大(至少是总 RAM 的两倍)的文件,并将所有字节异或在一起以防止优化。通过在 C++ 流上使用正确的标志并且仅使用 read 方法,可以实现 C 风格的 I/O 速度,但是您基本上是使用 C++ 流使用 C 风格的 I/O。请注意,在链接的博客文章中,每个 C++ 风格的 I/O 解决方案都比 C 风格的解决方案慢,除了您基本上使用 iostream 方法进行一对一映射的解决方案。
【解决方案3】:

已经多次证明,在linux系统上读取数据最快的方式是mmap()。我不知道Windows。但是,它肯定会没有这种缓冲。

fopen(), fread(), fwrite() (FILE*) 是更高级别的,可能会引入缓冲区,而open()read()write() 函数是低级别的,并且是唯一的缓冲区你可能有来自Os内核。

【讨论】:

  • 太好了。但他标记了他的问题 Visual Studio。所以这并没有真正的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 2012-10-23
  • 1970-01-01
相关资源
最近更新 更多