为什么使用带有ostringstream的std :: endl会影响输出速度?

时间:2012-10-01 14:29:40

标签: c++ performance flush stringstream ostream

我正在计算将文本打印到标准输出的各种方法之间的差异。我正在使用coutprintf测试ostringstream\nstd::endl。我希望std::endl能够与cout产生差异(而且确实如此),但我没想到它会使用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

这些结果是一致的。

3 个答案:

答案 0 :(得分:14)

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

除非您真的希望刷新流,否则通常建议不要使用std::endl。如果这对您来说非常重要,取决于您的使用案例。

关于为什么flush即使在ostringstream上也没有性能影响(不应该发生刷新):似乎需要一个实现来至少构造sentry对象。那些需要检查good的{​​{1}}和tie。应该能够优化对ostream的调用。这是基于我对libcpp和libstdc ++的解读。

经过一些阅读后,有趣的问题似乎是这样的:构建哨兵对象真的需要pubsync的实现吗?如果没有,这似乎是一个“实施质量”问题给我。但我实际上认为它需要,因为即使basic_ostringstream::flush可以更改为设置basic_stringbug

答案 1 :(得分:1)

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

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

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

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

......甚至

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

尽管如此,流实现可以应用多种改进来减少检查。当然,这是否完成将取决于实施。

答案 2 :(得分:0)

使用std::endl等同于编写

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

除非您确实想要触发刷新和/或不关心输出性能,否则请勿使用std::endl

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