paralelização fácil
-
07-07-2019 - |
Pergunta
Sinto-me frequentemente escrever simples para loops para executar uma operação para muitos arquivos, por exemplo:
for i in `find . | grep ".xml$"`; do bzip2 $i; done
Parece um pouco deprimente que na minha máquina de 4-core apenas um núcleo está sendo usado .. há uma maneira fácil eu posso adicionar paralelismo à minha shell script?
EDIT: Para introduzir um pouco mais contexto para os meus problemas, desculpe, eu não era mais claro para começar com
!Muitas vezes deseja executar scripts simples (ish), tais como enredo um gráfico, compactar ou descompactar, ou executar algum programa, em conjuntos de dados de tamanho razoável (geralmente entre 100 e 10.000). Os scripts que eu uso para resolver tais problemas parecido com o descrito acima, mas pode ter um comando diferente, ou mesmo uma sequência de comandos para executar.
Por exemplo, só agora eu estou correndo:
for i in `find . | grep ".xml.bz2$"`; do find_graph -build_graph $i.graph $i; done
Assim, meus problemas não são de forma bzip específico! (Embora bzip paralelo faz olhar fresco, eu pretendo usá-lo no futuro).
Solução
Se você tivesse que resolver o problema hoje você provavelmente usaria uma ferramenta como o GNU paralela (a menos que haja uma ferramenta paralelizado especializada para a sua tarefa como pbzip2
):
find . | grep ".xml$" | parallel bzip2
Para saber mais:
- Assista ao vídeo de introdução para uma rápida introdução: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
- Atravesse o tutorial (homem parallel_tutorial). Você comanda linha com amor por isso.
Outras dicas
Solução : Use xargs
para executar em paralelo (! Não se esqueça da opção -n
)
find -name \*.xml -print0 | xargs -0 -n 1 -P 3 bzip2
Este programa perl adapta às suas necessidades razoavelmente bem, você teria apenas que fazer isso:
runN -n 4 bzip2 `find . | grep ".xml$"`
GNU make tem um recurso interessante paralelismo (eg. -J 5) que iria trabalhar no seu caso. Criar um Makefile
%.xml.bz2 : %.xml
all: $(patsubt %.xml,%xml.bz2,$(shell find . -name '*.xml') )
, em seguida, fazer um
nice make -j 5
substituir '5' com um número, provavelmente 1 mais do que o número de CPUs. Você pode querer fazer 'agradável' este apenas no caso de alguém quiser usar a máquina enquanto você está nele.
A resposta para a pergunta geral é difícil, porque depende dos detalhes das coisas que você está paralelização. Por outro lado, para este fim específico, você deve usar pbzip2 vez de bzip2 simples (as chances são de que pbzip2 já está instalado ou, pelo menos nos repositórios ou sua distro). Veja aqui para mais detalhes: http://compression.ca/pbzip2/
I encontrar este tipo de contraproducente operação. O motivo é a mais processos acessar o disco ao mesmo tempo, quanto maior o tempo de leitura / gravação vai assim as extremidades resultado final em um longo tempo. O gargalo aqui não vai ser um problema de CPU, não importa quantos núcleos que você tem.
Você não já realizado um simples dois grandes cópias de arquivos ao mesmo tempo na mesma unidade HD? I é geralmente mais rápida para copiar um e depois o outro.
Eu sei que esta tarefa envolve algum poder de CPU (bzip2 é exigente método de compressão), mas tente medir primeira carga da CPU antes de ir a "desafiar" caminho que todos os técnicos tendem a escolher com muito mais frequência do que o necessário.
Eu fiz algo parecido com isto para a festança. O truque paralelo make é provavelmente muito mais rápido para one-offs, mas aqui é a seção de código principal para implementar algo parecido com isso em bash, você terá que modificá-lo para seus propósitos embora:
#!/bin/bash
# Replace NNN with the number of loops you want to run through
# and CMD with the command you want to parallel-ize.
set -m
nodes=`grep processor /proc/cpuinfo | wc -l`
job=($(yes 0 | head -n $nodes | tr '\n' ' '))
isin()
{
local v=$1
shift 1
while (( $# > 0 ))
do
if [ $v = $1 ]; then return 0; fi
shift 1
done
return 1
}
dowait()
{
while true
do
nj=( $(jobs -p) )
if (( ${#nj[@]} < nodes ))
then
for (( o=0; o<nodes; o++ ))
do
if ! isin ${job[$o]} ${nj[*]}; then let job[o]=0; fi
done
return;
fi
sleep 1
done
}
let x=0
while (( x < NNN ))
do
for (( o=0; o<nodes; o++ ))
do
if (( job[o] == 0 )); then break; fi
done
if (( o == nodes )); then
dowait;
continue;
fi
CMD &
let job[o]=$!
let x++
done
wait
Eu acho que você podia para o seguinte
for i in `find . | grep ".xml$"`; do bzip2 $i&; done
Mas isso cisão no entanto muitos processos que você tenha arquivos instantaneamente e não é um ideal como apenas correr quatro processos ao mesmo tempo.