流缓冲问题

时间:2014-06-06 21:11:59

标签: c

mod_rewrite documentation声明严格要求在重写程序中禁用(out)put缓冲。

记住这一点我写了一个简单的程序(我知道它缺少EOF检查,但这不是问题,它为每个循环保存了一个条件检查):

#include <stdio.h>
#include <stdlib.h>
int main ( void )
{
    setvbuf(stdin,NULL,_IONBF,0);
    setvbuf(stdout,NULL,_IONBF,0);
    int character;
    while ( 42 )
    {
        character = getchar();
        if ( character == '-' )
        {
            character = '_';
        }
        putchar(character);
    }
    return 0;
}

进行一些测量后,我感到震惊 - 它比文档提供的演示Perl脚本慢了9,000倍:

#!/usr/bin/perl
    $| = 1; # Turn off I/O buffering
    while (<STDIN>) {
        s/-/_/g; # Replace dashes with underscores
        print $_;
    }

现在我有两个相关的问题:

问题1。我相信这些流可能是行缓冲的,因为Apache在每条路径之后发送一个新行。我对么?将我的程序切换到

setvbuf(stdin,NULL,_IOLBF,4200);

setvbuf(stdout,NULL,_IOLBF,4200);

与Perl one一样快两次。这应该不会影响Apache的性能,不是吗?

问题2。如何在C中编写一个程序,它将使用无缓冲的流(如Perl one)并且执行速度与Perl一样快?

2 个答案:

答案 0 :(得分:2)

问题1:您必须查看代码。它可以是行缓冲的,它可以在每个请求(或请求块)结束时使用fflush,也可以使用具有更大缓冲区的写调用。在任何情况下,它都不会执行你的程序正在进行的每字符I / O.

问题2:我怀疑主要问题是输出。如果你要将整个结果组装在缓冲区中并将其作为一个调用写出来,那么你会更快。但是,这只是意味着您正在进行行缓冲,而不是让库为您处理它。关键是没有缓冲,每个输出调用都会导致系统调用 - 这是非常高的开销。从理论上讲,相同的概念在输入中也适用,但我不确定实现是否会注意到可用的字符并在任何情况下缓冲它们。同样的解决方法 - 读取更大的缓冲区,然后自己拆开。

就个人而言,我会避免所有的setvbuf内容,并在每次请求结束时执行fflush。

答案 1 :(得分:0)

写入终端时,每行后都会刷新stdout。这样您就可以随时看到输出。写入文件时,或者像管道一样,这个自动刷新被禁用。通常在这些情况下,表现更重要。

当进程必须相互交互时,这会导致问题。一个程序写东西。它不会立即发送,而是存储在缓冲区中。第二个程序等待该数据。第一个程序等待来自第二个程序的更多数据,导致死锁。

为避免这种情况,您需要在等待其他输入之前刷新所有输出。在每次读取操作之前,简单fflusuh(stdout)就足够了。这实际上是$|=1在Perl中的作用。使用stdin无需任何操作。

如果性能至关重要且您只需要对单个字节进行操作。使用无缓冲的read / write以大块读取和写入数据。例如:

#include <unistd.h>

int main() {
    char buf[1024];
    while(1) {
        int len = read(0,buf,sizeof(buf));
        for(int i=0;i<len;i++) {
            if ( buf[i] == '-' ) {
                buf[i] = '_';
            }
        }
        write(1,buf,len);
    }
}