Estou tentando fazer uma contagem de registros em um arquivo gzip de 7,6 GB. Encontrei poucas abordagens usando o zcat
comando
$ zcat T.csv.gz | wc -l
423668947
Isso funciona, mas leva muito tempo (mais de 10 minutos para obter a contagem). Eu tentei mais algumas abordagens como
$ sed -n '$=' T.csv.gz
28173811
$ perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811
Todos esses três comandos estão executando muito rapidamente, mas fornecendo uma contagem incorreta de 28173811.
Como posso executar uma contagem de registros em um período mínimo de tempo?
Respostas:
Os comandos
sed
,perl
eawk
que você mencionou podem estar corretos, mas todos lêem os dados compactados e contam caracteres de nova linha. Esses caracteres de nova linha não têm nada a ver com os caracteres de nova linha nos dados descompactados.Para contar o número de linhas nos dados não compactados, não há como contorná-los. Sua abordagem com
zcat
é a abordagem correta e desde que os dados é tão grande, ele vai ter tempo para descomprimir.A maioria dos utilitários que lida com
gzip
compactação e descompactação provavelmente usará as mesmas rotinas de biblioteca compartilhada para fazer isso. A única maneira de acelerar isso seria encontrar uma implementação daszlib
rotinas que sejam de algum modo mais rápidas que as padrão e reconstruir, por exemplo,zcat
para usá-las.fonte
zcat
. Uma parte significativa do trabalho dezcat
está gerando a saída real. Mas se você está apenas contando\n
caracteres, isso não é necessário.gzip
a compactação funciona basicamente substituindo longas cadeias comuns por outras mais curtas. Portanto, você só precisa se preocupar com as longas sequências no dicionário que contêm\n
ae contar a ocorrência (ponderada) delas. Por exemplo, devido às regras em inglês,.\n
é uma string comum de 16 bits.Use unpigz.
A resposta de Kusalananda está correta, você vai precisar para descompactar o arquivo inteiro para digitalizar seu conteúdo.
/bin/gunzip
faz isso o mais rápido possível, em um único núcleo. Pigz é uma implementação paralelagzip
que pode usar múltiplos núcleos.Infelizmente, o próprio descompressão de arquivos gzip normais não pode ser paralelizado, mas
pigz
não oferecem uma versão melhoradagunzip
,unpigz
que faz trabalhos relacionados, tais como a leitura, escrita e checksumming em um segmento separado. Em alguns benchmarks rápidos,unpigz
é quase o dobro da velocidadegunzip
da minha máquina core i5.Instale
pigz
com seu gerenciador de pacotes favorito e use emunpigz
vez degunzip
ou emunpigz -c
vez dezcat
. Portanto, seu comando se torna:Tudo isso pressupõe que o gargalo é a CPU, não o disco, é claro.
fonte
pigz
página de manual afirma que a descompressão não pode ser paralelizada, pelo menos não sem fluxos de esvaziamento especialmente preparados para esse fim. Como resultado, o pigz usa um único thread (o thread principal) para descompressão, mas criará outros três threads para leitura, gravação e verificação de cálculos, o que pode acelerar a descompressão em algumas circunstâncias . Ainda assim, como se eu acho que é pelo menos duas vezes mais rápido do quegzip
, se não por causa do paralelismoO problema com todos os pipelines é que você está basicamente dobrando o trabalho. Não importa a rapidez da descompressão, os dados ainda precisam ser transferidos para outro processo.
O Perl possui o PerlIO :: gzip, que permite ler diretamente os fluxos compactados em gzip. Portanto, ele pode oferecer uma vantagem, mesmo que sua velocidade de descompressão não corresponda à de
unpigz
:Eu tentei com um arquivo compactado em gzip de 13 MB (descompacta para 1,4 GB) em um antigo MacBook Pro 2010 com 16 GB de RAM e um ThinkPad T400 antigo com 8 GB de RAM com o arquivo já no cache. No Mac, o script Perl era significativamente mais rápido do que o uso de pipelines (5 segundos vs 22 segundos), mas no ArchLinux, ele perdia a desvantagem:
versus
e
Claramente, o uso
unpigz -c file.gz | wc -l
é o vencedor aqui, tanto em termos de velocidade. E essa linha de comando simples certamente supera a escrita de um programa, por mais curto que seja.fonte
gzip | wc
tem a mesma velocidade que o seu script perl. Epigz | wc
é o dobro da velocidade.gzip
roda com a mesma velocidade, independentemente de eu gravar a saída em / dev / null ou canalizar owc
que eu acredito é que a "biblioteca gzip" usada pelo perl é mais rápida que a ferramenta de linha de comando gzip. Talvez haja outro problema específico do Mac / Darwin com os pipes. Ainda é incrível que esta versão perl seja competitiva.zcat
e pior queunpigz
. Estou impressionado com a rapidez com que o pipeline é no sistema Linux comparado ao Mac. Eu não esperava isso, mesmo que eu já tenha observado o mesmo programa ser executado mais rapidamente em uma VM Linux Linux com CPU limitada no mesmo Mac do que no bare metal.zcat | wc -l
. Honestamente, estou impressionado com a variação que as pessoas estão relatando aqui, especialmente entre Linux e MacOS X!wc -l
leva 2,5 segundos.gzcat compressed.gz > /dev/null
leva 2,7 segundos. No entanto, o pipeline leva 22 segundos. Se eu tentar o GNUwc
, leva apenas meio segundo no arquivo descomprimido, mas 22 segundos no pipeline. O GNUzcat
leva o dobro do tempo para executarzcat compressed.gz > /dev/null
. Este é o Mavericks, antigo CPU Core 2 Duo, 16 GB de RAM, SSD Crucial MX100.A resposta de Kusalananda é principalmente correta. Para contar linhas, você precisa procurar novas linhas. No entanto, é teoricamente possível procurar novas linhas sem descompactar completamente o arquivo.
O gzip usa a compactação DEFLATE. DEFLATE é uma combinação de codificação LZ77 e Huffman. Pode haver uma maneira de descobrir apenas o nó do símbolo Huffman para nova linha e ignorar o resto. Quase certamente existe uma maneira de procurar novas linhas codificadas usando o L277, manter uma contagem de bytes e ignorar todo o resto.
Então, IMHO é teoricamente possível encontrar uma solução mais eficiente que unpigz ou zgrep. Dito isto, certamente não é prático (a menos que alguém já tenha feito isso).
fonte
Pode ser feito usando
zgrep
com-c
flag e$
parâmetro.Nesse caso, -c instrua o comando a gerar o número de linhas correspondentes e o regex $ corresponde ao final da linha, de forma que corresponda a todas as linhas ou arquivos.
Como comentado por @ StéphaneChazelas -
zgrep
só é um script em tornozcat
egrep
e deve proporcionar um desempenho semelhante ao sugestão original dezcat | wc -l
fonte
zgrep
geralmente é um script que invocazcat
(o mesmo quegzip -dcq
) para descompactar os dados e alimentá-losgrep
, por isso não vai ajudar.Como você pode ver, a maioria das respostas tenta otimizar o que pode: o número de alternâncias de contexto e E / S entre processos. O motivo é que esse é o único que você pode otimizar aqui facilmente.
Agora, o problema é que sua necessidade de recursos é quase insignificante para a necessidade de recursos da descompressão. É por isso que as otimizações realmente não tornam nada mais rápido.
Onde poderia ser realmente acelerado, seria um algoritmo un-gzip (isto é, descompressão) modificado, que deixa de fora a produção real do fluxo de dados descompactado; em vez disso, apenas calcula o número das novas linhas do fluxo descomprimido do comprimido um. Seria difícil, exigiria o conhecimento profundo do algoritmo do gzip (alguma combinação dos algoritmos de compressão LZW e Huffman ). É bem provável que o algoritmo não permita otimizar significativamente o tempo de descompressão com o raio, que precisamos apenas conhecer a contagem da nova linha. Mesmo que fosse possível, essencialmente uma nova biblioteca de descompactação gzip deveria ter sido desenvolvida (ela não existe até que seja conhecida).
A resposta realista para sua pergunta é que não, você não pode torná-la significativamente mais rápida.
Talvez você possa usar alguma descompressão paralela ao gzip, se existir. Ele pode usar vários núcleos da CPU para a descompressão. Se não existir, poderia ser desenvolvido com relativa facilidade.
Para o xz , existe um compressor paralelo (pxz).
fonte