Rendimiento de sed
vs. tail
para eliminar la primera línea de un archivo
TL; DR
sed
es muy potente y versátil, pero esto es lo que lo hace lento, especialmente para archivos grandes con muchas líneas.
tail
hace solo una cosa simple, pero esa lo hace bien y rápido, incluso para archivos más grandes con muchas líneas.
Para archivos pequeños y medianos, sed
y tail
tienen un rendimiento similar rápido (o lento, según sus expectativas). Sin embargo, para archivos de entrada más grandes (varios MB), la diferencia de rendimiento crece significativamente (un orden de magnitud para archivos en el rango de cientos de MB), con un tail
rendimiento claramente superior sed
.
Experimentar
Preparaciones generales:
Nuestros comandos para analizar son:
sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null
Tenga en cuenta que estoy canalizando la salida /dev/null
cada vez para eliminar la salida del terminal o las escrituras de archivos como cuello de botella de rendimiento.
Configuremos un disco RAM para eliminar la E / S del disco como posible cuello de botella. Personalmente tengo un tmpfs
montado en, /tmp
así que simplemente coloqué mi testfile
allí para este experimento.
Luego, una vez estoy creando un archivo de prueba aleatorio que contiene una cantidad específica de líneas $numoflines
con longitud de línea aleatoria y datos aleatorios usando este comando (tenga en cuenta que definitivamente no es óptimo, se vuelve realmente lento para aproximadamente> 2M líneas, pero a quién le importa, no es el lo que estamos analizando):
cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
Oh, por cierto. mi computadora portátil de prueba ejecuta Ubuntu 16.04, 64 bits en una CPU Intel i5-6200U. Solo para comparar.
Tiempo de archivos grandes:
Configurar un gran testfile
:
Ejecutar el comando anterior numoflines=10000000
produjo un archivo aleatorio que contiene 10 millones de líneas, ocupando un poco más de 600 MB; es bastante grande, pero comencemos con él, porque podemos:
$ wc -l testfile
10000000 testfile
$ du -h testfile
611M testfile
$ head -n 3 testfile
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO
Realice la ejecución cronometrada con nuestro enorme testfile
:
Ahora hagamos una sola ejecución temporizada con ambos comandos primero para estimar con qué magnitudes estamos trabajando.
$ time sed '1d' testfile > /dev/null
real 0m2.104s
user 0m1.944s
sys 0m0.156s
$ time tail -n +2 testfile > /dev/null
real 0m0.181s
user 0m0.044s
sys 0m0.132s
Ya vemos un resultado realmente claro para archivos grandes, tail
es una magnitud más rápido que sed
. Pero solo por diversión y para estar seguros de que no hay efectos secundarios aleatorios que hagan una gran diferencia, hagámoslo 100 veces:
$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real 3m36.756s
user 3m19.756s
sys 0m15.792s
$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real 0m14.573s
user 0m1.876s
sys 0m12.420s
La conclusión sigue siendo la misma, sed
es ineficiente para eliminar la primera línea de un archivo grande, tail
debe usarse allí.
Y sí, sé que las construcciones de bucle de Bash son lentas, pero solo estamos haciendo relativamente pocas iteraciones aquí y el tiempo que toma un bucle simple no es significativo en comparación con los tiempos de ejecución sed
/ de tail
todos modos.
Sincronización de archivos pequeños:
Configurar un pequeño testfile
:
Ahora para completar, veamos el caso más común de que tiene un pequeño archivo de entrada en el rango de kB. Creemos un archivo de entrada aleatorio con numoflines=100
este aspecto:
$ wc -l testfile
100 testfile
$ du -h testfile
8,0K testfile
$ head -n 3 testfile
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly
Realice la ejecución cronometrada con nuestro pequeño testfile
:
Como podemos esperar que los tiempos para archivos tan pequeños estén en el rango de unos pocos milisegundos de la experiencia, hagamos 1000 iteraciones de inmediato:
$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real 0m7.811s
user 0m0.412s
sys 0m7.020s
$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real 0m7.485s
user 0m0.292s
sys 0m6.020s
Como puede ver, los tiempos son bastante similares, no hay mucho para interpretar o preguntarse. Para archivos pequeños, ambas herramientas son igualmente adecuadas.
sed
es más portátil: "+2"tail
funciona bien en Ubuntu, que usa GNUtail
, pero no funcionará en BSDtail
.