¿Por qué es tan lento?

17

¿Por qué la utilidad wc es tan lenta?

Cuando lo ejecuto en un archivo grande, tarda aproximadamente 20 veces más que md5sum:

MyDesktop:/tmp$ dd if=/dev/zero bs=1024k count=1024 of=/tmp/bigfile
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.687094 s, 1.6 GB/s

MyDesktop:/tmp$ time wc /tmp/bigfile 
         0          0 1073741824 /tmp/bigfile

real    0m45.969s
user    0m45.424s
sys     0m0.424s

MyDesktop:/tmp$ time md5sum /tmp/bigfile 
cd573cfaace07e7949bc0c46028904ff  /tmp/bigfile

real    0m2.520s
user    0m2.196s
sys     0m0.316s

No es solo una extraña condición de borde causada por el archivo lleno de nulos, veo la misma diferencia en el rendimiento incluso si el archivo está lleno de datos aleatorios o es un archivo de texto.

(Esto está en Ubuntu 13.04, 64 bit)

Johnny
fuente
Nota para aquellos que solo se preocupan por el recuento de líneas: wc -l <nombre de archivo> es mucho más rápido en archivos muy grandes.
EL

Respuestas:

27

Así que fui a la fuente, y parece que la lentitud está en el manejo de caracteres de doble byte. Esencialmente, para cada carácter leído, debe llamar mbrtowc()para intentar convertirlo en un carácter ancho, luego se prueba ese carácter ancho para ver si es un separador de palabras, un separador de línea, etc.

De hecho, si cambio mi LANGvariable de configuración regional por defecto en_US.UTF-8(UTF-8 es un conjunto de caracteres multibyte) y lo configuro en " C" (conjunto de caracteres de un solo byte simple), wcpuedo usar optimizaciones de un solo byte, lo que lo acelera considerablemente, tomando solo alrededor de un cuarto del tiempo que antes.

Además, solo tiene que verificar cada carácter si está haciendo recuentos de palabras ( -w), longitud de línea ( -L) o caracteres ( -m). Si solo está haciendo recuentos de bytes y / o líneas, puede omitir el manejo de caracteres anchos y luego se ejecuta extremadamente rápido, más rápido que md5sum.

Lo pasé por gprof, y las funciones que se utilizan para manejar los caracteres de varios bytes ( mymbsinit(), mymbrtowc(), myiswprint(), etc.) están ocupando aproximadamente el 30% del tiempo de ejecución solo, y el código que los pasos a través de la memoria intermedia es mucho más compleja porque tiene que maneja pasos de tamaño variable a través del búfer para caracteres de tamaño variable, así como rellena los caracteres parcialmente completados que abarcan el búfer de regreso al comienzo del búfer para que pueda manejarse la próxima vez.

Ahora que sé qué buscar, encontré algunas publicaciones que mencionan la lentitud utf-8 con algunas utilidades:

/programming/13913014/grepping-a-huge-file-80gb-any-way-to-speed-it-up http://dtrace.org/blogs/brendan/2011/12/08 / 2000x-performance-win /

Johnny
fuente
2
Oh, acabo de darme cuenta de que eres OP. :pag
Ivan Chau
2
Aunque esta es la respuesta más votada, es irrelevante. md5sum¡nunca le permitirá contar el número de palabra y wcno calculará el hash md5 del archivo! Es como preguntar por qué mi auto es tan lento en comparación con mi máquina de escribir cuando escribo un texto.
user49468
55
@ user49468: es razonable suponer que ambos están vinculados a IO, ya que ambos tienen que leer cada byte del archivo de entrada. Esta respuesta demuestra que, wcde hecho, está vinculada a la CPU cuando se procesan caracteres de varios bytes.
MSalters
2
@ user49468: wc y md5sum pueden hacer cosas diferentes, pero ambos leen un archivo y hacen un cálculo relativamente simple, uno calcula una suma de verificación, uno cuenta bytes, separadores de palabras y líneas nuevas. Bueno, pensé que era simple, pero no había tenido en cuenta la complejidad adicional de los conjuntos de caracteres multibyte. Es más como preguntar "¿Por qué mi automóvil es 20 veces más rápido para ir a la tienda que mi minivan?" Esperarías alguna diferencia entre los dos, pero no una diferencia de 20X.
Johnny
1
La comparación de @Johnny you car / minivan carece del aspecto que ambos están diseñados para transportarlo a la tienda. Entonces, una comparación de velocidad está en su lugar. Comparar su automóvil con el vehículo de pintura de rayas es más adecuado. Solo porque ambos usan las calles, sus velocidades no son relevantes, ya que el pintor de rayas no es adecuado para ir de compras y viceversa.
user49468
1

