【问题标题】:Does C++ ofstream file writing use a buffer?C++ ofstream 文件写入是否使用缓冲区?
【发布时间】:2012-05-14 01:17:25
【问题描述】:

下面是两个将 50,000,000 字节写入文件的程序。

第一个用 C 语言编写的程序使用一个缓冲区,一旦填充到任意值,就会写入磁盘,然后重复该过程,直到写入所有 50,000,000 个字节。我注意到,当我增加缓冲区的大小时,程序运行的时间就会减少。例如,在 BUFFER_SIZE = 1 时,程序耗时约 88.0463 秒,而在 BUFFER_SIZE = 1024 时,程序仅耗时约 1.7773 秒。我记录的最佳时间是 BUFFER_SIZE = 131072。随着 BUFFER_SIZE 的增加,我注意到它实际上开始需要更长的时间。

第二个程序是用 C++ 编写的,它利用 ofstream 一次写入一个字节。令我惊讶的是,该程序只用了大约 1.87 秒即可运行。我预计它需要一分钟左右,就像 BUFFER_SIZE = 1 的 C 程序一样。显然,C++ ofstream 处理文件写入的方式与我想象的不同。根据我的数据,它的性能与 BUFFER_SIZE = 512 的 C 文件非常相似。它是否使用某种幕后缓冲区?

这是 C 程序:

const int NVALUES = 50000000; //#values written to the file
const char FILENAME[] = "/tmp/myfile";
const int BUFFER_SIZE = 8192; //# bytes to fill in buffer before writing

main()
{
    int fd;  //File descriptor associated with output file
    int i;
    char writeval = '\0';
    char buffer[BUFFER_SIZE];

    //Open file for writing and associate it with the file descriptor
    //Create file if it does not exist; if it does exist truncate its size to 0
    fd = open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);

    for(i=0;i<NVALUES;i++)
    {
        //Package bytes into BUFFER_SIZE chunks 
                //and write a chunk once it is filled
        buffer[i%BUFFER_SIZE] = writeval;
        if((i%BUFFER_SIZE == BUFFER_SIZE-1 || i == NVALUES-1))
            write(fd, buffer, i%BUFFER_SIZE+1);

    }

    fsync(fd);

    close(fd);
}

这是 C++ 程序:

int main()
{
    ofstream ofs("/tmp/iofile2");
    int i;

    for(i=0; i<50000000; i++)
        ofs << '\0';

    ofs.flush();
    ofs.close();

    return 0;
}

感谢您的宝贵时间。

【问题讨论】:

  • 是的,它在幕后缓冲,正如ofs.flush(); 所指出的那样。
  • 哦!当“新人”问这种问题时,我真的很喜欢:实验数据、智能假设……先生,很高兴有您加入!
  • 我一直听说 4096(操作系统页面大小)或 8192(双倍操作系统页面大小)是最佳目标。比这些更大的尺寸可能会更快,但速度的提高不值得内存的增加。

标签: c++ file file-io


【解决方案1】:

是的,所有的流操作都是缓冲的,尽管默认情况下标准输入、输出和错误输出都没有,因此与 C IO 的交互并不令人惊讶。

正如已经提到的,有一个在幕后使用的基类streambuf。它有自己的缓冲区,大小是一个实现细节。

假设输入文件流和输出文件流设置为相同的缓冲区大小,您可以使用streambuf::in_avail(实验性地)检查此缓冲区的大小...

您可以在此处执行可能感兴趣的其他两项操作:

  • 您可以更改流使用的streambuf 对象,以切换到自定义版本
  • 您可以更改streambuf 对象使用的缓冲区

两者都应该在创建流之后或flush之后立即完成,以免丢失一些数据...

要说明缓冲区更改,请查看streambuf::putsetbuf

#include <fstream>
#include <vector>

int main () {
  std::vector<char> vec(512);

  std::fstream fs;
  fs.rdbuf()->pubsetbuf(&vec.front(), vec.size());

  // operations with file stream here.
  fs << "Hello, World!\n";

  // the stream is automatically closed when the scope ends, so fs.close() is optional
  // the stream is automatically flushed when it is closed, so fs.flush() is optional

  return 0;
}

现在您可以重复您在 C 中所做的实验以找到最佳位置:)

【讨论】:

  • @Sam:我想你指的是 问题是当打印到控制台时(而不是重定向到文件),我们的 C 和 C++ I/O 都不是默认情况下被缓冲。(否则,请准确)。我要指出的是,我的第一句话以 虽然默认情况下标准输入、输出和错误输出不是这样,因此与 C IO 的交互不那么令人惊讶 这是完全相同的。
【解决方案2】:

是的,ostreams 使用流缓冲区,模板 basic_streambuf 的实例化的某个子类。 basic_streambuf 接口的设计是为了让实现可以在有优势的情况下进行缓冲。

但是,这是实施质量问题。实现不需要这样做,但任何有能力的实现都会这样做。

您可以在 ISO 标准的第 27 章中阅读有关它的所有内容,尽管可能更易读的来源是C++ 标准库:教程和参考 (google search)。

【讨论】:

    【解决方案3】:

    thisofstream内部有一个filebuf指针,可以通过rdbuf函数读取,它指向一个streambuf对象,就是这样:

    streambuf 对象通常与一个特定字符相关联 序列,他们通过内部 内存缓冲区。 缓冲区是内存中的一个数组,预计将 在需要时与物理内容同步 关联的字符序列。

    我把重要的部分加粗了,好像确实使用了缓冲区,但是我不知道或者还没发现它是什么类型的缓冲区。

    【讨论】:

    • 谢谢你,我一定是在查看 C++ 参考页面时错过了这一点。
    • @TimeBomb006:这不是 C++ 参考页。这是一个 C++ 参考页面,如果您正在寻找一个明确的答案,它是不可靠的。
    • 感谢@BenjaminLindley 的澄清。
    猜你喜欢
    • 1970-01-01
    • 2015-08-20
    • 1970-01-01
    • 1970-01-01
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多