Tengo una situación única en la que puedo comparar las soluciones propuestas en esta página, por lo que escribo esta respuesta como una consolidación de las soluciones propuestas con tiempos de ejecución incluidos para cada una.
Preparar
Tengo un archivo de datos de texto ASCII de 3.261 gigabytes con un par clave-valor por fila. El archivo contiene 3,339,550,320 filas en total y desafía la apertura en cualquier editor que haya probado, incluido mi Vim de acceso. Necesito subconjuntar este archivo para investigar algunos de los valores que descubrí que solo comienzan alrededor de la fila ~ 500,000,000.
Debido a que el archivo tiene tantas filas:
- Necesito extraer solo un subconjunto de las filas para hacer algo útil con los datos.
- Leer cada fila que conduzca a los valores que me interesan llevará mucho tiempo.
- Si la solución lee más allá de las filas que me interesan y continúa leyendo el resto del archivo, perderá tiempo leyendo casi 3 mil millones de filas irrelevantes y tomará 6 veces más de lo necesario.
Mi mejor escenario es una solución que extrae solo una sola línea del archivo sin leer ninguna de las otras filas del archivo, pero no puedo pensar en cómo podría lograr esto en Bash.
A los fines de mi cordura, no voy a tratar de leer las 500,000,000 líneas completas que necesitaría para mi propio problema. En cambio, intentaré extraer la fila 50,000,000 de 3,339,550,320 (lo que significa que leer el archivo completo tomará 60 veces más de lo necesario).
Usaré el time
incorporado para comparar cada comando.
Base
Primero veamos cómo la head
tail
solución:
$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0
real 1m15.321s
La línea de base para la fila 50 millones es 00: 01: 15.321, si hubiera ido directamente a la fila 500 millones probablemente sería ~ 12.5 minutos.
cortar
Dudo de esto, pero vale la pena intentarlo:
$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0
real 5m12.156s
Este tomó 00: 05: 12.156 para ejecutarse, ¡lo cual es mucho más lento que la línea de base! No estoy seguro de si leyó todo el archivo o solo hasta 50 millones de líneas antes de detenerse, pero independientemente de esto, no parece una solución viable para el problema.
AWK
Solo ejecuté la solución con el exit
porque no iba a esperar a que se ejecute el archivo completo:
$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0
real 1m16.583s
Este código se ejecutó en 00: 01: 16.583, que es solo ~ 1 segundo más lento, pero aún no es una mejora en la línea de base. ¡A este ritmo, si se hubiera excluido el comando de salida, probablemente habría tomado alrededor de ~ 76 minutos leer el archivo completo!
Perl
También ejecuté la solución Perl existente:
$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0
real 1m13.146s
Este código se ejecutó en 00: 01: 13.146, que es ~ 2 segundos más rápido que la línea de base. Si lo ejecutara en los 500,000,000 completos, probablemente tomaría ~ 12 minutos.
sed
La respuesta principal en el tablero, aquí está mi resultado:
$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0
real 1m12.705s
Este código se ejecutó en 00: 01: 12.705, que es 3 segundos más rápido que la línea de base y ~ 0.4 segundos más rápido que Perl. Si lo hubiera ejecutado en las 500,000,000 filas completas, probablemente hubiera tomado ~ 12 minutos.
archivo de mapa
Tengo bash 3.1 y, por lo tanto, no puedo probar la solución mapfile.
Conclusión
Parece que, en su mayor parte, es difícil mejorar la head
tail
solución. En el mejor de los casos, la sed
solución proporciona un aumento de ~ 3% en la eficiencia.
(porcentajes calculados con la fórmula % = (runtime/baseline - 1) * 100
)
Fila 50,000,000
- 00: 01: 12.705 (-00: 00: 02.616 = -3.47%)
sed
- 00: 01: 13.146 (-00: 00: 02.175 = -2.89%)
perl
- 00: 01: 15.321 (+00: 00: 00.000 = + 0.00%)
head|tail
- 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%)
awk
- 00: 05: 12.156 (+00: 03: 56.835 = + 314.43%)
cut
Fila 500,000,000
- 00: 12: 07.050 (-00: 00: 26.160)
sed
- 00: 12: 11.460 (-00: 00: 21.750)
perl
- 00: 12: 33.210 (+00: 00: 00.000)
head|tail
- 00: 12: 45.830 (+00: 00: 12.620)
awk
- 00: 52: 01.560 (+00: 40: 31.650)
cut
Fila 3,338,559,320
- 01: 20: 54.599 (-00: 03: 05.327)
sed
- 01: 21: 24.045 (-00: 02: 25.227)
perl
- 01: 23: 49.273 (+00: 00: 00.000)
head|tail
- 01: 25: 13.548 (+00: 02: 35.735)
awk
- 05: 47: 23.026 (+04: 24: 26.246)
cut
awk
y,sed
y estoy seguro, que a alguien también se le ocurre una línea de Perl o algo así;)