¿Printf tendrá un costo incluso si redirecciono la salida a/dev/null?
linux performance (5)
Bastante mucho
Cuando redirige la salida estándar del programa a
/dev/null
, cualquier llamada a
printf(3)
seguirá evaluando todos los argumentos, y el proceso de formateo de cadenas seguirá teniendo lugar antes de llamar a
write(2)
, que escribe la cadena con formato completo A la salida estándar del proceso.
Es en el nivel del kernel que los datos no se escriben en el disco, sino que se descartan por el controlador asociado con el dispositivo especial
/dev/null
.
Entonces, en el mejor de los casos, no pasará por alto ni evadirá la sobrecarga de evaluar los argumentos y pasarlos a
printf
, el trabajo de formato de cadena detrás de
printf
, y al menos una llamada al sistema para escribir los datos, simplemente redireccionando stdout a
/dev/null
Bueno, esa es una verdadera diferencia en Linux.
La implementación solo devuelve el número de bytes que desea escribir (especificado por el tercer argumento de su llamada a
write(2)
) e ignora todo lo demás (vea
esta respuesta
).
Dependiendo de la cantidad de datos que esté escribiendo y la velocidad del dispositivo de destino (disco o terminal), la diferencia en el rendimiento puede variar mucho.
En los sistemas integrados, en general, cortar la escritura del disco redirigiendo a
/dev/null
puede ahorrar algunos recursos del sistema para una cantidad no trivial de datos escritos.
Aunque en teoría, el programa podría detectar
/dev/null
y realizar algunas optimizaciones dentro de las restricciones de los estándares que cumplen (ISO C y POSIX), según el entendimiento general de implementaciones comunes, prácticamente no lo hacen (es decir, no tengo conocimiento de Cualquier sistema Unix o Linux que lo haga).
El estándar POSIX obliga a escribir en la salida estándar para cualquier llamada a
printf(3)
, por lo que no cumple con los estándares para suprimir la llamada a
write(2)
según los descriptores de archivo asociados.
Para obtener más detalles sobre los requisitos de POSIX, puede leer
la respuesta de Damon
.
Ah, y una nota rápida: todas las distribuciones de Linux son prácticamente compatibles con POSIX, a pesar de no estar
certificadas
para ser así.
Tenga en cuenta que si reemplaza
printf
completamente, algunos efectos secundarios pueden salir mal, por ejemplo,
printf("%d%n", a++, &b)
.
Si realmente necesita suprimir la salida en función del entorno de ejecución del programa, considere la posibilidad de configurar una bandera global y envolver printf para verificar la bandera antes de imprimir, no va a ralentizar el programa hasta el punto en que la pérdida de rendimiento sea visible. , como una sola verificación de condición es
mucho
más rápida que llamar a
printf
y hacer todo el formato de cadena.
Tenemos un demonio que contiene muchos mensajes impresos. Ya que estamos trabajando en un dispositivo integrado con una CPU débil y otro hardware de restricción, queremos minimizar cualquier tipo de costo (IO, CPU, etc.) de los mensajes de printf en nuestra versión final. (Los usuarios no tienen consola)
Mi compañero de equipo y yo tenemos un desacuerdo. Piensa que podemos redirigir todo a / dev / null. No costará ningún IO por lo que los afectos serán mínimos. Pero creo que aún costará la CPU y es mejor que definamos una macro para printf para que podamos reescribir "printf" (tal vez solo regrese).
Así que necesito algunas opiniones sobre quién tiene razón. ¿Será Linux lo suficientemente inteligente como para optimizar printf? Realmente lo dudo.
En términos generales, se permite una implementación para realizar tales optimizaciones si no afectan las salidas observables (funcionales) del programa.
En el caso de
printf()
, eso significaría que si el programa no usa el valor de retorno, y si no hay
%n
conversiones, entonces la implementación no podría hacer nada.
En la práctica, no conozco ninguna implementación en Linux que actualmente (a principios de 2019) realice tal optimización: los compiladores y bibliotecas con los que estoy familiarizado formatearán la salida y escribirán el resultado en el dispositivo nulo, confiando en el kernel ''para ignorarlo.
Es posible que desee escribir su propia función de reenvío si realmente necesita ahorrar el costo de formatear cuando no se usa la salida; querrá que se devuelva
void
y debe verificar la cadena de formato para
%n
.
(Podría usar
snprintf
con un búfer
NULL
y
0
si necesita esos efectos secundarios, pero es poco probable que los ahorros reembolsen el esfuerzo invertido).
Escriba el suyo que contenga printf () utilizando la fuente printf () como guía, y devuélvalo inmediatamente si se establece una marca noprint. La desventaja de esto es que cuando se imprime realmente consumirá más recursos debido a que tiene que analizar la cadena de formato dos veces. Pero utiliza recursos insignificantes cuando no imprime. No puede simplemente reemplazar printf () porque las llamadas subyacentes dentro de printf () pueden cambiar con una versión más nueva de la biblioteca stdio.
void printf2 (const char * formatstring, ...);
La función
printf
escribe en
stdout
.
Si el descriptor de archivo conectado a
stdout
se redirige a
/dev/null
entonces no se escribirá ningún resultado en ninguna parte (pero se seguirá escribiendo), pero la llamada a
printf
y el formateo seguirán ocurriendo.
La función
printf
escribirá en
stdout
.
No está conforme para optimizar para
/dev/null
.
Por lo tanto, tendrá la sobrecarga de analizar la cadena de formato y evaluar los argumentos necesarios, y tendrá al menos un syscall, además de que copiará un búfer en el espacio de direcciones del kernel (que, en comparación con el costo del syscall, es insignificante) .
Esta respuesta se basa en la documentación específica de POSIX.
Interfaces del sistema
dprintf, fprintf, printf, snprintf, sprintf - imprimir salida formateadaLa función fprintf () colocará la salida en el flujo de salida mencionado. La función printf () colocará la salida en la salida estándar del flujo de salida. La función sprintf () colocará la salida seguida del byte nulo, ''/ 0'', en bytes consecutivos comenzando en * s; Es responsabilidad del usuario asegurarse de que haya suficiente espacio disponible.
Definiciones de Base
deberá
Para una implementación que cumple con POSIX.1-2017, describe una característica o comportamiento que es obligatorio. Una aplicación puede confiar en la existencia de la característica o el comportamiento.