我想解释一下导致第二张图表峰值的原因。
实际上,std::ofstream
使用的虚函数会导致性能下降,类似于我们在第一张图片中看到的情况,但这并不能回答为什么手动缓冲区大小小于1024字节时性能受到最严重的影响。
问题与writev()
和write()
系统调用的高成本以及std::ofstream
的内部类std::filebuf
的内部实现有关。
为了展示write()
如何影响性能,我在Linux机器上使用dd
工具进行了简单的测试,复制了10MB文件并更改了不同的缓冲区大小(使用bs
选项):
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的数据段写入数据时,ofstream :: write()
操作至少调用2次writev()
(第一次写入缓冲区,第二次强制写入第一个和第二个)。根据这一点,我们可以得出结论:ofstream :: write()
速度不取决于峰值之前的手动缓冲区大小(很少调用write()
两次)。
当您尝试使用ofstream :: write()
调用一次写入大于或等于1024字节的缓冲区时,每个ofstream :: write()
都会调用writev()
系统调用。因此,当手动缓冲区大于1024(峰值之后)时,速度会增加。
此外,如果您想使用streambuf :: pubsetbuf()
设置大于1024缓冲区(例如8192字节缓冲区)并使用大小为1024的数据段调用ostream :: write()
来写入数据,则会惊讶地发现写入速度与使用1024缓冲区相同。这是因为std::basic_filebuf
的实现-std::ofstream
的内部类-在传递的缓冲区大于或等于1024字节时硬编码强制每个ofstream :: write()
调用系统writev()
调用(请参见basic_filebuf :: xsputn()源代码)。 GCC bugzilla中还存在一个已报告的问题,该问题报告于2014-11-05。
因此,可以通过以下两种情况中的任何一种解决此问题:
- 用自己的类替换
std :: filebuf
并重新定义std :: ofstream
- 将缓冲区划分为小于1024的大小,并逐个传递到
ofstream :: write()
中
- 不要将小数据段传递给
ofstream :: write()
,以避免在std::ofstream
的虚拟函数上降低性能