std::fstream缓冲区与手动缓冲区(为什么手动缓冲区可以提高10倍性能)?

16 浏览
0 Comments

std::fstream缓冲区与手动缓冲区(为什么手动缓冲区可以提高10倍性能)?

我测试了两种写入配置:

  1. Fstream缓冲:

    // 初始化
    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)
    // 写入时使用:
    stream.write(reinterpret_cast(&x), sizeof(x));
    

  2. 手动缓冲:

    // 初始化
    const unsigned int length = 8192;
    char buffer[length];
    std::ofstream stream("test.dat", std::ios::binary | std::ios::trunc);
    // 然后我手动将数据放入缓冲区
    // 写入时使用:
    stream.write(buffer, length);
    

我原本期望结果是一样的...

但是手动缓冲提升了性能,写入一个100MB的文件速度提高了10倍,而fstream缓冲与正常情况(不重新定义缓冲区)相比没有改变。

有人能解释这种情况吗?

编辑:

以下是在超级计算机上进行的性能测试(Linux 64位架构,最新的英特尔Xeon 8核处理器,Lustre文件系统和...希望配置良好的编译器)

benchmark

(我不解释1kB手动缓冲的"共鸣"原因...)

编辑2:

并且在1024B时出现共振(如果有人对此有想法,我很感兴趣):

enter image description here

0
0 Comments

std::fstream缓冲与手动缓冲(为什么手动缓冲可以获得10倍的增益)

这个问题的出现是由于函数调用开销和间接性引起的。ofstream::write()方法是从ostream继承而来的。在libstdc++中,该函数没有被内联,这是开销的第一个来源。然后ostream::write()必须调用rdbuf()->sputn()来进行实际的写入操作,这是一个虚函数调用。

除此之外,libstdc++将sputn()重定向到另一个虚函数xsputn(),这又增加了一次虚函数调用。

如果你自己将字符放入缓冲区,就可以避免这种开销。

我必须承认我不明白为什么这个答案是正确的。两个版本都使用了stream::write,为什么会有所不同?pubsetbuf然后打开应该和ifstream cstr有相同的效果。

:问题中没有明确说明,但假设在第一种情况下,当写入x时,这些写入是小的,所以为了相同的数据量,你最终会进行更多次的stream.write调用。

解决方法是手动缓冲。通过手动将字符放入缓冲区,可以避免函数调用开销和间接性引起的性能损失。这种方法可以通过以下步骤实现:

1. 创建一个字符数组作为缓冲区。

2. 使用字符数组的指针来写入字符数据。

3. 当缓冲区满时,使用write()方法将缓冲区中的数据写入文件。

4. 重复步骤2和3,直到所有字符数据都被写入文件。

通过手动缓冲,可以大大提高性能,因为避免了函数调用开销和间接性引起的性能损失。这是为什么手动缓冲可以获得10倍增益的原因。

下面是使用手动缓冲的示例代码:

#include

int main() {

std::ofstream file("data.txt", std::ios::binary);

const int bufferSize = 1024;

char buffer[bufferSize];

for (int i = 0; i < 1000000; i++) {

// Put characters into the buffer manually

buffer[i % bufferSize] = 'x';

// When the buffer is full, write it to the file

if ((i + 1) % bufferSize == 0) {

file.write(buffer, bufferSize);

}

}

file.close();

return 0;

}

通过手动缓冲,将字符放入缓冲区并一次性写入文件,可以避免频繁的函数调用和间接性引起的性能损失,从而获得更高的性能。

0
0 Comments

std::fstream有一个内置的缓冲区,它可以在每次写入数据时将其存储在缓冲区中,然后再将缓冲区中的内容一次性地写入磁盘。这是一种自动的缓冲机制,可以提高写入文件的效率。但是,有时手动进行缓冲操作可能会比std::fstream的自动缓冲机制更有效,可以获得更高的性能。

在这个问题中,原始代码每次只写入了少量的字节。由于std::fstream的自动缓冲机制,每次写入操作都会导致一次函数调用和一次数据拷贝,这会产生很大的开销。而手动进行缓冲操作可以避免这些开销,因此可以获得更高的性能。

解决这个问题的方法是使用手动缓冲机制。可以声明一个缓冲区,并将要写入的数据存储在缓冲区中。当缓冲区已满或需要刷新时,将缓冲区的内容一次性地写入磁盘。这样可以减少函数调用和数据拷贝的次数,提高写入文件的性能。

需要注意的是,如果要写入的数据量较大(大于等于1kB),使用std::fstream的自动缓冲机制和手动缓冲机制之间可能没有明显的性能差异。因为对于大块数据的写入,std::fstream的自动缓冲机制已经足够高效。

总结起来,std::fstream的自动缓冲机制在每次写入少量数据时可能会导致较大的性能开销。通过手动进行缓冲操作,可以减少函数调用和数据拷贝次数,提高写入文件的性能。但对于大块数据的写入,std::fstream的自动缓冲机制已经足够高效。

0
0 Comments

从上述内容中可以整理出以下问题的原因和解决方法:

问题:为什么手动缓冲大小小于1024字节时性能提升了10倍?

原因:问题与writev()和write()系统调用的高成本以及std::filebuf的内部实现有关。当使用小于1024的数据块写入时,至少会调用两次writev()系统调用,这导致了性能下降。而当使用大于等于1024字节的缓冲区一次性调用ofstream::write()时,每次调用都会触发一次writev()系统调用,这会导致性能提升。

解决方法:

1. 通过自定义类替换std::filebuf并重新定义std::ofstream。

2. 将要传递给ofstream::write()的缓冲区分成小于1024字节的片段,并逐个传递给ofstream::write()。

3. 避免将小数据片段传递给ofstream::write(),以避免在std::ofstream的虚拟函数上降低性能。

0