为什么std :: fstreams很慢?

我正在研究一个简单的解析器,当分析时我观察到瓶颈在...文件读取! 我提取非常简单的测试来比较读取大量数据时fstreamsFILE*的性能:

#include <stdio.h>
#include <chrono>
#include <fstream>
#include <iostream>
#include <functional>

void measure(const std::string& test, std::function<void()> function)
{
    auto start_time = std::chrono::high_resolution_clock::now();

    function();

    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
    std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
}

#define BUFFER_SIZE (1024 * 1024 * 1024)

int main(int argc, const char * argv[])
{
    auto buffer = new char[BUFFER_SIZE];
    memset(buffer, 123, BUFFER_SIZE);

    measure("FILE* write", [buffer]()
    {
        FILE* file = fopen("test_file_write", "wb");
        fwrite(buffer, 1, BUFFER_SIZE, file);
        fclose(file);
    });
    measure("FILE* read", [buffer]()
    {
        FILE* file = fopen("test_file_read", "rb");
        fread(buffer, 1, BUFFER_SIZE, file);
        fclose(file);
    });
    measure("fstream write", [buffer]()
    {
        std::ofstream stream("test_stream_write", std::ios::binary);
        stream.write(buffer, BUFFER_SIZE);
    });
    measure("fstream read", [buffer]()
    {
        std::ifstream stream("test_stream_read", std::ios::binary);
        stream.read(buffer, BUFFER_SIZE);
    });

    delete[] buffer;
}

在我的机器上运行此代码的结果是:

FILE* write 1388.59 ms
FILE* read 1292.51 ms
fstream write 3105.38 ms
fstream read 3319.82 ms

fstream写入/读取比FILE*写入/读取慢大约2倍! 在阅读大量数据时,没有任何解析或fstreams其他功能。 我在Mac OS上运行代码,Intel I7 2.6GHz,16GB 1600 MHz内存,SSD驱动器。 请注意,再次运行相同的代码时, FILE* read的时间很短(大约200 ms),可能是因为文件被缓存了......这就是为什么打开的文件不是使用代码创建的。

为什么在使用fstream读取二进制数据时与FILE*相比如此之慢?

编辑1:我更新了代码和时间。 抱歉耽搁了!

编辑2:我添加了命令行和新的结果(非常类似于以前的!)

$ clang++  main.cpp -std=c++11 -stdlib=libc++ -O3
$ ./a.out
FILE* write 1417.9 ms
FILE* read 1292.59 ms
fstream write 3214.02 ms
fstream read 3052.56 ms

第二轮的结果如下:

$ ./a.out
FILE* write 1428.98 ms
FILE* read 196.902 ms
fstream write 3343.69 ms
fstream read 2285.93 ms

它看起来像文件获取缓存时读取FILE*stream的时间减少与他们两个相同的金额。

编辑3:我减少了代码:

FILE* file = fopen("test_file_write", "wb");
fwrite(buffer, 1, BUFFER_SIZE, file);
fclose(file);

std::ofstream stream("test_stream_write", std::ios::binary);
stream.write(buffer, BUFFER_SIZE);

并开始分析器。 似乎streamxsputn函数中花费了大量时间,并且实际的write调用具有相同的持续时间(因为它应该是,它是相同的函数...)

Running    Time     Self       Symbol Name
3266.0ms   66.9%    0,0        std::__1::basic_ostream<char, std::__1::char_traits<char> >::write(char const*, long)
3265.0ms   66.9%    2145,0          std::__1::basic_streambuf<char, std::__1::char_traits<char> >::xsputn(char const*, long)
1120.0ms   22.9%    7,0                 std::__1::basic_filebuf<char, std::__1::char_traits<char> >::overflow(int)
1112.0ms   22.7%    2,0                      fwrite
1127.0ms   23.0%    0,0        fwrite

编辑4由于某些原因,这个问题被标记为重复。 我想指出,我根本不使用printf ,我只用std::cout来写时间。 read部分中使用的文件是write部分的输出,使用不同名称复制以避免缓存


看起来,在Linux上,对于这一大组数据, fwrite的实现效率要高得多,因为它使用write而不是writev

我不确定为什么writevwrite速度慢得多,但这似乎是差异所在。 至于为什么在这种情况下fstream需要使用这个构造,我绝对没有真正的理由。

这可以通过使用strace ./a.out (其中a.out是测试该程序的程序)轻松看出。

输出:

fstream的:

