2012-04-22 4 views
2

В настоящее время я пишу программу 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(), поэтому буферизация, похоже, имеет смысл в конце концов.

Так что я все равно хотел бы услышать ваше мнение о моей попытке буферизации и о проблемах, о которых я упомянул.

+2

Вы отправили свою программу на поиски? –

+0

Должен признаться, что нет, я не думал об этой возможности.И да, isatty() _does_ выполняет (ioctl) syscall каждый раз, когда он вызывается. –

+0

Почему не работает указатель, как предлагает @ Квери/Мартин? – Morpfh

ответ

5

Быстрый тест показал, что по крайней мере на Дарвине, isatty не кэшируется, и каждый раз он делает ioctl. 10 000 проверок файловых дескрипторов 0 - 99 занимали всего 0,4 секунды на 2,8 ГГц i7 (mac). Я бы сказал, что вызов printf стоит гораздо больше, чем вызов isatty.

В любом случае, я бы использовал указатель на функцию. В начале я бы назвал один isatty и нарисовал указатель на функцию (printf без ascii/printf с ascii), а затем используйте этот указатель.

Martin

+0

'isatty' не может быть кэширован, потому что у него нет способа узнать, был ли заменен целевой файловый дескриптор другим вызовом. –

+0

Ну, теперь я знаю, что нет кеша. Но и это было бы невозможно. Представьте себе крюк, который будет скрывать кеш-файл isatty одного файлового дескриптора на close() – Kveri

+0

Это также необходимо сделать на 'dup2' и, возможно, на других ... И для этого потребуется синхронизация, но она не может использовать блокировку, потому что' close' является безопасным асинхронным сигналом, поэтому ему нужно будет как-то атомизировать ... И ... [добавить много других проблем, возникающих, когда вы пытаетесь сделать дополнительное дерьмо, как это, в функции, которая должна быть чисто системным) , –

Смежные вопросы