【问题标题】:Freepascal flushes stdout on every output under Windows?Freepascal 在 Windows 下的每个输出上刷新标准输出?
【发布时间】:2014-11-15 17:54:10
【问题描述】:

请看以下四个程序。在 Windows 下使用 Freepascal 构建它们并运行,将输出重定向到任何文件,并注意它所花费的时间。

我的结果是:所有程序运行的时间大致相同(大约 6 秒),尽管第四个程序的输出字节数增加了 100 倍。这意味着第四个程序每字节输出的运行速度比其他树快得多。

对于第二个程序,缓慢的原因很明显:flush 调用。对于第三个程序,原因并不那么明显,但可以合理地假设每次调用 writeln 到 stdout 都会隐式刷新输出缓冲区。

但是,尚不清楚为什么第一个程序比第四个慢。然而,添加flush(output);(见程序 2)并没有太大改变时间的事实似乎意味着即使在每个write 之后 FPC 也会刷新输出缓冲区,这可以解释所有行为。仅当输出到标准输出时才会发生这种情况,甚至重定向;如果我使用分配/重写显式输出到特定文件,则没有刷新的程序运行速度比使用刷新的程序快得多——正如预期的那样。

Linux 下运行时间分别为 0.01s、0.65s、0.01s、0.30s(输出放大 100 倍)。这里显然flush()会减慢程序的速度,所以在Linux下FPC似乎不会每次都刷新stdout。

我试图用谷歌搜索 FPC 是否真的在每个输出上刷新标准输出缓冲区(无论是 write 还是 writeln),但除了 http://www.freepascal.org/docs-html/rtl/system/flush.html 的刷新函数文档中的示例程序中的注释外,没有找到任何信息,评论提到writeln 到“输出”总是会导致刷新[而不是write]。但是,那里的示例在 Windows 下和 Linux 下都没有产生预期的输出。事实上,在 Windows 下,无论是否重定向,每次 write 和 writeln 后,输出似乎都会被刷新,而在 Linux 下,当输出未重定向时也是如此。在具有重定向输出的 Linux 下,似乎根本没有隐式刷新。

所以,我的问题是:

  1. FPC 是否在每次输出后刷新输出缓冲区,无论是 write 还是 writeln,在 Windows 上是否将输出重定向到文件?
  2. 如果是,那么有什么方法可以关闭它(某些编译器指令或解决方法)?我仍然需要将输出保留到标准输出,这样如果我在没有任何重定向的情况下启动程序,它将向控制台输出文本。 (我知道由于缓冲,我可能会看到它在奇怪的时间出现,这不是问题。)
  3. 如果不是,那为什么第一个程序比第四个运行得慢很多?

我的系统是 Windows XP,在 Kubuntu 14.04 下的 VirtualBox 下带有 FPC 2.6.4,而 Kubuntu 14.04 本身带有 FPC 2.6.2。我没有机会尝试在真正的 Windows 机器上运行它,但我有理由相信那里的情况是一样的。


节目:

var i,j:integer;
    s:string;
begin
for j:=1 to 1000 do begin
   for i:=1 to 10 do 
      write('!');
end;
end.

var i,j:integer;
    s:string;
begin
for j:=1 to 1000 do begin
   for i:=1 to 10 do begin
      write('!');
      flush(output);
   end;
end;
end.

var i,j:integer;
    s:string;
begin
for j:=1 to 1000 do begin
   for i:=1 to 10 do 
      writeln('!');
end;
end.

var i,j:integer;
    s:string;
begin
for j:=1 to 10000 do begin
   s:='';
   for i:=1 to 100 do 
      s:=s+'!';
   write(s);
end;
end.

【问题讨论】:

  • 1.这是真的。举个例子:writeln('please type this'); answer := readln; 如果 writeln 没有刷新,那么最终用户就在黑色控制台前。即使 i/o 没有被重定向,这也会追加。
  • @user3661500,我理解这一点,并且确实希望看到writeln 刷新缓冲区,这就是我的第三个程序的内容。然而,令我惊讶的是,即使write 也会刷新它。

标签: windows stdout flush freepascal


【解决方案1】:

为防止 Stdout 被刷新,请将以下代码 sn-ps 插入到您的程序中:

// Textrec is defined in sysutils
uses
  sysutils;

// ...

// disabled flushing after each write(ln) on Output, do this at the start of the program
Textrec(Output).FlushFunc:=nil;

但请注意,这意味着 writelns 可能不会在程序结束之前完成。

您甚至可以通过增加 stdout 的输出缓冲区来进一步加速程序:

// define buffer
var
  buf : array[1..100000] of byte;

  // ...

  // install buffer, do this at the start of the program
  SetTextBuf(Output,buf,sizeof(buf));

【讨论】:

  • FlushFunc 的技巧非常有趣,谢谢!会试试这个。
  • 已尝试并可以确认它有效并解决了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 2019-03-02
  • 2019-11-22
  • 2013-11-04
  • 1970-01-01
  • 2021-03-30
相关资源
最近更新 更多