Por que o wc é tão lento?

17

Por que o utilitário wc é tão lento?

Quando o executo em um arquivo grande, leva cerca de 20 vezes mais que o 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

Não é apenas uma condição de borda estranha causada pelo fato de o arquivo estar cheio de nulos, vejo a mesma diferença de desempenho, mesmo que o arquivo seja preenchido com dados aleatórios ou seja um arquivo de texto.

(isso é no Ubuntu 13.04, 64 bits)

Johnny
fonte
Nota para aqueles que se preocupam apenas com a contagem de linhas: wc -l <filename> é muito mais rápido em arquivos muito grandes.
EL

Respostas:

27

Então eu fui para a fonte e parece que a lentidão está no tratamento de caracteres de byte duplo. Essencialmente, para cada caractere lido, ele precisa chamar mbrtowc()para tentar convertê-lo em um caractere amplo, e esse caractere amplo é testado para verificar se é um separador de palavras, separador de linhas, etc.

De fato, se eu alterar minha LANGvariável de localidade do padrão en_US.UTF-8(UTF-8 é um conjunto de caracteres multibyte) e defini-la como " C" (conjunto de caracteres simples de byte simples), wcserá possível usar otimizações de byte único, o que acelera consideravelmente, levando apenas cerca de um quarto do tempo antes.

Além disso, ele só precisa verificar cada caractere se estiver executando palavras ( -w), comprimento de linha ( -L) ou caracteres ( -m). Se estiver executando apenas contagens de bytes e / ou linhas, pode ignorar o amplo manuseio de caracteres e executar extremamente rápido - mais rapidamente que md5sum.

Corri-lo através de gprof, e as funções que são utilizados para tratar os caracteres de vários bytes ( mymbsinit(), mymbrtowc(), myiswprint(), etc) estão ocupando cerca de 30% do tempo de execução sozinho, e o código que os passos através do tampão é muito mais complexo porque tem que lida com etapas de tamanho variável através do buffer para caracteres de tamanho variável, além de preencher caracteres parcialmente concluídos que abrangem o buffer até o início do buffer, para que ele possa ser tratado na próxima vez.

Agora que sei o que procurar, encontrei alguns posts mencionando a lentidão do utf-8 em alguns utilitários:

/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
fonte
2
Oh, acabei de perceber que você é OP. : p
Ivan Chau
2
Embora essa seja a resposta mais votada, é irrelevante. md5sumnunca permitirá que você conte o número da palavra e wcnão computará o hash md5 do arquivo! É como perguntar por que meu carro é tão lento em comparação com a minha máquina de escrever ao escrever um texto.
user49468
5
@ user49468: É razoável supor que ambos sejam vinculados à IO, pois ambos precisam ler cada byte do arquivo de entrada. Essa resposta prova que wc, de fato, está vinculada à CPU ao processar caracteres de vários bytes.
MSalters
2
@ user49468: wc e md5sum podem fazer coisas diferentes, mas ambos lêem um arquivo e fazem um cálculo relativamente simples, calcula-se uma soma de verificação, conta-se bytes, separadores de palavras e novas linhas. Bem, eu pensei que era simples, mas não havia levado em consideração a complexidade extra dos conjuntos de caracteres multibyte. É mais como perguntar "Por que meu carro é 20 vezes mais rápido ao ir à loja do que minha minivan?" Você esperaria alguma diferença entre os dois, mas não uma diferença de 20X.
Johnny
1
@Johnny sua comparação carro / minivan não possui o aspecto de que ambos foram projetados para transportá-lo para a loja. Portanto, existe uma comparação de velocidade. Comparar o seu carro com o veículo de pintura com faixas é mais adequado. Só porque os dois usam as ruas, sua velocidade não é relevante, pois o pintor de faixas não é adequado para fazer compras e vice-versa.
user49468
1

Apenas um palpite, mas você está comparando maçãs com laranjas em relação ao que wcestá fazendo versus o que md5sumestá fazendo.

Tarefa de md5sum

Quando md5sumprocessa um arquivo, ele simplesmente abre o arquivo como um fluxo e começa a executá-lo através da função de soma de verificação MD5, que precisa de muito pouca memória. Essencialmente, CPU e disco ligado a E / S.

tarefa de wc

Quando wcexecutado, ele está fazendo muito mais do que apenas analisar o arquivo, um caractere de cada vez. Ele precisa realmente analisar a estrutura do arquivo, linhas de cada vez, fazendo determinações sobre onde estão os limites entre os caracteres e se é um limite de palavra ou não.

Exemplo

Pense nas seguintes seqüências de caracteres e como cada um dos algoritmos teria que passar por elas enquanto as analisava:

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

Para o MD5, ele move trivialmente essas strings, um caractere por vez. Pois wcele tem que decidir o que é um limite de palavras e linhas e acompanhar o número de ocorrências que vê.

Discussões adicionais do wc

Eu encontrei esse desafio de codificação de 2006 que discute a implementação wcno .NET. As dificuldades são bastante óbvias quando você olha para alguns dos pseudo-códigos, portanto, isso pode ajudar a começar a esclarecer por que wcparece ser muito mais lento que outras operações.

slm
fonte
1
Você está descrevendo algo diferente do comando wc padrão do Unix (pelo menos, não o que acompanha o Ubuntu). Que o wc não conta palavras únicas , apenas palavras, então "olá, olá, mundo" é de 3 palavras, não de 2.
Johnny
Com base nessa teoria, parece que uma tarefa mais simples, como contar linhas, iria mais rapidamente. Alterar 'wc' para especificar uma contagem de linhas modifica substancialmente os resultados? 'wc -l'
Joshua Miller
@ Johnny - eu nunca disse que conta palavras únicas que você disse isso. wcconta várias coisas enquanto analisa o arquivo. Conta o número de palavras, linhas e bytes à medida que analisa o arquivo. Leia a página de manual!
slm
@ JoshuaMiller - Não está claro se dizer wcapenas para contar linhas limita a análise interna, para que apenas conte essas coisas ou apenas relate os resultados das linhas, mesmo que ainda conte tudo.
slm
@slm Você disse que conta palavras únicas, seu exemplo diz “Olá! Greg ”resulta em Olá 1, Greg 1 , ou seja, conta para cada palavra. E o projeto .Net ao qual você se vinculou diz: "Uma de suas principais tarefas é passar por um conjunto de dados e contar o número de repetições de uma determinada palavra. Por exemplo, com a frase" Olá, sim, oi ", isso diria que a palavra Olá foi usada duas vezes e que a palavra sim foi usada uma vez. " Enquanto na realidade o resultado do eco "Olá, sim, olá" | wc --words , é "3", não "Olá: 2, Sim: 1"
Johnny