【问题标题】:Why does using std::endl with ostringstream affect output speed?为什么将 std::endl 与 ostringstream 一起使用会影响输出速度?
【发布时间】:2012-10-01 14:29:39
【问题描述】:

我正在计时将文本打印到标准输出的各种方式之间的差异。我正在使用\nstd::endl 测试coutprintfostringstream。我希望std::endlcout 有所不同(确实如此),但我没想到它会降低ostringstream 的输出速度。我认为使用std::endl 只会将\n 写入流,它仍然只会被刷新一次。这里发生了什么?这是我所有的代码:

// cout.cpp
#include <iostream>

using namespace std;

int main() {
  for (int i = 0; i < 10000000; i++) {
    cout << "Hello World!\n";
  }
  return 0;
}

// printf.cpp
#include <stdio.h>

int main() {
  for (int i = 0; i < 10000000; i++) {
    printf("Hello World!\n");
  }
  return 0;
}

// stream.cpp
#include <iostream>
#include <sstream>

using namespace std;

int main () {
  ostringstream ss;
  for (int i = 0; i < 10000000; i++) {
    ss << "stream" << endl;
  }
  cout << ss.str();
}

// streamn.cpp
#include <iostream>
#include <sstream>

using namespace std;

int main () {
  ostringstream ss;
  for (int i = 0; i < 10000000; i++) {
    ss << "stream\n";
  }
  cout << ss.str();
}

这是我的 Makefile

SHELL:=/bin/bash

all: cout.cpp printf.cpp
    g++ cout.cpp -o cout.out
    g++ printf.cpp -o printf.out
    g++ stream.cpp -o stream.out
    g++ streamn.cpp -o streamn.out
time:
    time ./cout.out > output.txt
    time ./printf.out > output.txt
    time ./stream.out > output.txt
    time ./streamn.out > output.txt

这是我运行 make 后跟 make time 时得到的结果

time ./cout.out > output.txt

real    0m1.771s
user    0m0.616s
sys 0m0.148s
time ./printf.out > output.txt

real    0m2.411s
user    0m0.392s
sys 0m0.172s
time ./stream.out > output.txt

real    0m2.048s
user    0m0.632s
sys 0m0.220s
time ./streamn.out > output.txt

real    0m1.742s
user    0m0.404s
sys 0m0.200s

这些结果是一致的。

【问题讨论】:

  • 这可能会回答你的问题:stackoverflow.com/questions/213907/c-stdendl-vs-n 简而言之:是的,std::endl 刷新输出
  • 我不知道我是否读错了这个问题,但我认为他在问为什么刷新字符串流会有性能问题。刷新字符串流有哪些额外的工作不能只留给.str() 的调用?对于cout,渲染到终端需要时间,刷新字符串流是否有任何可见的副作用?
  • @stefan 我实际上已经阅读了这个问题,但部分我想知道刷新字符串流意味着什么。我认为它基本上什么都不做,但显然我错了。
  • @stefan 我用 O3 优化过,每种方法的相对效率是一样的。
  • @bames53 好问题。 g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

标签: c++ performance flush stringstream ostream


【解决方案1】:

std::endl 触发流的刷新,这大大减慢了打印速度。见http://en.cppreference.com/w/cpp/io/manip/endl

通常建议不要使用std::endl,除非你真的想要刷新流。如果这对您真的很重要,取决于您的用例。

关于为什么flush 甚至对ostringstream(不应该发生刷新)也会产生性能影响:似乎至少需要一个实现来构造哨兵对象。那些需要检查ostreamgoodtie。对pubsync 的调用应该能够被优化掉。这是基于我对 libcpp 和 libstdc++ 的阅读。

阅读更多内容后,有趣的问题似乎是:构造哨兵对象真的需要basic_ostringstream::flush 的实现吗?如果不是,这对我来说似乎是一个“实施质量”问题。但我实际上认为它需要这样做,因为即使是 basic_stringbug 也可以更改为设置其 badbit

【讨论】:

  • 其实我是知道的,但我不知道它影响了stringstream的性能。刷新 cout 意味着输出缓冲区中的任何内容以输出,刷新 stringstream 是什么意思?文本刷新到的输出是什么?
  • @gsingh2011 这就是我的想法。但是basic_ostringstream实际上已经附加了basic_stringbuf,并且是构建flush文档中提到的哨兵所必需的。
【解决方案2】:

流上的每个输出操作都有多个步骤:

  • 它会检查流是否处于良好状态。
  • 它检查是否需要刷新其他流。
  • 生成字符序列。
  • 它检查是否有足够的空间用于书写字符。
  • endl 的使用会在流缓冲区上调用一个额外的虚函数。

我个人认为,与其他操作相比,额外的虚函数调用实际上影响相对较小。您还可以通过分析此输出来验证此猜测:

out << "stream" << '\n';

...甚至

out << "stream" << out.widen('\n');

也就是说,流实现可以应用许多改进来减少检查。当然,这是否完成将取决于实施。

【讨论】:

    【解决方案3】:

    使用std::endl相当于写

    stream << "\n";
    stream.flush();
    

    不要使用std::endl,除非你真的想触发刷新,和/或不关心输出性能。

    另外,不用担心不同平台上的不同行尾,您的实现会将"\n" 转换为适合您平台的行尾。

    【讨论】:

      猜你喜欢
      • 2014-05-16
      • 2020-07-23
      • 2015-12-20
      • 1970-01-01
      • 2017-05-06
      • 1970-01-01
      • 2018-09-12
      • 1970-01-01
      • 2022-11-06
      相关资源
      最近更新 更多