Como usar o GNU paralelamente de forma eficaz

8

Suponha que eu queira encontrar todas as correspondências no arquivo de texto compactado:

$ gzcat file.txt.gz | pv --rate -i 5 | grep some-pattern

pv --rateusado aqui para medir a produtividade do tubo. Na minha máquina, são cerca de 420 Mb / s (após a descompressão).

Agora estou tentando fazer grep paralelo usando o GNU paralelo.

$ gzcat documents.json.gz | pv --rate -i 5 | parallel --pipe -j4 --round-robin grep some-pattern

Agora, a taxa de transferência caiu para ~ 260Mb / s. E o que é o parallelprocesso mais interessante em si é usar muita CPU. Mais que grepprocessos (mas menos que gzcat).

Edição 1 : Eu tentei diferentes tamanhos de bloco ( --block), bem como diferentes valores para -N/ -Loptions. Nada me ajuda neste momento.

O que estou fazendo errado?

Denis Bazhenov
fonte

Respostas:

9

Estou realmente surpreso que você tenha 270 MB / s usando o GNU Parallel's --pipe. Meus testes geralmente atingem o máximo de 100 MB / s.

Seu gargalo é mais provável no GNU Parallel: --pipenão é muito eficiente. --pipepart, no entanto, é: Aqui posso obter na ordem de 1 GB / s por núcleo de CPU.

Infelizmente, existem algumas limitações no uso de --pipepart:

  • O arquivo deve ser procurado (ou seja, sem canal)
  • Você deve conseguir encontrar o início de um registro com --recstart / - recend (ou seja, nenhum arquivo compactado)
  • O número da linha é desconhecido (portanto, você não pode ter um registro de 4 linhas).

Exemplo:

parallel --pipepart -a bigfile --block 100M grep somepattern
Ole Tange
fonte
1
Obrigado. Existe alguma razão para --pipeser ineficiente? Quero dizer, é algum tipo de problema fundamental ou mais específico da implementação.
Denis Bazhenov
2
Sim: O GNU Parallel é escrito em perl, e com --pipecada byte único precisa passar pelo processo único, que precisa processar um pouco em cada byte. A --pipepartmaioria dos bytes nunca é vista pelo processo central: eles são processados ​​por trabalhos gerados. Como são poucas as linhas que constituem o gargalo, --pipeeu gostaria de receber um codificador C / C ++ que reescrevesse a parte que seria executada para as pessoas que possuem o compilador C no caminho.
Toll Ole Tange
2

grep é muito eficaz - não há sentido em executá-lo em paralelo. No seu comando, apenas a descompressão precisa de mais CPU, mas isso não pode ser paralelo.

A divisão da entrada por paralelo precisa de mais CPU do que a obtenção de linhas correspondentes por grep.

A mudança de situação se você deseja usar, em vez de grep, algo que precisa de muito mais CPU para cada linha - então paralelo teria mais sentido.

Se você deseja acelerar esta operação - veja onde estão os gargalos - provavelmente é descompactação (ajuda a usar outra ferramenta de descompressão ou melhor CPU) ou - leitura do disco (ajuda a usar outra ferramenta de descompressão ou sistema de disco melhor).

Pela minha experiência - às vezes é melhor usar o lzma (-2 por exemplo) para compactar / descompactar arquivos - ele tem uma compactação mais alta que o gzip, portanto, menos dados precisam ser lidos do disco e a velocidade é comparável.

indefinir
fonte
1
Na verdade, é o meu caso. Um processo Java com muita CPU é usado em vez de grep. Simplifiquei a pergunta um pouco. E ainda, comer em paralelo muita CPU não fornece muito trabalho aos processos Java.
Denis Bazhenov
1

A descompressão é o gargalo aqui. Se a descompressão não for paralelizada internamente, você não conseguirá isso sozinha. Se você tiver mais de um trabalho como esse, é claro iniciá-los em paralelo, mas é difícil paralelizar seu pipeline por si só. Dividir um fluxo em fluxos paralelos quase nunca vale a pena e pode ser muito doloroso com a sincronização e a mesclagem. Às vezes, você apenas precisa aceitar que vários núcleos não ajudarão em todas as tarefas que você está executando.

Em geral, a paralelização no shell deve estar principalmente no nível de processos independentes.

orion
fonte
1
Não parece que a descompressão é um gargalo no caso de uso parallel. Concordo que certamente está no primeiro caso (sem paralelo), mas no segundo (com paralelo) o gargalo está no lado paralelo. Isso decorre da observação de que a taxa de transferência cai significativamente, conforme medido por pv. Se o gargalo estiver em descompressão, a taxa de transferência não mudará o que você adicionar ao pipeline. É uma definição muito intuitiva de taxa de transferência, eu acho - a coisa que mais limita a taxa de transferência.
Denis Bazhenov
1
É possível que o grep seja tão rápido, que termine mais rápido do que parallelpode gravar no seu pipe. Nesse caso, a maioria dos grepprocessos simplesmente espera obter mais, enquanto paralleltrabalha o tempo todo para multiplexar os blocos em vários pipes (que são operações de E / S extras e podem até bloquear a descompressão, se o buffer estiver cheio). Você também tentou brincar com o --blockparâmetro? O padrão é que 1M, até que um grep obtenha 1Mdados, o restante quase certamente já está concluído. Portanto, voltamos ao fato de que não faz sentido paralelizar isso.
orion
1
Sim, eu tentei essas opções com tamanho de bloco grande e pequeno. Bem como valores diferentes para -N/ -Lopções. Parece que as opções padrão estão muito perto de ótimo local que eu experimentar :)
Denis Bazhenov
1
Tente cronometrar com e sem pv(com time). Dessa forma, você pode ver se pvestá diminuindo a velocidade. Se for, parallelcopiar dados em pipes é uma sobrecarga adicional. E, de qualquer forma, tenho certeza de que grepé quase em tempo real nesse caso, especialmente se o padrão for uma sequência simples sem muito retorno. Além disso, parallelirá intercalar e atrapalhar as grepsaídas.
orion
1
Vou verificar pvse isso não causa o problema, obrigado pelo conselho.
Denis Bazhenov