A maneira mais rápida e eficiente de obter o número de registros (linhas) em um arquivo compactado com gzip

13

Estou tentando fazer uma contagem de registros em um arquivo gzip de 7,6 GB. Eu encontrei algumas abordagens usando o comando zcat .

$ 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 sendo executados bem rápido, mas com uma contagem incorreta de 28173811.

Como posso executar uma contagem de registros em um período mínimo?

    
por Rahul 08.05.2017 / 09:22

6 respostas

27

Os comandos sed , perl e awk que você mencionou podem estar corretos, mas todos eles 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 não compactados.

Para contar o número de linhas nos dados não compactados, não há como descompactá-lo. Sua abordagem com zcat é a abordagem correta e, como os dados são muito grandes, será necessário tempo para descompactá-los.

A maioria dos utilitários que lida com a compactação e descompactação gzip provavelmente usará as mesmas rotinas de biblioteca compartilhada para fazer isso. A única maneira de acelerá-lo seria encontrar uma implementação das rotinas zlib que são de alguma forma mais rápidas que as padrão, e reconstruir, e. zcat para usá-los.

    
por 08.05.2017 / 09:28
18

Use unpigz.

A resposta de Kusalananda está correta, você precisará precisar descompactar o arquivo inteiro para verificar seu conteúdo. /bin/gunzip faz isso o mais rápido possível, em um único núcleo. Pigz é uma implementação paralela de gzip que pode usar vários núcleos.

Infelizmente, a própria descompactação dos arquivos gzip normais não pode ser paralelizada, mas pigz oferece uma versão aprimorada de gunzip , unpigz , que faz trabalhos relacionados, como leitura, gravação e soma de verificação em um encadeamento separado . Em alguns benchmarks rápidos, unpigz é quase duas vezes mais rápido que gunzip em minha máquina core i5.

Instale pigz com seu gerenciador de pacotes favorito e use unpigz em vez de gunzip ou unpigz -c em vez de zcat . Então seu comando se torna:

$ unpigz -c T.csv.gz | wc -l

Tudo isso pressupõe que o gargalo é a CPU, não o disco, é claro.

    
por 08.05.2017 / 11:37
5

O problema com todos os pipelines é que você está essencialmente dobrando o trabalho. Não importa quão rápido a descompressão seja, os dados ainda precisam ser transferidos para outro processo.

Perl tem PerlIO :: gzip que permite ler streams gzipados diretamente. Portanto, ele pode oferecer uma vantagem, mesmo que sua velocidade de descompactação não corresponda à de unpigz :

#!/usr/bin/env perl

use strict;
use warnings;

use autouse Carp => 'croak';
use PerlIO::gzip;

@ARGV or croak "Need filename\n";

open my $in, '<:gzip', $ARGV[0]
    or croak "Failed to open '$ARGV[0]': $!";

1 while <$in>;

print "$.\n";

close $in or croak "Failed to close '$ARGV[0]': $!";

Eu tentei com um arquivo compactado gzip de 13 MB (descomprime até 1,4 GB) em um antigo 2010 MacBook Pro com 16 GB de RAM e um antigo ThinkPad T400 com 8 GB de RAM com o arquivo que já está no cache. No Mac, o script Perl foi significativamente mais rápido do que o uso de pipelines (5 segundos vs 22 segundos), mas no ArchLinux, perdeu para unpigz:

$ time -p ./gzlc.pl spy.gz 
1154737
real 4.49
user 4.47
sys 0.01

versus

$ time -p unpigz -c spy.gz | wc -l
1154737
real 3.68
user 4.10
sys 1.46

e

$ time -p zcat spy.gz | wc -l
1154737
real 6.41
user 6.08
sys 0.86

Claramente, usar unpigz -c file.gz | wc -l é o vencedor aqui, tanto em termos de velocidade. E, essa simples linha de comando certamente bate escrevendo um programa, ainda que curto.

    
por 08.05.2017 / 18:04
4

A resposta de Kusalananda é principalmente correta. Para contar as linhas, você precisa procurar por novas linhas. No entanto, é teoricamente possível procurar por novas linhas sem descompactar completamente o arquivo.

O gzip usa a compressão DEFLATE. DEFLATE é uma combinação de codificação LZ77 e Huffman. Pode haver uma maneira de descobrir apenas o nó do símbolo de Huffman para a nova linha e ignorar o resto. Há quase certamente uma maneira de procurar por novas linhas codificadas usando L277, manter uma contagem de bytes e ignorar todo o resto.

Então IMHO é teoricamente possível chegar a uma solução mais eficiente do que unpigz ou zgrep. Dito isto, certamente não é prático (a menos que alguém já tenha feito isso).

    
por 08.05.2017 / 14:43
1

Pode ser feito usando zgrep com -c flag e $ .

Nesse caso, instrua o comando a gerar o número de linhas correspondentes e o regex $ corresponde ao fim da linha para que corresponda a cada linha ou arquivo.

zgrep -c $ T.csv.gz 

Como comentado por @ StéphaneChazelas - zgrep é apenas um script em torno de zcat e grep e deve fornecer desempenho semelhante à sugestão original de zcat | wc -l

    
por 08.05.2017 / 09:47
0

Como você pode ver, a maioria das respostas tenta otimizar o que pode: o número de opções de contexto e IO entre processos. A razão é que este é o único que você pode otimizar aqui facilmente.

Agora, o problema é que a necessidade de recursos é quase insignificante para a necessidade de recursos da descompactação. É por isso que as otimizações não farão nada mais rápido.

Onde poderia ser realmente acelerado, seria um algoritmo modificado de un-gzip (isto é, descompressão), que deixa de fora a produção real do fluxo de dados descompactado; em vez disso, ele calcula apenas o número de novas linhas no fluxo descompactado do comprimido . Seria difícil, exigiria o conhecimento profundo do algoritmo do gzip (alguma combinação do Algoritmos de compressão LZW e Huffman ). É bastante provável que o algoritmo não permita otimizar significativamente o tempo de descompressão com o raio, que precisamos apenas saber as contagens de novas linhas. Mesmo que fosse possível, essencialmente uma nova biblioteca de descompressão gzip deveria ter sido desenvolvida (não existe até saber).

A resposta realista à sua pergunta é que, não, você não pode torná-la significativamente mais rápida.

Talvez você possa usar alguma descompactação gzip paralelizada, se existir. Ele poderia usar vários núcleos de CPU para a descompactação. Se não existir, pode ser desenvolvido de forma relativamente fácil.

Para o xz , existe um compressor paralelo (pxz).

    
por 09.05.2017 / 17:24

Tags