Desempenho do loop versus expansão

9

Precisa de sugestões de especialistas na comparação abaixo:

Segmento de código usando loop:

for file in `cat large_file_list`
do
    gzip -d $file
done

Segmento de código usando expansão simples:

gzip -d `cat large_file_list`

Qual será mais rápido? Tem que manipular um grande conjunto de dados.

Leon
fonte
1
A resposta correta dependerá de quanto tempo leva para iniciar gzipo sistema, o número de arquivos na lista de arquivos e o tamanho desses arquivos.
Kusalananda
A lista de arquivos terá entre 1000 e 10000 arquivos. O tamanho varia de alguns kilobytes a 500 MB. Não tenho idéia de quanto tempo leva para iniciar o gzip no meu sistema. de qualquer maneira verificar?
Leon
1
Ok, também pode depender do tamanho dos nomes dos arquivos . Se os nomes dos arquivos forem longos, alguns sistemas poderão gerar um erro "lista de argumentos muito longa" se você tentar fazê-lo sem um loop, pois a substituição do comando resultaria em uma linha de comando muito longa para a execução do shell. Se você não quiser depender do número de arquivos na lista, basta usar um loop. Você está gastando um tempo significativo descompactando esses arquivos em comparação com o outro processamento que você executará neles?
Kusalananda
Leon dê uma olhada nos meus resultados de teste: "enorme-arglist" é 20x mais rápido que "loop" na minha configuração.
para um meio termo entre processo começa e comprimento da linha de comando, use algo como xargs gzip -d < large_file_list, mas cuidado com os espaços em nomes de arquivos, talvez comtr \\n \\0 large_file_list | xargs -0 gzip -d
w00t

Respostas:

19

Complicações

O seguinte funcionará apenas algumas vezes:

gzip -d `cat large_file_list`

Três problemas são (na bashmaioria das conchas semelhantes a Bourne):

  1. Ele falhará se qualquer nome de arquivo tiver uma guia de espaço ou caracteres de nova linha (supondo $IFSque não tenha sido modificado). Isso ocorre por causa da divisão da palavra do shell .

  2. Também é provável que falhe se algum nome de arquivo tiver caracteres ativos globos nele. Isso ocorre porque o shell aplicará a expansão do nome do caminho à lista de arquivos.

  3. Também falhará se os nomes de arquivos começarem -(se POSIXLY_CORRECT=1isso se aplicar apenas ao primeiro arquivo) ou se houver algum nome de arquivo -.

  4. Ele também falhará se houver muitos nomes de arquivos nele para caber em uma linha de comando.

O código abaixo está sujeito aos mesmos problemas que o código acima (exceto o quarto)

for file in `cat large_file_list`
do
    gzip -d $file
done

Solução confiável

Se você large_file_listtiver exatamente um nome de arquivo por linha e um arquivo chamado -não estiver entre eles, e você estiver em um sistema GNU, use:

xargs -rd'\n' gzip -d -- <large_file_list

-d'\n'diz xargspara tratar cada linha de entrada como um nome de arquivo separado.

-rdiz xargspara não executar o comando se o arquivo de entrada estiver vazio.

--informa gzipque os seguintes argumentos não devem ser tratados como opções, mesmo que iniciem -. -sozinho ainda seria tratado como em -vez do arquivo chamado -.

xargscolocará muitos nomes de arquivo em cada linha de comando, mas não tantos que exceda o limite da linha de comando. Isso reduz o número de vezes que um gzipprocesso deve ser iniciado e, portanto, torna isso rápido. Também é seguro: os nomes dos arquivos também serão protegidos contra a divisão de palavras e a expansão do nome do caminho .

