Eu criei este script por tédio com o único propósito de usar/testar o GNU paralelo, então sei que não é particularmente útil ou otimizado, mas tenho um script que calculará todos os números primos até n:
#!/usr/bin/env bash
isprime () {
local n=$1
((n==1)) && return 1
for ((i=2;i<n;i++)); do
if ((n%i==0)); then
return 1
fi
done
printf '%d\n' "$n"
}
for ((f=1;f<=$1;f++)); do
isprime "$f"
done
Quando executado com o loop:
$ time ./script.sh 5000 >/dev/null
real 0m28.875s
user 0m38.818s
sys 0m29.628s
Eu esperaria que a substituição do loop for pelo GNU paralelo fizesse com que isso fosse executado significativamente mais rápido, mas essa não foi minha experiência. Em média, é apenas cerca de 1 segundo mais rápido:
#!/usr/bin/env bash
isprime () {
local n=$1
((n==1)) && return 1
for ((i=2;i<n;i++)); do
if ((n%i==0)); then
return 1
fi
done
printf '%d\n' "$n"
}
export -f isprime
seq 1 $1 | parallel -j 20 -N 1 isprime {}
Executar com paralelo:
$ time ./script.sh 5000 >/dev/null
real 0m27.655s
user 0m38.145s
sys 0m28.774s
Eu não estou realmente interessado em otimizar a isprime()
função, só estou querendo saber se há algo que eu possa fazer para otimizar o GNU paralelo?
No meu teste, seq
na verdade, é mais rápido do que for ((i=1...))
, então, não acho que tenha muito a ver com o tempo de execução
Curiosamente, se eu modificar o loop for para:
for ((f=1;f<=$1;f++)); do
isprime "$f" &
done | sort -n
Ele roda ainda mais rápido:
$ time ./script.sh 5000 >/dev/null
real 0m5.995s
user 0m33.229s
sys 0m6.382s
Não vou mencionar otimizar
is_prime
iterar para squrare_root de (n).Desconfio que a versão com paralelo, esteja gastando uma quantidade significativa de tempo iniciando processos. Portanto, divida-o em pedaços maiores. por exemplo, n/Number_of_cpus deve ser o mais rápido (se cada pedaço levar o mesmo tempo). Experimente alguns tamanhos de pedaços, veja o que acontece.
Você terá que adaptar seu script para diminuir e incrementar.
por exemplo, organize a execução paralela (se você tiver 5 núcleos).
GNU Parallel gasta 2-10 ms de sobrecarga por trabalho. Ele pode ser reduzido um pouco usando
-u
, mas isso significa que você pode obter saída de diferentes trabalhos misturados.O GNU Parallel não é ideal se seus trabalhos estiverem na faixa de ms e o tempo de execução for importante: a sobrecarga geralmente será muito grande.
Você pode distribuir a sobrecarga para vários núcleos executando vários GNU Parallels:
Você ainda paga a sobrecarga, mas agora pelo menos tem mais núcleos para pagar.
Uma maneira melhor seria mudar
isprime
para que seja necessário várias entradas e, portanto, demore mais para ser executado:Alterando o loop for principal:
ele corre um pouco mais rápido
do que sem
&
:ou apenas com chamadas em segundo plano
&
:Então, enquanto o e comercial levava você de 28s para 5s, eu fui de 5s para 3s.
Eu também tentei 2 com e comercial e 1 sem, mas isso já está ficando mais lento.
Desaceleração dramática (veja o ^C) se o e comercial estiver apenas na segunda chamada:
Isso parece um pouco confuso.
Usando os números primos encontrados apenas como divisores, você pode acelerar pelo fator 20:
Isto dá:
E a mesma coisa em perl:
(a primeira linha se parece com
*** 3
, entãowc
a saída é normal)Mas este algoritmo é mais difícil de paralelizar:
isprime()
tem que ter acesso à lista (crescente) de números primos (até sqrt).Talvez
factor
(um comando lutando contra a seção 6 :) seria útil como uma unidade funcional padrão. Então você pode alimentá-lo com diferentes "pedaços".O
sed
exclui linhas com mais de um espaço (ou seja, mais de um fator).Mas, novamente, é muito rápido para ser ajudado: