【问题标题】:Why does endl get used as a synonym for "\n" even though it incurs significant performance penalties?为什么 endl 被用作“\n”的同义词,即使它会导致显着的性能损失?
【发布时间】:2010-01-23 11:35:57
【问题描述】:

这个程序:

#include <iostream>
#include <cstdlib>
#include <string>

int main(int argc, const char *argv[])
{
   using ::std::cerr;
   using ::std::cout;
   using ::std::endl;

   if (argc < 2 || argc > 3) {
      cerr << "Usage: " << argv[0] << " [<count>] <message>\n";
      return 1;
   }
   unsigned long count = 10000;
   if (argc > 2) {
      char *endptr = 0;
      count = ::std::strtoul(argv[1], &endptr, 10);
      if ((argv[1][0] == '\0') || (*endptr != '\0')) {
         cerr << "Usage: " << argv[0] << " [<count>] <message>\n";
         return 1;
      }
   }
   const ::std::string msg((argc < 3) ? argv[1] : argv[2]);
   for (unsigned long i = 0; i < count; ++i) {
      cout << i << ": " << msg << '\n';
   }
   return 0;
}

当这样计时:

$ time ./joe 10000000 fred >/dev/null

real  0m15.410s
user  0m10.551s
sys   0m0.166s

执行需要 15.4 秒的实时时间。用这个替换输出行:cout &lt;&lt; i &lt;&lt; ": " &lt;&lt; msg &lt;&lt; endl;,你最终会得到这样的结果:

$ time ./joe 10000000 fred >/dev/null

real  0m39.115s
user  0m16.482s
sys   0m15.803s

如您所见,运行时间增加了一倍多,并且程序从在操作系统中花费的时间最短变为在操作系统中花费了将近一半的时间。

两个版本的程序具有相同的输出,并且由标准保证在每个平台上具有相同的输出。

鉴于此,为什么人们坚持使用endl作为'\n'?的同义词

编辑:如果不是很明显,这个问题旨在成为一个引导性问题,并用于教学目的。我知道为什么会存在性能损失。

【问题讨论】:

标签: c++ performance iostream


【解决方案1】:

我不确定。在输出流中插入std::endl被定义为等同于插入.widen('\n')然后调用flush(),但是许多程序员坚持使用std::endl,即使没有理由刷新,例如他们继续立即输出别的东西。

我的假设是它来自一个错误的信念,即它在某种程度上更便携,因为它没有明确使用特定的换行符。这是不正确的,因为\n 必须始终由流库映射到系统对非二进制文件的正确换行符序列。

【讨论】:

  • 你知道,你是第一个给出实际答案的人。 :-) 提出引导性问题会让你在这个网站上无处可去。我需要找到一种更好的方式来陈述我打算用于指导的问题。
  • 碰巧我也给出了一个实际的答案。当然,你可能不同意。
  • @Neil Butterworth,哦,好吧。是的,查尔斯是第二个给出实际答案的人。 :-) 你的也是。
  • 另外,严格来说,我的回答并不完全是假设;这是从我与之交谈过的开发人员的极小样本中提取的。外推可能具有非常不稳定的统计有效性。
  • 对这个问题的兴趣已经消退,你肯定有最好的答案,有实际的真实数据来支持它。 :-)
【解决方案2】:

Afaik,endl 也会刷新流,这可能是导致性能下降的原因。

【讨论】:

  • 你是对的。 std::endl 刷新输出缓冲区,而 "\n" 不会。
  • 我知道为什么会存在性能损失。 :-) 我的问题是:为什么::std::endl 仍然只是“做了什么”,即使它有性能损失?
  • 可能是因为大多数程序员喜欢他们的用户可以看到输出而不是强迫他们等到缓冲区填满。考虑到实际输出设备的速度,这并不重要。
【解决方案3】:

并不是每个人都如此关心性能。对于某些应用程序,确保流被刷新更为重要。

编辑:另外,我发现endl'\n' 更容易输入:-)

【讨论】:

  • 在我看到endl 几乎没有使用过的情况下,是否在当时和那里刷新流实际上很重要。 :-)
  • 日志应用程序、网络通信和数据库立即浮现在脑海中。
  • 人们应该使用::std::cerr 或其他一些无缓冲的流进行日志记录。我从未见过使用 iostream 进行网络通信或写入数据库的严肃网络应用程序或数据库。
  • 您是第一个给出实际答案的人,尽管我不同意。 :-)
  • 在同一个应用程序中很少有需要性能和流的用途。在这种情况下,通常更容易保持一致并使用 std::endl (除了示例程序来演示差异)。我真的在乎一个应用程序需要 15 秒还是 30 秒。
【解决方案4】:

我倾向于在 stringstreams 上使用 endl,因为它可以很容易地发现丢失的换行符。

【讨论】:

    【解决方案5】:

    我的猜测是教学文本使用std::endl,相信它对初学者来说更简单,更不容易混淆,后来人们习惯了使用它。

    【讨论】:

      【解决方案6】:

      真正的问题是,为什么编译器会在编译 endl 版本时做出这样的狗早餐?如果保证它们具有相同的语义,那么它们也应该具有相同的运行时。

      编辑:显然,我不知道 endl 刷新了流……这就是你不查找它的结果。

      【讨论】:

      • 事实上,它们绝对没有相同的语义。
      • 嗯,他们可以做到,只是 C++ 标准不保证他们这样做。
      • @Neil Butterworth,标准允许::std::cout 不被缓冲?
      • 没有,但是标准没有规定输出'\n'的效果是什么。
      • @Neil Butterworth,啊。 我想这是有先例的。 C 中的 stdio 库特别对待 '\n' 并在遇到它时自动刷新。他们称之为“行缓冲”。
      猜你喜欢
      • 1970-01-01
      • 2020-04-02
      • 2020-06-28
      • 1970-01-01
      • 1970-01-01
      • 2012-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多