我正在编写一个在终端上生成彩色输出的linux程序。
由于程序stdout可以重定向到文本文件,或者通常重定向到非终端接收器,并且方法应该保持尽可能通用,我需要调用isatty(int fd)
来确定是否应该发送ASCII颜色转义码。
由于我不确定在每次调用printf()之前调用isatty()对性能的影响,我已经实现了一个缓冲区,缓冲了前16个fds的isatty()结果:
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#define TERM_NORMAL "\x1b\x5bm"
#define TERM_BRIGHT "\x1b\x5b\x31m"
#define TERM_BOLD "\x1b\x5b\x31m"
#define TERM_BLINK "\x1b\x5b\x35m"
#define TERM_RED "\x1b\x5b\x33\x31m"
//to prevent unnecessary isatty() calls, provide this lookup table
//for the first 16 fds
//0 means that it has not been checked yet
//1 means the fd is not a tty
//2 means the fd is a tty
char isattybuf[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
inline bool isattybuffered(int fd) {
if(fd >= 0 && fd < sizeof(isattybuf)) {
if(!isattybuf[fd])
isattybuf[fd] = isatty(fd) + 1;
return isattybuf[16] - 1;
} else {
return isatty(fd);
}
}
#define colprintf(col, format, ...) \
if(isattybuffered(fileno(stdout))) \
printf(col format TERM_NORMAL, ## __VA_ARGS__); \
else \
printf(format, ## __VA_ARGS__);
#define colfprintf(col, f, format, ...) \
if(isattybuffered(fileno(f))) \
fprintf(f, col format TERM_NORMAL, ## __VA_ARGS__); \
else \
fprintf(f, format, ## __VA_ARGS__);
//for testing
int main() {
colprintf(TERM_BRIGHT TERM_BLINK, "test1\n");
colprintf(TERM_RED TERM_BRIGHT, "test2\n");
}
但这也有一些缺点:
将消除前两个问题的替代解决方案是使用extern
关键字将缓冲区变量放入单独的c文件中,但即使将代码编译为共享库对象,这也会有效。多个程序同时使用?
不幸的是,ISATTY(3)
联机帮助页没有提供有关该方法性能影响的任何提示。
更新
我只是运行了一些基准测试,看起来isatty()
每次调用时都会进行一次ioctl
系统调用,在我的x86_64 ARCH系统上花费大约700ns或500个时钟周期。 write()系统调用(由printf
调用)花费大约相同的时间,因此如果isatty()
没有缓冲,我会减少1μs或大约一半的输出操作性能(这似乎与终端滚动所需的时间相比可忽略不计,但在将输出重定向到大文本文件时可能变得很重要。特别是当连续调用printf()
时,write
系统调用仅每4096个字节调用一次,因此代码可能花费大部分时间等待isatty()的结果,因此缓冲似乎有意义所有
所以我仍然想听听你对我的缓冲尝试以及我提到的问题的看法。
答案 0 :(得分:5)
一个快速的基准测试表明,至少在达尔文上,isatty不会被缓存,而且每次都会进行ioctl。 文件描述符0 - 99的10 000次检查在2.8GHz i7(mac)上仅用了0.4秒。我会说调用printf的成本远远超过调用isatty。
无论如何,我会使用函数指针。在开始我会调用一个isatty并映射指向函数的指针(printf没有ascii / printf与ascii)然后使用该指针。
马丁