Будет ли printf по-прежнему иметь стоимость, даже если я перенаправлю вывод в / dev / null? - PullRequest
0 голосов
/ 15 января 2019

У нас есть демон, который содержит много сообщений для печати. Поскольку мы работаем над встроенным устройством со слабым процессором и другим ограничивающим оборудованием, мы хотим минимизировать любые затраты (ввод-вывод, процессор и т. Д.) Сообщений printf в нашей окончательной версии. (У пользователей нет консоли)

Мой товарищ по команде и я не согласны. Он думает, что мы можем просто перенаправить все в / dev / null. Это не будет стоить IO, поэтому привязанность будет минимальной. Но я думаю, что это все равно будет стоить CPU, и мы лучше определим макрос для printf, чтобы мы могли переписать «printf» (возможно, просто вернуть).

Так что мне нужно несколько мнений о том, кто прав. Будет ли Linux достаточно умным, чтобы оптимизировать printf? Я действительно в этом сомневаюсь.

Ответы [ 5 ]

0 голосов
/ 15 января 2019

Напишите свой собственный, который оборачивает printf (), используя источник printf () в качестве ориентира, и возвращая немедленно, если установлен флаг noprint. Недостатком этого является то, что при фактической печати он потребляет больше ресурсов из-за необходимости дважды анализировать строку формата. Но он использует незначительные ресурсы, когда не печатает. Невозможно просто заменить printf (), потому что базовые вызовы внутри printf () могут измениться на более новую версию библиотеки stdio.

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

0 голосов
/ 15 января 2019

Функция printf будет записывать в stdout. Не соответствует оптимизации для /dev/null. Следовательно, у вас будут дополнительные расходы на анализ строки формата и оценку любых необходимых аргументов, и у вас будет хотя бы один системный вызов, плюс вы скопируете буфер в адресное пространство ядра (что, по сравнению со стоимостью системного вызова, пренебрежимо мало). .

Этот ответ основан на специальной документации POSIX.

Системные интерфейсы
dprintf, fprintf, printf, snprintf, sprintf - вывод на печать в формате

Функция fprintf () помещает вывод в именованный поток вывода. Функция printf () помещает вывод в стандартный поток вывода stdout. Функция sprintf () помещает вывод, за которым следует нулевой байт '\ 0', в последовательные байты, начинающиеся с * s; ответственность за то, чтобы было достаточно места, лежит на пользователе.

Базовые определения
должен
Для реализации, которая соответствует POSIX.1-2017, описывает обязательную функцию или поведение. Приложение может полагаться на существование функции или поведения.

0 голосов
/ 15 января 2019

В значительной степени.

Когда вы перенаправляете стандартный вывод программы на /dev/null, любой вызов printf(3) будет по-прежнему оценивать все аргументы, и процесс форматирования строки будет происходить до вызова write(2), который записывает полностью отформатированную строку на стандартный вывод процесса. На уровне ядра данные не записываются на диск, а отбрасываются обработчиком, связанным со специальным устройством /dev/null.

Так что, в лучшем случае, вы не будете обходить или уклоняться от накладных расходов, связанных с оценкой аргументов и передачей их printf, заданием форматирования строки, стоящим за printf, и хотя бы одним системным вызовом для фактической записи данных. просто перенаправив стандартный вывод на /dev/null. Ну, это настоящая разница в Linux. Реализация просто возвращает количество байтов, которое вы хотели записать (указано 3-м аргументом вашего вызова для write(2)), и игнорирует все остальное (см. этот ответ ). В зависимости от объема записываемых данных и скорости целевого устройства (диска или терминала) разница в производительности может сильно различаться. Во встроенных системах, вообще говоря, отключение записи на диск путем перенаправления на /dev/null может сэкономить довольно много системных ресурсов для нетривиального объема записанных данных.

Хотя теоретически программа может обнаруживать /dev/null и выполнять некоторые оптимизации в рамках ограничений стандартов, которым они соответствуют (ISO C и POSIX), основываясь на общем понимании общих реализаций, они практически этого не делают (то есть я не подозревая об этом в любой системе Unix или Linux).

Стандарт POSIX предписывает запись в стандартный вывод для любого вызова на printf(3), поэтому не соответствует стандарту подавление вызова на write(2) в зависимости от связанных файловых дескрипторов. Для более подробной информации о требованиях POSIX вы можете прочитать Damon's answer . Да, и небольшое замечание: все дистрибутивы Linux практически POSIX-совместимы, несмотря на то, что они не сертифицированы , чтобы быть таковыми.

Имейте в виду, что если вы полностью замените printf, некоторые побочные эффекты могут пойти не так, например printf("%d%n", a++, &b). Если вам действительно нужно подавить вывод в зависимости от среды выполнения программы, рассмотрите возможность установки глобального флага и оберните printf, чтобы проверить флаг перед печатью - это не приведет к замедлению программы до такой степени, чтобы было видно снижение производительности , так как одиночная проверка условия на намного быстрее, чем вызов printf и выполнение всего форматирования строки.

0 голосов
/ 15 января 2019

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

На практике мне неизвестно о какой-либо реализации в Linux, которая в настоящее время (в начале 2019 г.) выполняет такую ​​оптимизацию - компиляторы и библиотеки, с которыми я знаком, будут форматировать вывод и записывать результат на нулевое устройство, полагаясь на в ядре ", чтобы игнорировать его.

Возможно, вы захотите написать собственную функцию пересылки, если вам действительно нужно сэкономить на стоимости форматирования, когда вывод не используется - вам нужно, чтобы он возвратил void, и вам следует проверить строку формата для %n. (Вы можете использовать snprintf с буфером NULL и 0, если вам нужны эти побочные эффекты, но экономия вряд ли окупит вложенные усилия).

0 голосов
/ 15 января 2019

Функция printf записывает в stdout. Если дескриптор файла, подключенный к stdout, перенаправляется на /dev/null, то нигде не будет записано никакого вывода (но оно все равно будет записано), но сам вызов printf и его форматирование все равно произойдут.

...