Aprenda scripts de multi-threading com o GNU Parallel

Se você está cansado de seus scripts Bash levarem uma eternidade para serem executados, este tutorial é para você. Frequentemente, é possível executar scripts Bash em paralelo, o que pode acelerar dramaticamente o resultado. Como? Usando a utilidade GNU Parallel, também chamada apenas de Parallel, com alguns exemplos úteis do GNU Parallel!

O Parallel executa scripts Bash em paralelo por meio de um conceito chamado multithreading. Essa utilidade permite que você execute diferentes trabalhos por CPU em vez de apenas um, reduzindo o tempo de execução de um script.

Neste tutorial, você aprenderá a criar scripts Bash multithreading com uma tonelada de ótimos exemplos do GNU Parallel!

Pré-requisitos

Este tutorial estará repleto de demonstrações práticas. Se você pretende acompanhar, certifique-se de ter o seguinte:

  • A Linux computer. Any distribution will work. The tutorial uses Ubuntu 20.04 running on Windows Subsystem for Linux (WSL).
  • Logado com um usuário com privilégios de sudo.

Instalando o GNU Parallel

Para começar a acelerar scripts Bash com multithreading, primeiro você precisa instalar o Parallel. Então, vamos começar baixando e instalando-o.

1. Abra um terminal Bash.

2. Execute wget para baixar o pacote do Parallel. O comando abaixo baixa a versão mais recente (parallel-latest) para o diretório de trabalho atual.

wget https://ftp.gnu.org/gnu/parallel/parallel-latest.tar.bz2

Se preferir usar uma versão mais antiga do GNU Parallel, você pode encontrar todos os pacotes no site oficial de download.

3. Agora, execute o comando tar abaixo para descompactar o pacote que você acabou de baixar.

Abaixo, o comando usa a opção x para extrair o arquivo, j para especificar que se trata de um arquivo com extensão .bz2 e f para aceitar um arquivo como entrada para o comando tar. sudo tar -xjf parallel-latest.tar.bz2

sudo tar -xjf parallel-latest.tar.bz2

Agora, você deve ter um diretório chamado parallel- com o mês, dia e ano do lançamento mais recente.

4. Navegue até a pasta de arquivo do pacote com o comando cd. Neste tutorial, a pasta de arquivo do pacote é chamada de parallel-20210422, como mostrado abaixo.

Navigating to the Parallel archive folder

5. Em seguida, construa e instale o binário do GNU Parallel executando os seguintes comandos:

./configure
 make
 make install

Agora, verifique se o Parallel foi instalado corretamente verificando a versão instalada.

parallel --version
Checking the GNU Parallel version

Ao executar o Parallel pela primeira vez, você também pode ver algumas linhas assustadoras que exibem texto como perl: warning:. Essas mensagens de aviso indicam que o Parallel não consegue detectar as configurações atuais de localidade e idioma. Mas não se preocupe com esses avisos por enquanto. Você aprenderá como corrigir esses avisos posteriormente.

Configurando o GNU Parallel

Agora que o Parallel está instalado, você pode usá-lo imediatamente! Mas primeiro, é importante configurar algumas configurações menores antes de começar.

Enquanto ainda estiver no seu terminal Bash, concorde com a permissão de pesquisa acadêmica do GNU Parallel academic research permission, informando ao Parallel que você o citará em qualquer pesquisa acadêmica, especificando o parâmetro citation seguido por will cite.

Se você não quiser apoiar o GNU ou seus mantenedores, concordar em citar não é necessário para usar o GNU Parallel.

parallel --citation
will cite

Altere o local definindo as seguintes variáveis de ambiente executando as linhas de código abaixo. Definir variáveis de ambiente de localidade e idioma como esta não é um requisito. Mas o GNU Parallel as verifica toda vez que é executado.

Se as variáveis de ambiente não existirem, o Parallel reclamará sobre elas toda vez, como você viu na seção anterior.

Este tutorial presume que você é um falante de inglês. Outros idiomas também são suportados.

export LC_ALL=C man
export LANGUAGE=en_US
export LANG=en_US.UTF-8
Setting locale and language for GNU Parallel

Executando Comandos Shell Ad-Hoc

