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)


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 /


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.