我想解释一下第二张图表中峰值的原因是什么。
事实上,使用的虚函数std::ofstream会导致性能下降,类似于我们在第一张图片上看到的情况,但它没有给出为什么当手动缓冲区大小小于 1024 字节时性能最高的原因。
这个问题涉及到系统调用和内部类的内部writev()实现的高成本。write()std::filebufstd::ofstream
为了展示如何write()影响性能,我在我的 Linux 机器上使用该工具进行了一个简单的测试,dd以复制具有不同缓冲区大小(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()很少调用至少两次)。
ofstream::write()当您尝试使用call一次写入大于或等于 1024 字节的缓冲区时,writev()系统调用会为每个ofstream::write. 因此,您会看到当手动缓冲区大于 1024(峰值之后)时速度会增加。
此外,如果您想设置std::ofstream缓冲区大于 1024 缓冲区(例如,8192 字节缓冲区)使用streambuf::pubsetbuf()并调用ostream::write()以使用 1024 大小的块写入数据,您会惊讶于写入速度将与使用相同1024 缓冲区。这是因为- 内部类 - 的实现std::basic_filebufstd::ofstream是硬编码的,以在传递的缓冲区大于或等于 1024 字节时强制writev()为每个调用调用系统调用(参见basic_filebuf::xsputn()源代码)。在2014-11-05报告的 GCC bugzilla 中还有一个未解决的问题。ofstream::write()
因此,可以使用两种可能的情况来解决这个问题:
- 替换
std::filebuf为您自己的班级并重新定义std::ofstream
- 将必须传递给 的缓冲区
ofstream::write()划分为小于 1024 的块,并ofstream::write()逐个传递给
- 不要将小块数据传递给
ofstream::write()以避免降低虚拟功能的性能std::ofstream