繁体   English   中英

为什么C ++中的缓冲很重要?

[英]Why is buffering in C++ important?

我试图打印Hello World 200,000次,它让我永远,所以我必须停下来。 但是在我添加一个char数组作为缓冲区后,它花了不到10秒。 为什么?

在添加缓冲区之前:

#include <iostream> 
using namespace std;

int main() {
        int count = 0;
        std::ios_base::sync_with_stdio(false);
        for(int i = 1; i < 200000; i++)
        {       
                cout << "Hello world!\n";
                count++;
        }
                cout<<"Count:%d\n"<<count;
return 0;
}

这是在添加缓冲区之后:

#include <iostream> 
using namespace std;

int main() {
        int count = 0;
        std::ios_base::sync_with_stdio(false);
        char buffer[1024];
        cout.rdbuf()->pubsetbuf(buffer, 1024);
        for(int i = 1; i < 200000; i++)
        {       
                cout << "Hello world!\n";
                count++;
        }
                cout<<"Count:%d\n"<<count;
return 0;
}

这让我想起了Java。 使用BufferReader读取文件有什么好处?

对于文件操作的立场,写入内存(RAM)总是比直接写入磁盘上的文件更快。

为了说明,我们定义:

  • 每个写IO操作到磁盘上的文件花费1毫秒
  • 每次通过网络写入IO操作到磁盘上的文件的成本为5毫秒
  • 每次写IO操作到内存的成本为0.5毫秒

假设我们必须将一些数据写入文件100次。

案例1:直接写入磁盘上的文件

100 times x 1 ms = 100 ms

案例2:通过网络直接写入磁盘上的文件

100 times x 5 ms = 500 ms

案例3:在写入磁盘上的文件之前在内存中缓冲

(100 times x 0.5 ms) + 1 ms = 51 ms

案例4:在通过网络写入磁盘上的文件之前在内存中缓冲

(100 times x 0.5 ms) + 5 ms = 55 ms

结论

内存缓冲总是比直接操作更快。 但是,如果您的系统内存不足并且必须与页面文件交换,那么它将再次变慢。 因此,您必须在内存和磁盘/网络之间平衡IO操作。

写入磁盘的主要问题是写入时间不是数字字节的线性函数,而是具有巨大常数的仿射函数。

在计算方面,这意味着,对于IO,您具有良好的吞吐量(低于内存,但仍然相当不错),但是您的延迟较差(通常比网络好一点)。

如果您查看HDD或SSD的评估文章,您会注意到读/写测试分为两类:

  • 随机读取的吞吐量
  • 连续读取的吞吐量

后者通常明显大于前者。

通常,操作系统和IO库应该为您抽象,但正如您所注意到的,如果您的例程是IO密集型的,您可能会通过增加缓冲区大小来获得。 这是正常的,该库通常适合各种用途,因此为普通应用提供了良好的中间立场。 如果您的应用程序不是“平均”,那么它的执行速度可能不会那么快。

你使用什么编译器/平台? 我认为这里没有显着差异(RedHat,gcc 4.1.2); 两个程序都需要5-6秒才能完成(但“用户”时间约为150毫秒)。 如果我将输出重定向到一个文件(通过shell),总时间约为300毫秒(所以6秒的大部分花费在等待我的控制台赶上程序的时间)。

换句话说,输出应该默认缓冲,所以我很好奇为什么你会看到如此巨大的加速。

3个与切线相关的注释:

  1. 您的程序有一个错误的错误,因为您只打印199999次而不是规定的200000次(从i = 0开始或以i <= 200000结束)
  2. 在输出计数时,你将printf语法与cout语法混合......对此的修复是显而易见的。
  3. 在输出到控制台时,禁用sync_with_stdio会为我带来一个小的加速(大约5%),但重定向到文件时影响可以忽略不计。 这是一个微观优化,在大多数情况下你可能不需要(恕我直言)。

如果你有一个缓冲区,你会得到更少的实际I / O调用,这是缓慢的部分。 首先,缓冲区被填充,然后进行一次I / O调用以刷新缓冲区。 在Java或任何其他I / O速度慢的系统中同样有用。

cout函数包含许多隐藏和复杂的逻辑,一直到内核,所以你可以将文本写入屏幕,当你以这种方式使用缓冲区时,你基本上做一个批处理请求而不是重复复杂的I / O电话。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM