
¿Por qué el servicio de WC es tan lento?
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 condición de borde extraña causada por que el archivo esté lleno de valores nulos, veo la misma diferencia en el rendimiento incluso si el archivo está lleno de datos aleatorios o es un archivo de texto.
(esto es en Ubuntu 13.04, 64 bits)
Respuesta1
Así que fui a la fuente y parece que la lentitud se debe al manejo de caracteres de doble byte. Esencialmente, para cada carácter leído, es necesario llamar mbrtowc()
para intentar convertirlo en un carácter ancho, luego ese carácter ancho se prueba para ver si es un separador de palabras, un separador de líneas, etc.
De hecho, si cambio mi LANG
variable local predeterminada en_US.UTF-8
(UTF-8 es un conjunto de caracteres multibyte) y la configuro en " C
" (conjunto de caracteres simple de un solo byte), wc
puedo usar optimizaciones de un solo byte, lo que lo acelera considerablemente. tomando sólo alrededor de una cuarta parte del tiempo que antes.
Además, solo tiene que verificar cada carácter si está contando palabras ( -w
), longitud de línea ( -L
) o caracteres ( -m
). Si solo realiza 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 ejecuté gprof
y las funciones que se utilizan para manejar los caracteres multibyte ( mymbsinit()
,,, etc.) ocupan alrededor del 30% del tiempo de ejecución por sí solas, y el código que recorre el búfer es mucho más complejo porque tiene mymbrtowc()
que myiswprint()
manejar pasos de tamaño variable a través del búfer para caracteres de tamaño variable, así como rellenar cualquier carácter parcialmente completado que abarque el búfer hasta el principio del búfer para que pueda ser manejado la próxima vez.
Ahora que sé qué buscar, encontré algunas publicaciones que mencionan la lentitud de utf-8 con algunas utilidades:
https://stackoverflow.com/questions/13913014/grepping-a-huge-file-80gb-any-way-to-speed-it-up http://dtrace.org/blogs/brendan/2011/12/08/2000x-performance-win/
Respuesta2
Sólo una suposición, pero estás comparando manzanas con naranjas con respecto a lo que wc
se está haciendo frente a lo que md5sum
se está haciendo.
tarea de md5sum
Cuando md5sum
procesa un archivo, simplemente abre el archivo como una secuencia y luego comienza a ejecutar la secuencia a través delFunción de suma de comprobación MD5que necesita muy poca memoria. Básicamente, está vinculado a E/S de disco y CPU.
tarea de wc
Cuando wc
se ejecuta, hace mucho más que simplemente analizar el archivo carácter por carácter. En realidad, tiene que analizar la estructura del archivo, líneas a la vez, determinando dónde están los límites entre caracteres y si se trata de 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 mientras 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, se mueve trivialmente a través de estas cadenas un carácter a la vez. Porque wc
tiene que decidir qué es un límite de palabra y línea y realizar un seguimiento del número de apariciones que ve.
Discusiones adicionales sobre baños
encontré estodesafío de codificación de 2006que analiza la implementación wc
en .NET. Las dificultades son bastante obvias al observar parte del pseudocódigo, por lo que esto podría ayudar a comenzar a arrojar luz sobre por qué wc
parece ser mucho más lento que otras operaciones.