为什么endl被用作"\n"的同义词,即使它会带来显著的性能损失?

8
这个程序:
#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 << i << ": " << msg << endl;,然后您会得到类似于这样的东西:

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

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

正如您所看到的,运行时间增加了两倍以上,程序从在操作系统中花费最少的时间到在操作系统中花费近一半的时间。
这两个版本的程序具有相同的输出,并且根据标准保证在每个平台上具有相同的输出。
鉴于此,为什么人们还要坚持使用“endl”作为“\n”的同义词?
编辑:如果不明显,这个问题旨在成为一个引导性问题,并且是为了教学目的而在这里。我知道为什么会存在性能惩罚。

你能告诉我标准的哪一部分保证输出在所有平台上都是相同的吗? - Glen
2
请参见:https://dev59.com/rnVC5IYBdhLWcg3wsTRi - Mark Byers
6个回答

21

我不确定。将std::endl插入输出流被定义为等同于插入.widen('\n')然后调用flush(),但许多程序员仍然坚持在没有必要刷新的情况下使用std::endl,例如他们继续立即输出其他内容。

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


2
你知道吗,你是第一个给出实际答案的人。:-) 在这个网站上问引导性问题是没有用的。我需要找到更好的表达问题的方式,以便让它们具有指导性。 - Omnifarious
我确实给了一个实际的答案,当然你可能不同意。 - anon
@Neil Butterworth,好的。是的,Charles是第二个给出实际答案的人。 :-) 你的也是。 - Omnifarious
严格来说,我的答案并非完全是假设;它是我与极少数开发人员讨论此问题时提取出来的。这种推断可能具有非常不稳定的统计学有效性。 - CB Bailey
对这个问题的兴趣已经消退了,而你肯定有最好的答案,并且有实际的真实数据来支持它。 :-) - Omnifarious

4

并非每个人都特别关注性能。对于某些应用程序来说,保证流被刷新更加重要。

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


几乎在我看到使用 endl 的所有情况中,流是否立即刷新实际上都没有什么影响。 :-) - Omnifarious
记录应用程序、网络通信和数据库是首先想到的。 - anon
你是第一个给出实际答案的人,尽管我不同意。 :-) - Omnifarious
很少有情况需要在同一应用程序中同时使用性能和流。在这种情况下,通常更容易保持一致并使用std::endl(除了示例程序以演示差异之外)。我真的关心应用程序需要15秒还是30秒吗? - Martin York
@erick2red,除了'\n'之外,endl并不比它更具可移植性。在使用它时,没有可移植性和性能的权衡。在我看来,使用endl是完全没有意义的。人们几乎从不需要在使用它时实际刷新流,他们只是在书中看到它并按照那种方式做而没有思考或理解差异。 - Omnifarious
显示剩余2条评论

4
据我所知,endl同时刷新流,这可能是性能下降的原因。

你是对的。std::endl刷新输出缓冲区,而“\n”则不会。 - Mark Byers
我知道为什么会存在性能惩罚。 :-) 我的问题是:为什么::std::endl仍然只是“惯例”,即使它有性能惩罚? - Omnifarious
1
可能是因为大多数程序员喜欢让用户看到输出,而不是强制等待缓冲区填满。考虑到真实输出设备的速度,这并不重要。 - Hans Passant

2
我倾向于在字符串流中使用endl,因为它可以很容易地发现缺少的换行符。

2

我猜教学文本使用 std::endl 的原因是认为对于初学者来说更简单和不容易混淆,随后人们习惯了使用它。


0

真正的问题是,为什么编译器在编译endl版本时会出现如此混乱的情况?如果它们保证具有相同的语义,那么它们也应该具有相同的运行时。

编辑:显然,我不知道endl会刷新流...这就是你没有查找信息的后果。


它们实际上绝对不具有相同的语义。 - Omnifarious
好的,它们可以这样做,但是C++标准并不保证。 - anon
@Neil Butterworth,标准允许::std::cout是无缓冲的吗? - Omnifarious
1
不是的,但标准没有指定输出 '\n' 的效果是什么。 - anon
1
@Neil Butterworth,啊哈。我想这是有先例的。C语言中的stdio库会特殊处理'\n'并在遇到它时自动刷新。他们称之为“行缓冲”。 - Omnifarious
在许多Unix系统中,只有当输出连接到交互式终端时,std::cout和stdout才是行缓冲的,而不是(例如)附加到文件或其他设备。 - CB Bailey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接