Vamos começar agora a usar o GNU Parallel! Para começar, você aprenderá a sintaxe básica. Uma vez que esteja confortável com a sintaxe, você poderá explorar alguns exemplos úteis do GNU Parallel mais tarde.

Para começar, vamos cobrir um exemplo super simples de apenas ecoar os números de 1 a 5.

1. No seu terminal Bash, execute os seguintes comandos. Empolgante, certo? O Bash usa o comando echo para enviar os números de 1 a 5 para o terminal. Se você colocasse cada um desses comandos em um script, o Bash os executaria sequencialmente, aguardando o término do anterior.

Neste exemplo, você está executando cinco comandos que não levam praticamente nenhum tempo. Mas, imagine se esses comandos fossem scripts Bash que realmente fizessem algo útil, mas levassem uma eternidade para serem concluídos?

 echo 1
 echo 2
 echo 3
 echo 4
 echo 5

Agora, execute cada um desses comandos ao mesmo tempo com o Parallel, como mostrado abaixo. Neste exemplo, o Parallel executa o comando echo e, designado pelo :::, passa a esse comando os argumentos, 1, 2, 3, 4, 5. Os três dois-pontos indicam ao Parallel que você está fornecendo a entrada via linha de comando em vez do pipeline (mais sobre isso depois).

No exemplo abaixo, você passou um único comando para o Parallel sem opções. Aqui, como todos os exemplos do Parallel, o Parallel iniciou um novo processo para cada comando usando um núcleo de CPU diferente.

# Da linha de comando
 parallel echo ::: 1 2 3 4 5

Todos os comandos do Parallel seguem a sintaxe parallel [Opções] <Comando para multithread>.

3. Para demonstrar o recebimento paralelo de entrada do pipeline Bash, crie um arquivo chamado count_file.txt como abaixo. Cada número representa o argumento que você passará para o comando echo.

 1
 2
 3
 4
 5

4. Agora, execute o comando cat para ler esse arquivo e passar a saída para o Parallel, conforme mostrado abaixo. Neste exemplo, o {} representa cada argumento (1-5) que será passado para o Parallel.

# Do pipeline cat count_file.txt | parallel echo {}
GNU Parallel Example #1

Comparando Bash e GNU Parallel

Neste momento, usar o Parallel pode parecer apenas uma maneira complicada de executar comandos Bash. Mas o verdadeiro benefício para você é a economia de tempo. Lembre-se, o Bash será executado em apenas um núcleo de CPU, enquanto o GNU Parallel será executado em vários de uma só vez.

1. Para demonstrar a diferença entre comandos sequenciais do Bash versus o Parallel, crie um script Bash chamado test.sh com o código a seguir. Crie este script no mesmo diretório onde você criou o count_file.txt anteriormente.

O script Bash abaixo lê o arquivo count_file.txt, espera por 1, 2, 3, 4 e 5 segundos, ecoa o comprimento do tempo de espera no terminal e termina.

#!/bin/bash
 nums=$(cat count_file.txt) # Ler count_file.txt
 for num in $nums           # Para cada linha no arquivo, iniciar um loop
 do
     sleep $num             # Ler a linha e aguardar tantos segundos
     echo $num              # Imprimir a linha
 done

2. Agora, execute o script usando o time comando para medir quanto tempo o script leva para ser concluído. Levará 15 segundos.

time ./test.sh

3. Agora, use o comando time novamente para realizar a mesma tarefa, mas desta vez use o Paralelo para fazê-lo.

O comando abaixo executa a mesma tarefa, mas desta vez, em vez de esperar o primeiro loop ser concluído antes de iniciar o próximo, ele será executado em cada núcleo da CPU e iniciará o máximo possível ao mesmo tempo.

time cat count_file.txt | parallel "sleep {}; echo {}"
The prompt on the right side of the terminal confirms the time each command took to complete

Conheça o Teste de Execução!

Agora é hora de entrar em alguns exemplos do Paralelo GNU mais voltados para o mundo real. Mas, antes de fazer isso, você deve primeiro conhecer a bandeira --dryrun. Esta bandeira é útil quando você deseja ver o que acontecerá sem o Paralelo realmente fazer isso.

O sinalizador --dryrun pode ser a verificação final de sanidade antes de executar um comando que não se comporta da maneira que você imaginou. Infelizmente, se você inserir um comando que possa prejudicar o seu sistema, a única coisa que o GNU Parallel ajudará você a fazer é prejudicá-lo mais rápido!