John1024
fonte
Obrigado pela resposta detalhada. Entendo os três problemas mencionados. O nome do arquivo é simples e não enfrentará esses desafios, pois a lista conterá até 20.000. E minha pergunta é basicamente sobre o desempenho desses dois segmentos. Obrigado.
Leon
1
@Leon O forloop será, de longe, o mais lento. Os outros dois métodos terão velocidade muito próxima um do outro.
precisa saber é o seguinte
7
Além disso, não descarte os problemas em potencial: muitas perguntas aqui no StackExchange são porque a divisão de palavras ou a expansão do nome do caminho aconteceu com pessoas que não estavam esperando por isso.
precisa saber é o seguinte
5
Observe também que há variação na leitura de um arquivo com xargs: pelo menos a versão GNU possui a --arg-fileopção (formato abreviado -a). Então, um poderia fazer em seu xargs -a large_file_list -rd'\n' gzip -d lugar. Efetivamente, não há diferença, além do fato de que <é operador de shell e faria xargsler de stdin (que desembolsar "links" para arquivo), enquanto que -afaria xargsexplicitamente abrir o arquivo em questão
Sergiy Kolodyazhnyy
2
terdon observou em outro comentário sobre o uso parallelpara executar várias cópias de gzip, mas xargs(pelo menos a GNU), também tem a -Popção para isso. Em máquinas multicore, isso pode fazer a diferença. Mas também é possível que a descompressão seja completamente ligada à E / S.
21919 ilkkachu
12

Duvido que isso importe muito.

Eu usaria um loop, apenas porque não sei quantos arquivos estão listados no arquivo de lista e (geralmente) não sei se algum dos nomes de arquivos tem espaços em seus nomes. Fazer uma substituição de comando que geraria uma lista muito longa de argumentos pode resultar em um erro "Lista de argumentos muito longa" quando o comprimento da lista gerada for muito longo.

Meu loop ficaria assim

while IFS= read -r name; do
    gunzip "$name"
done <file.list

Isso também me permitiria inserir comandos para processar os dados após o gunzipcomando. De fato, dependendo do que os dados realmente são e do que precisa ser feito com eles, pode até ser possível processá-los sem salvá-los no arquivo:

while IFS= read -r name; do
    zcat "$name" | process_data
done <file.list

(onde process_dataestá um pipeline que lê os dados não compactados da entrada padrão)

Se o processamento dos dados demorar mais que a descompactação, a questão de saber se um loop é mais eficiente ou não se torna irrelevante.

Idealmente , eu preferiria não elaborar uma lista de nomes de arquivos e usar um padrão globbing de nome de arquivo, como em

for name in ./*.gz; do
    # processing of "$name" here
done

onde ./*.gzestá algum padrão que corresponde aos arquivos relevantes. Dessa forma, não dependemos do número de arquivos nem dos caracteres usados ​​nos nomes de arquivos (eles podem conter novas linhas ou outros caracteres de espaço em branco, ou começar com traços, etc.)

Relacionado:

Kusalananda
fonte
5

Desses, o arquivo com todos os arquivos passados ​​para uma única chamada gzipprovavelmente será mais rápido, exatamente porque você só precisa iniciar gzipuma vez. (Ou seja, se o comando funcionar, consulte as outras respostas para as advertências.)

Mas, gostaria de lembrar a regra de ouro da otimização : não faça isso prematuramente.

  1. Não otimize esse tipo de coisa antes de perceber que é um problema.

    Essa parte do programa leva muito tempo? Bem, descomprimir arquivos grandes pode, e você precisará fazê-lo de qualquer maneira, portanto, pode não ser tão fácil de responder.

  2. A medida. Realmente, é a melhor maneira de ter certeza.

    Você verá os resultados com seus próprios olhos (ou com seu próprio cronômetro), e eles se aplicarão à sua situação que respostas aleatórias na Internet podem não ter. Coloque as duas variantes nos scripts e execute time script1.she time script2.sh. (Faça isso com uma lista de arquivos compactados vazios para medir a quantidade absoluta da sobrecarga.)

ilkkachu
fonte
0

Qual a velocidade do seu disco?

Isso deve usar todas as suas CPUs:

parallel -X gzip -d :::: large_file_list

Portanto, seu limite provavelmente será a velocidade do seu disco.

Você pode tentar ajustar com -j:

parallel -j50% -X gzip -d :::: large_file_list

Isso executará metade dos trabalhos em paralelo como o comando anterior e estressará menos o disco, portanto, dependendo do disco, isso pode ser mais rápido.

Ole Tange
fonte