En un bash
script necesito varios valores de /proc/
archivos. Hasta ahora tengo docenas de líneas agrupando los archivos directamente así:
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
En un esfuerzo por hacerlo más eficiente, guardé el contenido del archivo en una variable y seleccioné eso:
a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'
En lugar de abrir el archivo varias veces, esto debería abrirlo una sola vez y seleccionar el contenido variable, que supuse sería más rápido, pero de hecho es más lento:
bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.803s
user 0m0.619s
sys 0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.182s
user 0m1.425s
sys 0m0.506s
Lo mismo es cierto para dash
y zsh
. Sospeché el estado especial de los /proc/
archivos como una razón, pero cuando copio el contenido de /proc/meminfo
un archivo normal y uso que los resultados son los mismos:
bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real 0m0.790s
user 0m0.608s
sys 0m0.227s
El uso de una cadena here para guardar la tubería lo hace un poco más rápido, pero aún no tan rápido como con los archivos:
bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real 0m0.977s
user 0m0.758s
sys 0m0.268s
¿Por qué abrir un archivo más rápido que leer el mismo contenido de una variable?
/proc/
archivos como una razón, pero cuando copio el contenido de/proc/meminfo
un archivo normal y el uso que los resultados son los mismos:” Es no especial/proc/
archivos, ¡leer archivos normales también es más rápido!Respuestas:
Aquí, no se trata de abrir un archivo en lugar de leer el contenido de una variable, sino más bien de bifurcar un proceso adicional o no.
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
bifurca un proceso que se ejecuta ygrep
que se abre/proc/meminfo
(un archivo virtual, en la memoria, sin E / S de disco involucrado) lo lee y coincide con la expresión regular.La parte más costosa es bifurcar el proceso y cargar la utilidad grep y sus dependencias de la biblioteca, hacer la vinculación dinámica, abrir la base de datos de configuración regional, docenas de archivos que están en el disco (pero probablemente en caché en la memoria).
La parte sobre leer
/proc/meminfo
es insignificante en comparación, el núcleo necesita poco tiempo para generar la información allí ygrep
necesita poco tiempo para leerlo.Si se ejecuta
strace -c
en eso, verá que las llamadas a los sistemas unoopen()
y unoread()
utilizados para leer/proc/meminfo
son cacahuetes en comparación con todo lo demás.grep
hace para comenzar (strace -c
no cuenta la bifurcación).En:
En la mayoría de los depósitos que soportan eso
$(<...)
operador ksh, el shell simplemente abre el archivo y lee su contenido (y elimina los caracteres de la nueva línea final).bash
es diferente y mucho menos eficiente, ya que bifurca un proceso para hacer esa lectura y pasa los datos al padre a través de una tubería. Pero aquí, se hace una vez, así que no importa.En:
El caparazón necesita generar dos procesos, que se ejecutan simultáneamente pero interactúan entre sí a través de una tubería. Esa creación de tubería, derribar y escribir y leer tiene un costo pequeño. El costo mucho mayor es el engendro de un proceso adicional. La programación de los procesos también tiene algún impacto.
Puede encontrar que usar el
<<<
operador zsh lo hace un poco más rápido:En zsh y bash, eso se hace escribiendo el contenido de
$a
un archivo temporal, que es menos costoso que generar un proceso adicional, pero probablemente no le dará ninguna ganancia en comparación con obtener los datos directamente/proc/meminfo
. Eso sigue siendo menos eficiente que su enfoque que copia/proc/meminfo
en el disco, ya que la escritura del archivo temporal se realiza en cada iteración.dash
no admite cadenas aquí, pero sus documentos heredados se implementan con una tubería que no implica generar un proceso adicional. En:El caparazón crea una tubería, bifurca un proceso. El niño se ejecuta
grep
con su stdin como el extremo de lectura de la tubería, y el padre escribe el contenido en el otro extremo de la tubería.Pero es probable que el manejo de la tubería y la sincronización del proceso sigan siendo más costosos que simplemente obtener los datos directamente
/proc/meminfo
.El contenido de
/proc/meminfo
es corto y no lleva mucho tiempo producirlo. Si desea guardar algunos ciclos de CPU, desea eliminar las partes costosas: bifurcar procesos y ejecutar comandos externos.Me gusta:
Sin
bash
embargo, evite cuyo patrón de coincidencia es muy ineficiente. Conzsh -o extendedglob
, puedes acortarlo a:Tenga en cuenta que
^
es especial en muchos shells (Bourne, fish, rc, es y zsh con la opción de globo extendido al menos), recomendaría citarlo. También tenga en cuenta queecho
no se puede utilizar para generar datos arbitrarios (de ahí mi uso de loprintf
anterior).fuente
printf
usted dice que el shell necesita generar dos procesos, pero ¿no esprintf
un shell incorporado?grep
y ejecuta.A | B
, hay algunos shells como AT&T ksh o zsh que se ejecutanB
en el proceso de shell actual si se trata de un comando incorporado o compuesto o de función, no conozco ninguno que se ejecuteA
en el proceso actual. En todo caso, para hacer eso, tendrían que manejar SIGPIPE de una manera compleja como si seA
estuviera ejecutando en un proceso secundario y sin terminar el shell para que el comportamiento no sea demasiado sorprendente cuandoB
sale temprano. Es mucho más fácil ejecutarB
el proceso padre.<<<
bash
no era compatible<<<
, solo que el operador vinozsh
como$(<...)
vino de ksh.En su primer caso, solo está utilizando la utilidad grep y está buscando algo del archivo
/proc/meminfo
,/proc
es un sistema de archivos virtual, por lo que el/proc/meminfo
archivo está en la memoria y requiere muy poco tiempo para recuperar su contenido.Pero en el segundo caso, está creando una tubería, luego pasa la salida del primer comando al segundo comando usando esta tubería, lo cual es costoso.
La diferencia se debe a
/proc
(porque está en la memoria) y a la tubería, consulte el siguiente ejemplo:fuente
Está llamando a un comando externo en ambos casos (grep). La llamada externa requiere una subshell. Bifurcar ese caparazón es la causa fundamental de la demora. Ambos casos son similares, por lo tanto: un retraso similar.
Si desea leer el archivo externo solo una vez y usarlo (desde una variable) varias veces, no salga del shell:
Lo que toma solo alrededor de 0.1 segundos en lugar del 1 segundo completo para la llamada grep.
fuente