我正在尝试加速我的C程序,以更快地输出数据。
目前我使用printf()
向外界提供一些数据。它是连续的数据流,因此我无法使用return(data)。
我该如何使用write()
或fwrite()
将数据输出到控制台
而不是文件?
总体而言,我的设置包括使用C编写的程序,其输出进入Python脚本,其中数据进一步处理。我形成了一个管道:
./program_in_c | script_in_python
这样做能够在树莓派上利用更多的处理器内核,从而带来额外的好处。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
write()
函数从缓冲区buf开始写入最多count个字节到由文件描述符fd指定的文件中。
在Linux中,标准输出文件描述符至少为1!在调用write系统调用之前,务必使用flush函数清除stdoutput缓冲区,以确保清除所有先前的垃圾。
fflush(stdout); // Will now print everything in the stdout buffer
write(1, buf, count);
fwrite
:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fflush(stdout);
int buf[8];
fwrite(buf, sizeof(int), sizeof(buf), stdout);
尝试克服已使用的stdio.h
包的缓冲系统几乎没有胜利的可能。如果您尝试使用更大的缓冲区和fwrite()
,您可能不会节省更多时间,并且会使用比必要更多的内存,因为stdio.h
会选择适合写入数据的文件系统的最佳缓冲区大小。
以下简单的程序将显示速度并不重要,因为stdio已经将输出缓冲。
#include <stdio.h>
int
main()
{
int c;
while((c = getchar()) >= 0)
putchar(c);
}
如果您尝试上面和下面的程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main()
{
char buffer[512];
int n;
while((n = read(0, buffer, sizeof buffer)) > 0)
write(1, buffer, n);
if (n < 0) {
perror("read");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
read()
和write()
每个都涉及系统调用,缓冲区大小由您决定。单独的getchar()
和putchar()
调用不是这样。它们只是在打印时将接收到的字符存储在内存缓冲区中,其大小已由stdio.h
库实现根据文件系统确定,并且一旦缓冲区充满数据,它就会刷新缓冲区。如果在第二个程序中增加缓冲区大小,您将看到在某个点之前增加缓冲区大小可以获得更好的结果,但是在此之后您将不会看到速度的增加。与涉及实际I/O所需的时间相比,对库进行的调用次数微不足道,并且选择非常大的缓冲区将从系统中消耗大量内存(而Raspberry Pi的内存在这方面是有限制的,最多为1Gb的RAM)。如果由于如此大的缓冲区而进行交换,则将完全输掉比赛。stat(2)
系统调用告诉您什么是最佳缓冲区大小,并且当stdio
选择实际缓冲区大小时使用它。read(2)
或write(2)
---因为我看到建议您在write(2)
之后使用fflush(3)
,这是完全不连贯的---没有收益(如果您部分使用printf(3)
,部分使用write(2)
(这在您计划执行的管道中更常见),则可能会导致输出不正确地排序,因为缓冲区不是行定向的 ---这是stdio中缓冲输出的另一个特性---)stdio
包和unix文件系统调用进行读写。带着一点运气,您会在互联网上找到它的.pdf。#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main()
{
int i;
char *sep = "";
for (i = 0; i < 10; i++) {
printf("%s%d", sep, i);
sep = ", ";
sleep(1);
}
printf("\n");
}
假设你将在终端上看到(程序中)写出由标记0
到9
的数字,以,
分隔,并以一秒为间隔。
但由于缓冲区,所观察到的与此相当不同。你会看到你的程序等待了10秒钟,而没有在终端上写任何东西,最后,在程序终止时,它一次性地写下所有内容,包括最后的行结束符号,此时shell再度显示提示符。
如果您将程序更改为以下内容:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main()
{
int i;
char *sep = "";
for (i = 0; i < 10; i++) {
printf("%s%d", sep, i);
fflush(stdout);
sep = ", ";
sleep(1);
}
printf("\n");
}
stdio
在每次循环经过时刷新缓冲区。在两个程序中,你都调用了10次printf(3)
,但仅有一个write(2)
在最后写入整个缓冲区。在第二个版本中,你强制 stdio
每次printf之后做一个这样的write(2)
,从而显示程序通过循环时的数据输出情况。stdio
相关的特性可能会让你感到困惑,当你向终端设备打印时,printf(3)
会在每个\n
处刷新输出,但当你将其通过管道运行时,它只在缓冲区完全填满时才这样做。这样可以节省系统调用(例如,在FreeBSD中,stdio选择的缓冲区大小约为32kb,足够强制两个块write(2)
并且达到最优效果(超过该大小你将不会获得更好的效果)。stdio
输出格式化的流,并从下一个进程中读取它。使用管道时,数据通常不会存储到磁盘中。 - Luis ColoradoC语言中的控制台输出几乎与文件相同。一旦您包含了stdio.h
,就可以在控制台输出上写入,名称为stdout
(代表“标准输出”)。最终,以下语句:
printf("hello world!\n");
等同于:
char str[] = "hello world\n";
fwrite(str, sizeof(char), sizeof(str) - 1, stdout);
fflush(stdout);
sizeof(char)
总是为 1
。(在使用“总是”这个词比较安全的少数几个地方之一 :) ) - ryyker
sizeof
的用法。谢谢。 - Adamwrite
然后是fflush(stdout)
的顺序是错误的。write
总是立即写入而不缓冲。而 stdout 并不知道这一点。因此,如果您使用了 stdout,您可能需要在调用write
之前写入缓冲区中的任何内容(通过调用 fflush)。 - user253751write()
与fflush()
没有任何关系,恐怕你的回答完全不正确。 - Luis Colorado