C++中endl和\n的差异引发的思考

2022-02-16  本文已影响0人  Domibaba

  今天在处理输出时候遇到一个问题,当输出量级比较小时,使用std::cout << std::endl正常工作,而当输出的量级达到百万左右的时候,程序一直工作异常,原始的程序类似下面的代码片段,每一行输出一个字符:

// 使用std::endl的版本
vector<char> buff;
// 其他给buff赋值的代码
for (const auto &ch : buff) {
    std::cout << ch << std::endl;
}

  而使用\n替代std::endl后,程序可以正常工作,也就是按照如下修改后工作正常:

// 使用\n的版本
vector<char> buff;
// 其他给buff赋值的代码
for (const auto &ch : buff) {
    std::cout << ch << '\n';
}

  那么std::endl\n的区别是啥呢?简而言之:

std::endl会首先在输出流的缓冲区插入一个换行符\n,然后强制刷新输出流的缓冲区;而\n只是简单的在输出流的缓冲区中插入换行符。

  正是因为多了强制刷新输出流缓冲区这个动作,导致使用std::endl的版本明显要慢于使用\n的版本。

  那么为什么会造成这种结果呢?想了解这个问题,首先要弄清楚两个概念: 缓冲区

:流C++标准库为处理 输入输出 (IO)而定义的一种类型,它支持向设备写入数据,以及从设备读取数据的操作。这里的设备可以是文件,可以是控制台,可以是string类型的字符串等等。能读取数据的流叫做 输入流 ,能往设备写入数据的流叫做 输出流 ,例如C++标准库中定义的cin即为输入流,从标准输入读取数据;cout即为输出流,往标准输出写入数据。

缓冲区:这里特指流的缓冲区,也就是保存流读写数据的地方。流的写操作并不马上作用于设备(控制台、文件等),而是保存在缓冲区中,等待合适的时机才会刷新(也就是输出到实际的设备)。

  举个例子:std::cout << "Hello world!;这个输出并不会立马被打印到标准输出,而是存到缓冲区中,等到合适的时机再打印到标准输出,换成写文件就好理解了,每输出一个字符就向文件做一次写操作;和将输出的字符存到缓冲区,等到合适的时候再把缓冲区存的多个字符一次写入文件;这两者相比显然是后者效率要高,能带来性能的提升,因为写文件是耗时操作。

  上面提到的 合适的时机 也叫做 缓冲区刷新 ,也就是把缓冲区输出到设备(文件、控制台等),缓冲区刷新可能由以下几个方面触发:

  回到最开始的问题,那么答案就是:每次输出都触发缓冲区的刷新会比让系统决定在合适的时候刷新缓冲区效率低,因此除非有明确的手动刷新缓冲区诉求,否则建议在频繁操作中使用\n代替std::endl;也许单次的std::cout << "Hello world!" << std::endlstd::cout << "Hello world!\n"差别并不大,但是频繁的操作前者的性能显然比后者低。我们使用2个程序来做对比验证,输出一百万次,程序1每次输出都刷新,程序2使用系统默认的缓冲区刷新策略:

// ./test_flush_immediately
#include <iostream>
int main(int argc, char **argv)
{
    for (int i = 0; i < 1000000; i++) {
        std::cout << i << std::endl;
    }
}
// ./test_flush_later
#include <iostream>
int main(int argc, char **argv)
{
    for (int i = 0; i < 1000000; i++) {
        std::cout << i << '\n';
    }
}

  使用g++编译,测试结果如下:

time ./test_flush_immediately
./test_flush_immediately  `1.93`s user 1.20s system 57% cpu 5.485 total
 
time ./test_flush_later
./test_flush_later  1.81s user 1.22s system 55% cpu 5.486 total
time ./test_flush_immediately >/dev/null
./test_flush_immediately >/dev/null  1.56s user 0.53s system 99% cpu 2.111 total

time ./test_flush_later >/dev/null
./test_flush_later >/dev/null  0.41s user 0.01s system 97% cpu 0.423 total
time ./test_flush_immediately >file
./test_flush_immediately > file  `1.76`s user 6.04s system 87% cpu 8.933 total
 
time ./test_flush_later >file
./test_flush_later > file  0.41s user 0.02s system 98% cpu 0.440 total
  结论:std::endl比使用\n耗时,且在不同的输出设备上耗时也有差异(输出到文件时尤其明显,使用std::endl的系统调用需要6.04s,而使用\n只需要0.02s),因此除非明确需要手动刷新缓冲区,否则建议使用\n
上一篇 下一篇

猜你喜欢

热点阅读