我测试了两种写入配置:
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核,Lustre文件系统和...希望配置良好的编译器) (我没有解释1kB手动缓冲器“共振”的原因......)
编辑2: 而在1024 B的共振(如果有人对此有所了解,我很感兴趣):
答案 0 :(得分:24)
这基本上是由于函数调用开销和间接。 ofstream :: write()方法继承自ostream。 libstdc ++中没有内联该函数,这是第一个开销源。然后ostream :: write()必须调用rdbuf() - &gt; sputn()来进行实际写入,这是一个虚函数调用。
最重要的是,libstdc ++将sputn()重定向到另一个虚函数xsputn(),这会增加另一个虚函数调用。
如果您自己将字符放入缓冲区,则可以避免这种开销。
答案 1 :(得分:3)
我想解释second chart
中峰值的原因是什么事实上,std::ofstream
使用的虚函数导致性能下降,正如我们在第一张图片上看到的那样,但它没有给出为什么当手动缓冲区大小小于1024字节时性能最高的原因。
问题与writev()
和write()
系统调用的高成本以及std::filebuf
std::ofstream
内部类的内部实现有关。
为了显示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的和平来编写数据时,writev()
(非write()
)系统调用至少会被调用两次ofstream::write()
操作(和平大小为1023 <1024) - 首先写入缓冲区,第二个强制写入第一个和第二个)。基于此,我们可以得出结论:ofstream::write()
速度不依赖于峰值之前的手动缓冲区大小(write()
至少被调用两次)。
当您尝试使用ofstream::write()
调用一次写入大于或等于1024字节的缓冲区时,会为每个writev()
调用ofstream::write
系统调用。因此,当手动缓冲区大于1024(峰值之后)时,您会看到速度增加。
此外,如果您希望使用std::ofstream
将streambuf::pubsetbuf()
缓冲区设置为大于1024的缓冲区(例如,8192字节缓冲区),并使用1024个大小的和平来调用ostream::write()
来写入数据,你会惊讶地发现写入速度与使用1024缓冲区的速度相同。这是因为std::basic_filebuf
的实施 - 内部类std::ofstream
- 硬编码强制调用系统writev()
传递缓冲区时ofstream::write()
调用大于或等于1024字节(请参阅basic_filebuf::xsputn()源代码)。 GCC bugzilla中还存在一个未解决的问题,该问题已在2014-11-05报告。
因此,可以使用两种可能的情况来解决这个问题:
std::filebuf
并重新定义std::ofstream
ofstream::write()
)发送到小于1024的和平区,并将它们逐个传递给ofstream::write()
ofstream::write()
,以避免降低std::ofstream
答案 2 :(得分:0)
我想添加到现有响应中,如果写入大量数据,则这种性能行为(虚拟方法调用/间接调用的所有开销)通常不是问题。问题和这些先前的答案(尽管可能是隐式理解的)似乎已被忽略了,因为原始代码每次都写入少量字节。只是为了向其他人澄清:如果您要写入大块数据(〜kB +),则没有理由期望手动缓冲与使用std::fstream
的缓冲会有明显的性能差异。