В настоящее время я пишу программу linux, которая производит цветной вывод на терминале. Поскольку программа stdout может перенаправляться в текстовый файл или, как правило, на нетерминальный приемник, а методы должны оставаться как можно более универсальными, мне нужно позвонить isatty(int fd)
, чтобы определить, следует ли мне отправлять коды возврата цвета ASCII.Влияние производительности частых вызовов isatty()
Поскольку я не уверен о влиянии на производительность вызова isatty() перед каждым вызовом PRINTF(), я реализовал буфер, который буферизует isatty() результаты первых 16 FDS:
#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");
}
Но это имеет несколько недостатков, а также:
- поскольку это будет часть заголовка файла библиотеки, будет буфер массивом для каждого с файлом, который содержит файл заголовка
- Соответственно, isatty() можно назвать n раз, где n - число c-файлов с использованием кода
- Информация о буфере может быть неправильной, если файл открыт, вызывается isatty(), файл затем закрывается, а затем терминал открыт с тем же дескриптором
альтернативы решения, которое позволит устранить первые две проблем, было бы поместить переменный буфер в отдельный файл Си, используя extern
ключевое слово, но это будет работать, даже если код компилируется как объект общей библиотеки и используется несколькими программами одновременно?
К сожалению, справочная страница ISATTY(3)
не содержит никаких указаний на воздействие производительности метода.
UPDATE Я просто провел несколько тестов, и это кажется isatty()
делает один ioctl
системный вызов каждый раз, когда она называется, принимая около 700ns или циклов 500 часов на моей системе x86_64 ARCH. Сценарий write() (как вызвано printf
) занимает примерно такое же количество времени, поэтому, если isatty()
не буферизирован, я теряю меньше 1μs или около половины производительности на операцию вывода (что кажется небрежным по сравнению с временем, требуемым для прокрутка терминала, но может стать важной при перенаправлении вывода в большой текстовый файл). Особенно при постоянном вызове printf()
, write
syscalls вызывается только каждые 4096 байт, поэтому код может потратить большую часть времени на ожидание результатов isatty(), поэтому буферизация, похоже, имеет смысл в конце концов.
Так что я все равно хотел бы услышать ваше мнение о моей попытке буферизации и о проблемах, о которых я упомянул.
Вы отправили свою программу на поиски? –
Должен признаться, что нет, я не думал об этой возможности.И да, isatty() _does_ выполняет (ioctl) syscall каждый раз, когда он вызывается. –
Почему не работает указатель, как предлагает @ Квери/Мартин? – Morpfh