parallel --dryrun "rm rf {}"

Exemplo do GNU Parallel #1: Baixando Arquivos da Web

Para esta tarefa, você irá baixar uma lista de arquivos de várias URLs na web. Por exemplo, essas URLs podem representar páginas da web que você deseja salvar, imagens ou até mesmo uma lista de arquivos de um servidor FTP.

Para este exemplo, você irá baixar uma lista de pacotes de arquivos (e os arquivos SIG) do servidor FTP do GNU Parallel.

1. Crie um arquivo chamado download_items.txt, pegue alguns links de download do site oficial de download e adicione-os ao arquivo separados por uma nova linha.

 https://ftp.gnu.org/gnu/parallel/parallel-20120122.tar.bz2
 https://ftp.gnu.org/gnu/parallel/parallel-20120122.tar.bz2.sig
 https://ftp.gnu.org/gnu/parallel/parallel-20120222.tar.bz2
 https://ftp.gnu.org/gnu/parallel/parallel-20120222.tar.bz2.sig

Você pode economizar tempo usando a biblioteca Beautiful Soup do Python para extrair todos os links da página de download.

2. Leia todas as URLs do arquivo download_items.txt e passe-as para o Parallel, que invocará o wget e passará cada URL.

cat download_items.txt | parallel wget {}

Não se esqueça de que {} em um comando paralelo é um espaço reservado para a string de entrada!

3. Talvez você precise controlar o número de threads que o GNU Parallel usa de uma vez. Se for o caso, adicione o parâmetro --jobs ou -j ao comando. O parâmetro --jobs limita o número de threads que podem ser executadas simultaneamente para o número que você especificar.

Por exemplo, para limitar o Parallel a baixar cinco URLs de uma vez, o comando ficaria assim:

#!/bin/bash
 cat download_items.txt | parallel --jobs 5 wget {}

O parâmetro --jobs no comando acima pode ser ajustado para baixar qualquer número de arquivos, desde que o computador em que você está executando tenha CPUs suficientes para processá-los.

4. Para demonstrar o efeito do parâmetro --jobs, agora ajuste a contagem de trabalhos e execute o comando time para medir quanto tempo cada execução leva.

 time cat download_items.txt | parallel --jobs 5 wget {}
 time cat download_items.txt | parallel --jobs 10 wget {}

Exemplo do GNU Parallel #2: Descompactando Pacotes de Arquivos

Agora que você baixou todos esses arquivos de arquivamento do exemplo anterior, agora você precisa descompactá-los.

Enquanto estiver no mesmo diretório dos pacotes de arquivos, execute o seguinte comando Parallel. Observe o uso do wildcard (*). Como este diretório contém tanto pacotes de arquivos quanto os arquivos SIG, você deve informar ao Parallel para processar apenas os arquivos .tar.bz2.

sudo parallel tar -xjf ::: *.tar.bz2

Bônus! Se você estiver usando o GNU parallel interativamente (não em um script), adicione a opção --bar para que o Parallel mostre uma barra de progresso enquanto a tarefa está em execução.

Showing the output from the --bar flag

Exemplo do GNU Parallel #3: Removendo Arquivos

Se você seguiu os exemplos um e dois, agora deve ter muitas pastas no seu diretório de trabalho ocupando espaço. Então vamos remover todos esses arquivos em paralelo!

Para remover todas as pastas que começam com parallel- usando o Parallel, liste todas as pastas com o comando ls -d e redirecione cada um dos caminhos das pastas para o Parallel, invocando o rm -rf em cada pasta, conforme mostrado abaixo.

Lembre-se da flag --dryrun!

ls -d parallel-*/ | parallel "rm -rf {}"

Conclusão

Agora você pode automatizar tarefas com o Bash e economizar muito tempo. O que você escolher fazer com esse tempo é com você. Se economizar tempo significa sair um pouco mais cedo do trabalho ou ler outro post do blog ATA, é tempo de volta no seu dia.

Agora pense em todos os scripts de longa duração em seu ambiente. Quais você pode acelerar com o Parallel?

Source:
https://adamtheautomator.com/how-to-speed-up-bash-scripts-with-multithreading-and-gnu-parallel/