我的队友和我意见不一。他认为我们可以将所有内容重定向到/dev/null。这样不会产生任何IO成本,所以影响将是最小的。但我认为这仍然会耗费CPU,我们最好定义一个printf的宏,这样我们就可以重新编写"printf"(可能只是返回)。
因此,我需要一些关于谁是正确的意见。Linux会聪明到足以优化printf吗?我真的很怀疑。
/dev/null
时,任何对printf(3)
的调用仍会评估所有参数,并且在调用write(2)
之前仍然进行字符串格式化处理,该函数将完整格式化的字符串写入进程的标准输出。在内核级别上,数据不会写入磁盘,而是由与特殊设备/dev/null
关联的处理程序丢弃。因此,在最好的情况下,仅通过将stdout重定向到/dev/null
,您将无法绕过或规避评估参数并将它们传递给printf
、printf
背后的字符串格式化工作以及至少一次实际写入数据的系统调用的开销。嗯,这在Linux上是有真正的区别的。该实现仅返回您想要写入的字节数(由调用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
并做所有字符串格式化要快得多。
stdout
,了解它是否为/dev/null
,并抑制那些不包含%n
且返回值未被使用的printf
调用。我们无法肯定没有人这样做过,学生应该了解信息来源,因为工程学的一个重要部分就是知道你知道某些东西的方式(它是否在标准中规定,是否只是默认的,是否可以证明等等)。 - Eric Postpischilprintf
函数将写入到标准输出stdout
,不遵循优化/dev/null
的规范。因此,您需要解析格式字符串并评估任何必要的参数,至少需要一个系统调用,并将缓冲区复制到内核地址空间(与系统调用的成本相比是可以忽略不计的)。
这个答案是基于POSIX具体的文档。
系统接口
dprintf、fprintf、printf、snprintf、sprintf-打印格式化输出
fprintf()
函数应将输出放置在指定的输出流上。printf()
函数应将输出放置在标准输出流stdout
上。sprintf()
函数应将输出和空字节'\0'放置在从*s开始的连续字节中;用户有责任确保有足够的空间可用。基本定义
应该
对于符合POSIX.1-2017的实现,描述了强制执行的功能或行为。应用程序可以依赖于该功能或行为的存在。
printf
函数会写入到标准输出流 (stdout
)。如果连接到 stdout
的文件描述符被重定向到 /dev/null
,则不会在任何地方输出 (但仍然会被写入),但是调用 printf
本身和其格式化的内容仍将发生。
printf()
的成本,该驱动程序提供一个新的FILE *
(取决于您的平台是否支持)。在这种情况下,您可以创建一个数据池,用于丢弃数据。格式化等成本仍然存在,但是写入/dev/null
的操作系统调用消失了。 - glglglstdout
,了解它是/dev/null
,并抑制不包含%n
且返回值未使用的printf
调用。我们无法真正断言没有人这样做,学生们应该学习信息的来源,因为工程的重要部分是知道您如何获得某些信息(它是否在标准中指定,是否仅仅是假设,是否可证明等)。 - Eric Postpischil编写自己的printf()函数,以printf()源代码为指南,并在设置了noprint标志后立即返回。 缺点是实际打印时由于需要解析格式字符串两次,会消耗更多资源。 但在不打印时使用的资源微不足道。不能简单地替换printf(),因为stdio库的新版本中printf()内部调用可能会改变。
void printf2(const char *formatstring, ...);
printf2
函数中使用vprintf
,你就不需要两次解析格式字符串。 - zwolprintf()
的情况下,这意味着如果程序不使用返回值,并且没有 %n
转换,则允许实现什么都不做。void
,并且应该检查格式字符串是否包含 %n
。(如果需要这些副作用,您可以使用带有 NULL
和 0
缓冲区的 snprintf
,但是节省的效果不大)。printf
,是吗?虽然我不确定两个不同的库函数调用是否被认为是不同的... - jinawee0;
将执行无操作,类似于;
。#if DEBUG
#define devlognix(frmt,...) fprintf(stderr,(frmt).UTF8String,##__VA_ARGS__)
#else
#define nadanix 0
#define devlognix(frmt,...) nadanix
#endif
#define XYZKitLogError(frmt, ...) devlognix(frmt)
在这里,XYZKitLogError
将是您的日志命令。甚至可以这样说。
#define nadanix ;
该方法会在编译时踢出所有日志调用,并用0;
或;
替换,以便被解析掉。
您将收到未使用变量警告,但它做了您想要的事情,而这种副作用甚至可以帮助您,因为它告诉您在发布中不需要的计算。
.UTF8String是将NSString转换为const char*的Objective-C方法 - 您不需要使用它。
printf("%d", x=a+b);
如果你重定向到 /dev/null,会发生副作用; 如果你将其改写为一个 do nothing 宏,那么就会失去副作用。 - pmgmyprintf(...) { return; }
可能是你想要的。然后你可以有一个宏来将printf转发到该方法,保留副作用但不格式化任何字符串或调用write。 - msrd0printf
语句中使用副作用是有害的。在代码审查中,我肯定会提出问题。 - MSalters