的std :: fstream的缓冲VS手动缓存(为什么10倍的增益,手动缓冲)?(std::fst

2019-06-18 14:04发布

我已经测试了两种写作配置:

1)fstream的缓冲:

// Initialization
const unsigned int length = 8192;
char buffer[length];
std::ofstream stream;
stream.rdbuf()->pubsetbuf(buffer, length);
stream.open("test.dat", std::ios::binary | std::ios::trunc)

// To write I use :
stream.write(reinterpret_cast<char*>(&x), sizeof(x));

2)手动缓冲:

// Initialization
const unsigned int length = 8192;
char buffer[length];
std::ofstream stream("test.dat", std::ios::binary | std::ios::trunc);

// Then I put manually the data in the buffer

// To write I use :
stream.write(buffer, length);

我希望同样的结果...

但我的手工缓冲改善了10倍的性能写一个100MB的文件,相比正常情况下fstream的缓冲不会改变任何东西(没有重新定义缓存)。

是否有人有这种情况的一个解释吗?

编辑:这里是新闻:刚刚在超级计算机上完成的基准(Linux 64位架构,持续英特尔至强8核,光泽文件系统,...希望配置良好的编译器) (我不解释了“共振”为1KB的手工缓冲的原因...)

编辑2:而在1024 B中的共振(如果什么人有一个想法,我很感兴趣):

Answer 1:

这基本上是由于函数调用的开销和间接。 所述的ofstream :: write()方法从ostream的继承。 该功能并不在的libstdc ++,它是开销的第一源内联。 随后的ostream ::写()的调用rdbuf() - > sputn()做实际的写作,这是一个虚函数调用。

最重要的是,的libstdc ++重定向sputn()到另一个虚拟函数xsputn()这增加了另一虚拟函数调用。

如果你把字符到缓冲区自己,你能避免开销。



Answer 2:

我想解释什么是高峰的事业第二个图表

事实上,使用虚函数std::ofstream铅的服务表现下降,因为我们看到的第一张图片,但它没有给出一个答案,为什么最高的服务表现是,当手动缓冲区大小小于1024个字节。

这个问题涉及到的高成本writev()write()系统调用和内部实现std::filebuf内部类std::ofstream

要显示该怎么write()对我没有用一个简单的测试性能的影响dd我的Linux机器上的工具复制与不同的缓冲区大小(BS期权)10MB的文件:

test@test$ time dd if=/dev/zero of=zero bs=256 count=40000
40000+0 records in
40000+0 records out
10240000 bytes (10 MB) copied, 2.36589 s, 4.3 MB/s

real    0m2.370s
user    0m0.000s
sys     0m0.952s
test$test: time dd if=/dev/zero of=zero bs=512 count=20000
20000+0 records in
20000+0 records out
10240000 bytes (10 MB) copied, 1.31708 s, 7.8 MB/s

real    0m1.324s
user    0m0.000s
sys     0m0.476s

test@test: time dd if=/dev/zero of=zero bs=1024 count=10000
10000+0 records in
10000+0 records out
10240000 bytes (10 MB) copied, 0.792634 s, 12.9 MB/s

real    0m0.798s
user    0m0.008s
sys     0m0.236s

test@test: time dd if=/dev/zero of=zero bs=4096 count=2500
2500+0 records in
2500+0 records out
10240000 bytes (10 MB) copied, 0.274074 s, 37.4 MB/s

real    0m0.293s
user    0m0.000s
sys     0m0.064s

正如你可以看到,少缓冲区,少写入速度和多少时间dd系统中的空间花费。 所以,当缓冲区大小减小读/写速度下降。

但是,为什么最高速度是在手动缓存大小是在这个主题的人手工缓冲试验小于1024个字节? 为什么它几乎是恒定的?

该解释涉及std::ofstream实施,特别是对std::basic_filebuf

缺省情况下它使用1024字节缓冲器(BUFSIZ变量)。 所以,当你写使用撕成小块小于1024的数据, writev()write()系统调用被调用至少一次,为期ofstream::write()操作(一块块有1023 <1024尺寸-第一个被写入到缓冲器中,力和第二力的第一和第二的写入)。 在此基础上,我们可以得出这样的结论ofstream::write()速度不前峰依赖于手动缓存大小( write()很少被称为至少两次)。

当你尝试写大于或一次使用等于1024字节的缓冲区ofstream::write()调用, writev()系统调用被每一个信息ofstream::write 。 所以,你看到的速度增加时,手工缓冲大于1024(后峰)。

此外,如果你想设置std::ofstream缓存大于1024的缓冲区使用(例如,8192个字节的缓冲区) streambuf::pubsetbuf()并调用ostream::write()使用1024大小的一块块写数据时,你会很惊讶的是写入速度将是一样的,你将使用1024缓冲区。 这是因为执行std::basic_filebuf -内部类std::ofstream -是硬编码强制呼叫系统writev()调用每个ofstream::write()时传递的缓冲区是大于或等于1024个字节 (调用见basic_filebuf :: xsputn()源代码)。 也有在该报道在GCC的Bugzilla一个开放的问题, 2014年11月5日 。

所以,这个问题的解决方案可以使用两种可能的情况下完成的:

  • 替换std::filebuf通过自己的类,并重新定义std::ofstream
  • devide一个缓冲器,其具有要被传递到ofstream::write()对一块块小于1024,并将它们传递到ofstream::write()逐个
  • 不小的数据撕成小块传递到ofstream::write()以避免对的虚拟功能降低性能std::ofstream


Answer 3:

我想添加到现有的响应,这种性能行为(均来自虚拟方法调用/间接开销)通常不是一个问题,如果写大数据块。 什么似乎已经从问题和这些答案之前(虽然可能隐含地理解)忽略的是,原代码编写每次字节数小的。 只是为了澄清他人:如果你正在写的数据(〜KB +)的大盖帽,没有理由手动期待缓冲将有显著的性能差异,使用std::fstream的缓冲。



文章来源: std::fstream buffering vs manual buffering (why 10x gain with manual buffering)?