clock_gettime(CLOCK_REALTIME, {1411978373, 114560081}) = 0
open("test", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
writev(3, [{NULL, 0}, {""..., 1073741824}], 2) = 1073741824
close(3)                                = 0
clock_gettime(CLOCK_REALTIME, {1411978386, 376353883}) = 0
write(1, "fstream write 13261.8 msn", 25fstream write 13261.8 ms) = 25

文件*:

clock_gettime(CLOCK_REALTIME, {1411978386, 930326134}) = 0
open("test", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, ""..., 1073741824) = 1073741824
clock_gettime(CLOCK_REALTIME, {1411978388, 584197782}) = 0
write(1, "FILE* write 1653.87 msn", 23FILE* write 1653.87 ms) = 23

我没有他们喜欢的SSD硬盘,所以我的机器会慢一些 - 或者其他的东西在我的情况下比较慢。

正如扬·胡德克指出的那样,我误解了结果。 我只写了这个:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <chrono>

void measure(const std::string& test, std::function<void()> function)
{
    auto start_time = std::chrono::high_resolution_clock::now();

    function();

    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
    std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
}

#define BUFFER_SIZE (1024 * 1024 * 1024)


int main()
{
    auto buffer = new char[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);

    measure("writev", [buffer]()
    {
        int fd = open("test", O_CREAT|O_WRONLY);
        struct iovec vec[] = 
        {
            { NULL, 0 },
            { (void *)buffer, BUFFER_SIZE }
        };
        writev(fd, vec, sizeof(vec)/sizeof(vec[0]));
        close(fd);
    });

    measure("write", [buffer]()
    {
        int fd = open("test", O_CREAT|O_WRONLY);
        write(fd, buffer, BUFFER_SIZE);
        close(fd);
    });
}

这是fstream的实际实现,它可能会做些愚蠢的事情 - 可能以小块,某处或某种方式复制整个数据,或者类似的东西。 我会尝试进一步了解。

结果对于两种情况都非常相似,并且比问题中的fstreamFILE*变体更快。

编辑:

在我的机器上,现在看来,如果在写入之后添加fclose(file) ,则对于fstreamFILE* ,在我的系统上花费大约相同的时间量 - 在我的系统上花费大约13秒来写入1GB - 与旧式旋转磁盘驱动器,而不是SSD。

然而,我可以使用这段代码更快地写出更多内容:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <unistd.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <chrono>

void measure(const std::string& test, std::function<void()> function)
{
    auto start_time = std::chrono::high_resolution_clock::now();

    function();

    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start_time);
    std::cout<<test<<" "<<static_cast<double>(duration.count()) * 0.000001<<" ms"<<std::endl;
}

#define BUFFER_SIZE (1024 * 1024 * 1024)


int main()
{
    auto buffer = new char[BUFFER_SIZE];
    memset(buffer, 0, BUFFER_SIZE);

    measure("writev", [buffer]()
    {
        int fd = open("test", O_CREAT|O_WRONLY, 0660);
        struct iovec vec[] = 
        {
            { NULL, 0 },
            { (void *)buffer, BUFFER_SIZE }
        };
        writev(fd, vec, sizeof(vec)/sizeof(vec[0]));
        close(fd);
    });

    measure("write", [buffer]()
    {
        int fd = open("test", O_CREAT|O_WRONLY, 0660);
        write(fd, buffer, BUFFER_SIZE);
        close(fd);
    });
}

给出约650-900毫秒的时间。

我也可以编辑原始程序给予fwrite大约1000ms的时间 - 只需删除fclose

我也加了这个方法:

measure("fstream write (new)", [buffer]()
{
    std::ofstream* stream = new std::ofstream("test", std::ios::binary);
    stream->write(buffer, BUFFER_SIZE);
    // Intentionally no delete.
});

然后在这里也需要大约1000毫秒。

所以,我的结论是,不知怎的,有时候,关闭文件会使其刷新到磁盘。 在其他情况下,它不会。 我仍然不明白为什么......


与其他答案相反,大文件读取的一个大问题来自C标准库的缓冲。 尝试在大块(1024KB)中使用低级read / write调用并查看性能跳跃。

C库的文件缓冲对读取或写入小块数据(小于磁盘块大小)非常有用。

在Windows上,当读取和写入原始视频流时,我几乎获得了3倍的性能提升,从而减少了文件缓冲。

我还使用本地操作系统(win32)API调用打开了该文件,并告诉操作系统不要缓存该文件,因为这涉及另一个副本。


MAC在某种程度上被破坏,旧的实现或设置。

旧的设置可能会导致FILE被写入exe目录和用户目录中的流中,除非您有2个磁盘或其他不同的设置,否则这应该没有任何区别。

在我糟糕的Vista上,我获得了Normal buffer + Uncached:
C ++ 201103
文件*写入4756毫秒
文件*读取5007毫秒
fstream写5526毫秒
fstream读取5728毫秒

正常缓冲区+缓存:
C ++ 201103
文件*写入4747毫秒
文件*读取454毫秒
fstream写入5490毫秒
fstream读取396毫秒

大缓存+缓存:
C ++ 201103
第五轮:
文件*写入4760毫秒
文件*读取446毫秒
fstream写5278毫秒
fstream读取369毫秒

这表明文件写入速度比fstream快,但读取速度比fstream慢,但所有数据都在彼此的〜10%之内。

尝试添加更多缓冲到您的流,看看是否有帮助。

const int MySize = 1024*1024;
char MrBuf[MySize];
stream.rdbuf()->pubsetbuf(MrBuf, MySize);

FILE的等价物是

const int MySize = 1024*1024;
if (!setvbuf ( file , NULL , _IOFBF , MySize )) 
    DieInDisgrace();
链接地址: http://www.djcxy.com/p/31519.html

上一篇: Why are std::fstreams so slow?

下一篇: Why is istream/ostream slow