En un servidor multinúcleo, utilice GNU paralelo para contar las líneas de archivo en paralelo. Después de imprimir el recuento de líneas de cada archivo, bc suma todos los recuentos de líneas.
find . -name '*.txt' | parallel 'wc -l {}' 2>/dev/null | paste -sd+ - | bc
Para ahorrar espacio, incluso puede mantener todos los archivos comprimidos. La siguiente línea descomprime cada archivo y cuenta sus líneas en paralelo, luego suma todas las cuentas.
find . -name '*.xz' | parallel 'xzcat {} | wc -l' 2>/dev/null | paste -sd+ - | bc
Según mi prueba, puedo verificar que Spark-Shell (basado en Scala) es mucho más rápido que las otras herramientas (GREP, SED, AWK, PERL, WC). Aquí está el resultado de la prueba que ejecuté en un archivo que tenía 23782409 líneas
time grep -c $ my_file.txt;
real 0m44.96susuario 0m41.59ssys 0m3.09s
time wc -l my_file.txt;
real 0m37.57susuario 0m33.48ssys 0m3.97s
time sed -n '$=' my_file.txt;
real 0m38.22suser 0m28.05ssys 0m10.14s
time perl -ne 'END { $_=$.;if(!/^[0-9]+$/){$_=0;};print "$_" }' my_file.txt
;
real 0m23.38suser 0m20.19ssys 0m3.11s
time awk 'END { print NR }' my_file.txt;
real 0m19.90suser 0m16.76ssys 0m3.12s
spark-shell
import org.joda.time._
val t_start = DateTime.now()
sc.textFile("file://my_file.txt").count()
val t_end = DateTime.now()
new Period(t_start, t_end).toStandardSeconds()
res1:org.joda.time.Seconds =PT15S
Su factor de velocidad limitante es la velocidad de E/S de su dispositivo de almacenamiento, por lo que cambiar entre programas simples de conteo de líneas nuevas/patrones no ayudará, porque es probable que la diferencia de velocidad de ejecución entre esos programas se suprima por la forma en que el disco/almacenamiento/dispositivo es más lento. lo que tengas.
Pero si tiene el mismo archivo copiado en discos/dispositivos, o el archivo está distribuido entre esos discos, ciertamente puede realizar la operación en paralelo. No sé específicamente sobre este Hadoop, pero suponiendo que pueda leer un archivo de 10 gb desde 4 ubicaciones diferentes, puede ejecutar 4 procesos de conteo de líneas diferentes, cada uno en una parte del archivo, y resumir sus resultados:
$ dd bs=4k count=655360 if=/path/to/copy/on/disk/1/file | wc -l &
$ dd bs=4k skip=655360 count=655360 if=/path/to/copy/on/disk/2/file | wc -l &
$ dd bs=4k skip=1310720 count=655360 if=/path/to/copy/on/disk/3/file | wc -l &
$ dd bs=4k skip=1966080 if=/path/to/copy/on/disk/4/file | wc -l &
Observe el &
en cada línea de comando, por lo que todo se ejecutará en paralelo; dd
funciona como cat
aquí, pero nos permite especificar cuántos bytes leer (count * bs
bytes) y cuántos omitir al principio de la entrada (skip * bs
bytes). Funciona en bloques, de ahí la necesidad de especificar bs
como el tamaño del bloque. En este ejemplo, he particionado el archivo de 10 Gb en 4 partes iguales de 4 Kb * 655360 =2684354560 bytes =2,5 GB, uno dado a cada trabajo, es posible que desee configurar un script para que lo haga por usted en función del tamaño del archivo y el número de trabajos paralelos que ejecutará. También debe sumar el resultado de las ejecuciones, lo que no he hecho por mi falta de capacidad de script de shell.
Si su sistema de archivos es lo suficientemente inteligente como para dividir archivos grandes entre muchos dispositivos, como un RAID o un sistema de archivos distribuido o algo así, y paralelizar automáticamente las solicitudes de E/S que se pueden paralelizar, puede hacer esa división, ejecutando muchos trabajos paralelos, pero usando la misma ruta de archivo, y aún puede tener algo de ganancia de velocidad.
EDITAR:Otra idea que se me ocurrió es que, si las líneas dentro del archivo tienen el mismo tamaño, puede obtener el número exacto de líneas dividiendo el tamaño del archivo por el tamaño de la línea, ambos en bytes. Puedes hacerlo casi instantáneamente en un solo trabajo. Si tiene el tamaño medio y no le importa exactamente el número de líneas, pero desea una estimación, puede realizar esta misma operación y obtener un resultado satisfactorio mucho más rápido que la operación exacta.
Prueba:sed -n '$=' filename
También cat es innecesario:wc -l filename
es suficiente en tu forma actual.