Solo una suposición, pero estás comparando manzanas con naranjas con respecto a lo que wcestá haciendo frente a lo que md5sumestá haciendo.

tarea de md5sum

Cuando md5sumprocesa un archivo, simplemente abre el archivo como una secuencia y luego comienza a ejecutar la secuencia a través de la función de suma de comprobación MD5 que necesita muy poca memoria. Esencialmente CPU y disco de E / S vinculadas.

tarea de wc

Cuando se wcejecuta, está haciendo mucho más que simplemente analizar el archivo de un carácter a la vez. Realmente tiene que analizar la estructura del archivo, líneas a la vez haciendo determinaciones sobre dónde están los límites entre los caracteres y si es un límite de palabra o no.

Ejemplo

Piense en las siguientes cadenas y en cómo cada uno de los algoritmos tendría que moverse a través de ellas a medida que las analizan:

“Hello! Greg”
“Hello!Greg”
“Hello\nGreg”
“A.D.D.”
“Wow, how great!”
“wow     \n\n\n    great”
“it was a man-eating shark.”

Para MD5, trivialmente se mueve a través de estas cadenas de un carácter a la vez. porwc ello tiene que decidir qué es un límite de palabra y línea y realizar un seguimiento de la cantidad de ocurrencias que ve.

Discusiones adicionales de wc

Encontré este desafío de codificación de 2006 que analiza la implementación wcen .NET. Las dificultades son bastante obvias cuando observa algunos de los pseudocódigos, por lo que esto podría ayudar a comenzar a arrojar luz sobre por qué wcparece ser mucho más lento que otras operaciones.

slm
fuente
1
Estás describiendo algo diferente al comando estándar de Unix wc (al menos, no el que viene con Ubuntu). Ese wc no cuenta palabras únicas , solo palabras, así que "hola hola mundo" son 3 palabras, no 2.
Johnny
Según esta teoría, parece que una tarea más simple, como contar líneas, iría más rápido. ¿Cambiar 'wc' para especificar un recuento de líneas modifica sustancialmente los resultados? 'wc -l'
Joshua Miller
@Johnny: nunca dije que cuenta palabras únicas que dijiste. wccuenta varias cosas a medida que analiza el archivo. Cuenta la cantidad de palabras, líneas y bytes a medida que analiza el archivo. ¡Lea la página del manual!
slm
@JoshuaMiller: no está claro si decir wcque solo contar líneas limita el análisis interno para que solo cuente estas cosas o solo informe los resultados de las líneas, a pesar de que todavía cuenta todo.
slm
@slm Usted dijo que cuenta palabras únicas, su ejemplo dice "¡Hola! Greg "da como resultado Hello 1, Greg 1 , es decir, cuenta para cada palabra. Y el proyecto .Net al que se vinculó dice: "Una de sus tareas principales es revisar un conjunto de datos y contar el número de repeticiones de una palabra dada. Por ejemplo, dada la oración" Hola, sí, hola ", te diría que la palabra Hola se usó dos veces y la palabra sí se usó una vez ". Mientras que en realidad el resultado del eco "Hola, sí, hola" | wc --palabras , es "3", no "Hola: 2, Sí: 1"
Johnny