即使我将输出重定向到/ dev/null,printf仍会有成本吗?

Mic*_*l P 60 c linux embedded performance dev-null

我们有一个包含大量打印消息的守护进程.由于我们正在开发具有弱CPU和其他约束硬件的嵌入式设备,因此我们希望在最终版本中最小化printf消息的任何类型的成本(IO,CPU等).(用户没有控制台)

我的队友和我有分歧.他认为我们可以将所有内容重定向到/ dev/null.它不会花费任何IO,所以感情将是最小的.但我认为它仍将花费CPU,我们最好为printf定义一个宏,这样我们就可以重写"printf"(也许只是返回).

所以我需要一些关于谁是对的意见.Linux是否足够聪明以优化printf?我真的很怀疑.

iBu*_*Bug 71

差不多.

当您将程序的stdout重定向到时/dev/null,任何调用printf(3)仍将评估所有参数,并且字符串格式化过程仍将在调用之前进行write(2),这会将完整格式化的字符串写入进程的标准输出.在内核级别,数据不会写入磁盘,而是由与特殊设备关联的处理程序丢弃/dev/null.

所以在最好的情况下,你不会绕过或逃避评估参数并传递它们的开销,printf后面的字符串格式化工作printf,以及至少一个实际写入数据的系统调用,只需将stdout重定向到/dev/null.嗯,这是Linux的真正区别.实现只返回您想要写入的字节数(由您调用的第3个参数指定write(2))并忽略其他所有内容(请参阅此答案).根据您正在编写的数据量以及目标设备(磁盘或终端)的速度,性能差异可能会有很大差异.在嵌入式系统中,一般来说,通过重定向来切断磁盘写入/dev/null可以为非常少量的写入数据节省相当多的系统资源.

虽然从理论上讲,程序可以/dev/null在符合标准(ISO C和POSIX)的标准限制内检测并执行某些优化,但基于对常见实现的一般理解,它们实际上没有(即我不知道任何Unix或Linux)系统这样做).

POSIX标准强制要求对任何调用的标准输出进行写入printf(3),因此write(2)根据相关的文件描述符,它不符合标准 - 禁止调用.有关POSIX要求的更多详细信息,您可以阅读Damon的答案.哦,快速说明:所有Linux发行版都符合POSIX标准,尽管没有被证明是这样的.

请注意,如果printf完全更换,某些副作用可能会出错,例如printf("%d%n", a++, &b).如果你真的需要根据程序执行环境来抑制输出,可以考虑设置一个全局标志并包装printf以在打印前检查标志 - 它不会使程序变慢到可见性能损失的程度,因为单个条件检查比调用和执行所有字符串格式化快得多printf.

  • 像这样的答案应该声明它们是基于对常见实现的一般理解,而不是基于特定文档.理论上,没有理由C实现可能不检查`stdout`,学习它是/ dev/null,并且抑制不包含`%n`并且不使用其返回值的`printf`调用.我们无法断言没有人这样做过,学生应该学习信息的来源,因为工程的一个重要部分就是知道你是如何知道的(它是在标准中指定的,它是假设的,是可证明的,等等) ). (28认同)
  • @EricPostpischil参见[Damon的答案](/sf/answers/3793974691/)以获取POSIX标准的参考 - 标准禁止使用`write(2)`调用写stdout. (7认同)
  • AFAIK,控制台输出非常慢,因此写入/ dev/null节省的时间可能很长.但是,如果您要写入文件,它将不那么有效. (4认同)
  • @ SJuan76写入终端不一定慢,*显示*它(即取决于终端). (3认同)

Dam*_*mon 41

printf函数写入stdout.它不符合优化/dev/null.因此,您将有解析格式字符串和评估任何必要参数的开销,并且您将至少有一个系统调用,另外您将缓冲区复制到内核地址空间(与系统调用的成本相比,这是可忽略的) .

这个答案基于POSIX的具体文档.

系统接口
dprintf,fprintf,printf,snprintf,sprintf - 打印格式化输出

fprintf()函数应将输出放在命名输出流上.printf()函数应将输出放在标准输出流stdout上.sprintf()函数应将输出后跟空字节'\ 0'放在从*s开始的连续字节中; 用户有责任确保有足够的空间.

基本定义

对于符合POSIX.1-2017的实现,描述必需的功能或行为.应用程序可以依赖于特征或行为的存在.

  • -1,这忽略了as-if规则(适用于*整个实现*,而不仅仅是编译器).如果整个实现可以证明可观察行为没有区别,那么即使在编译完成之后,它也可以进行任何优化.现在,如果我们谈论的是C语言之外的其他语言,那么你可能是对的. (11认同)
  • @Kevin**C**标准的as-if规则是否也适用于**POSIX**标准? (4认同)
  • 内核负责将缓冲区复制到内核地址空间,而null驱动程序*可能*省略了该步骤(在Linux上,它肯定会 - 它甚至不检查它是否是有效地址) (2认同)
  • @Kevin:不,它说“对齐”,那是不同的。相反:C 编程语言的语言相关服务规定:_“寻求使用 ISO C 标准声明一致性的实现者应声明 POSIX 一致性,如 POSIX 一致性中所述”_。 (2认同)

Som*_*ude 10

printf函数写入stdout.如果连接到的文件描述符stdout被重定向到/dev/null那么没有输出将被写入任何地方(但它仍将被写入),但是printf对它自己的调用和它所做的格式化仍然会发生.

  • 像这样的答案应该声明它们是基于对常见实现的一般理解,而不是基于特定文档.理论上,没有理由C实现可能不检查`stdout`,学习它是/ dev/null,并且抑制不包含`%n`并且不使用其返回值的`printf`调用.我们无法断言没有人这样做过,学生应该学习信息的来源,因为工程的一个重要部分就是知道你是如何知道的(它是在标准中指定的,它是假设的,是可证明的,等等) ). (2认同)

MAX*_*XdB 5

使用printf()源作为指导来编写自己的包装printf(),如果设置了noprint标志,则立即返回.这样做的缺点是,实际打印时会因为必须解析格式字符串两次而消耗更多资源.但是在不打印时它使用的资源可以忽略不计.不能简单地替换printf(),因为printf()中的底层调用可以使用较新版本的stdio库进行更改.

void printf2(const char*formatstring,...);

  • 如果在`printf2`中使用`vprintf`,则不必解析格式字符串两次. (5认同)