34

下面是两个将 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;
}

感谢您的时间。

4

3 回答 3

14

是的,所有流操作都是缓冲的,尽管默认情况下标准输入、输出和错误输出不是这样,因此与 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 中所做的实验以找到最佳位置 :)

于 2012-05-04T15:12:14.573 回答
13

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

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

您可以在 ISO 标准的第 27 章中阅读有关它的所有内容,尽管可能更具可读性的来源是C++ 标准库:教程和参考谷歌搜索)。

于 2012-05-04T13:33:49.337 回答
3

根据thisofstream有一个内部filebuf指针,可以通过rdbuf函数读取,它指向一个streambuf对象,即 this:

streambuf对象通常与一个特定的字符序列相关联,它们通过内部内存缓冲区从中读取和写入数据。缓冲区是内存中的一个数组,在需要时与相关字符序列的物理内容同步。

我将重要的部分加粗,似乎它确实使用了缓冲区,但我不知道或没有发现它是什么类型的缓冲区。

于 2012-05-04T13:30:33.843 回答