Fuente: https://gist.github.com/manwar
Ideas y código para problemas de genómica de plantas, biología computacional y estructural
Hola,
hace un tiempo explicaba aquí cómo montar grid-engine en Debian para gestionar trabajos paralelos en un servidor multicore. Como he tenido algunos problemas he probado slurm, que está activamente desarrollado y documentado.
En concreto lo he hecho sobre un servidor Debian 11. Si vienes de otros sistemas, como LSF, tendrás que convertir tus comandos con tablas como ésta. Los pasos que he seguido los he adaptado de aquí:
sudo apt install slurmd slurmctld -y sudo chmod 777 /etc/slurm # creo fichero de configuración /etc/slurm/slurm.conf, # tendrás que adaptar CPUs= y RealMemory= al final , # en mi caso he definido "16 nodos" sudo cat << EOF > /etc/slurm/slurm.conf # See the slurm.conf man page for more information. # ClusterName=localcluster SlurmctldHost=localhost MpiDefault=none ProctrackType=proctrack/linuxproc ReturnToService=2 SlurmctldPidFile=/var/run/slurmctld.pid SlurmctldPort=6817 SlurmdPidFile=/var/run/slurmd.pid SlurmdPort=6818 SlurmdSpoolDir=/var/lib/slurm/slurmd SlurmUser=slurm StateSaveLocation=/var/lib/slurm/slurmctld SwitchType=switch/none TaskPlugin=task/none # # TIMERS InactiveLimit=0 KillWait=30 MinJobAge=300 SlurmctldTimeout=120 SlurmdTimeout=300 Waittime=0 # SCHEDULING SchedulerType=sched/backfill SelectType=select/cons_tres SelectTypeParameters=CR_Core # #AccountingStoragePort= AccountingStorageType=accounting_storage/none JobCompType=jobcomp/none JobAcctGatherFrequency=30 JobAcctGatherType=jobacct_gather/none SlurmctldDebug=info SlurmctldLogFile=/var/log/slurm/slurmctld.log SlurmdDebug=info SlurmdLogFile=/var/log/slurm/slurmd.log # # COMPUTE NODES NodeName=localhost CPUs=16 RealMemory=200 State=UNKNOWN PartitionName=LocalQ Nodes=ALL Default=YES MaxTime=INFINITE State=UP EOF # iniciamos el gestor sudo chmod 755 /etc/slurm sudo systemctl start slurmctld sudo systemctl start slurmd # probamos el cluster sinfo sbatch --wrap=date cat slurm-1.out
PD Para ver las propiedades de tu nuevo "cluster" puedes probar:
scontrol show node
Hasta pronto,
Bruno
Hola,
BLAST+ es una de las pocas herramientas esenciales para cualquier persona que haga biología computacional, todos la hemos usado alguna vez. Simplificando, creo que el secreto es que hace una sola cosa (alinear localmente secuencias) y la hace bien. Eso, y que BLAST evoluciona, y de vez en cuando introduce novedades interesantes, que podemos repasar en las notas de publicación periódicas disponibles en https://www.ncbi.nlm.nih.gov/books/NBK131777
La versión 2.12.0 (28 de junio de 2021) incluye un nuevo modo de paralelización por lotes de secuencias input que beneficia entre otros a los algoritmos clásicos BLASTN, BLASTP y BLASTX. Los detalles puedes verlos en https://www.ncbi.nlm.nih.gov/books/NBK571452 , aquí comparto un ejemplo de invocación, que requiere el argumento -mt_mode 1:
$ blastp –db swissprot -query BIGFASTA.fsa –out test.out -num_threads 32 -mt_mode 1
La siguiente figura muestra la ganancia en tiempo de cálculo en función del número de cores y hebras empleado:
Espero que os sea útil, hasta pronto,
Bruno
$ mkdir /path/to/salida/
$ cd entrada
$ ls -1 *fasta | parallel --gnu -j 20 ~/soft/clustal-omega-1.2.1/src/clustalo \
--threads=1 -i {} -o /path/to/salida/{} :::
| cores (-j) | time(real) | time(user) | time(sys) | | 1 | 4m34.440s | 4m5.180s | 0m2.168s | | 10 | 0m29.358s | 3m57.768s | 0m2.400s | | 20 | 0m23.248s | 5m6.204s | 0m3.364s |
@SEQ_ID GATTTGGGGTTCAAAGCAGTA... + !''*((((***+))%%%++)(...
quedaría así:
@SEQ_ID GATTTGGGGTTCAAAGCAGTA... + !''*((((***+))%%%++)(...
$ export LC_ALL=POSIX
$ ls -lh /tmp/unsortedXXpJ6CaB -rw-------. 1 576M Jun 5 10:15 /tmp/unsortedXXpJ6CaB
$ time sort -k 1,1 -u -S 500M /tmp/unsortedXXpJ6CaB > /tmp/unsortedXXpJ6CaB.S real 0m7.628s user 0m5.143s sys 0m2.373s
$ time cat /tmp/unsortedXXpJ6CaB | parallel -N 100000 --pipe --files sort -k 1,1 |
parallel -Xj1 sort -k 1,1 -u -m {} ';' rm {} > /tmp/unsortedXXpJ6CaB.P real 0m15.451s user 0m9.919s sys 0m8.371s
$ diff /tmp/unsortedXXpJ6CaB.S /tmp/unsortedXXpJ6CaB.P
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Importar módulos
import time
import os, sys
import subprocess
from pprint import pprint
maximum_process_number = 4 # Número máximo de procesos simultáneos
checking_processes_interval = 2 # (segundos) Tiempo de espera para checkear si han terminado los procesos
data_to_process = range(10) # Datos a procesar, como ejemplo una lista de números del 0 al 9
# El proceso a ejecutar (como ejemplo se muestran los datos de la version de Blastp)
# Se puede cambiar por cualquier otro programa y comando que no sea Blast
def run_process(processes) :
print "Running process"
processes.append(subprocess.Popen("blastp -version", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
# Programa principal
if __name__ == '__main__':
# Definir variables
count_processes = 0
finished_processes = []
processes = []
results = {}
# Procesar múltiples datos
for i in data_to_process:
# Chequear si los procesos han terminado (cuando hay tantos procesos como el máximo permitido)
while count_processes == maximum_process_number:
for p in processes:
p.wait()
if p.returncode == 0:
results[p.pid] = "\n".join(p.stdout)
processes.remove(p)
count_processes -= 1
print "Process finished"
else:
results[p.pid] = "\n".join(p.stdout)
processes.remove(p)
count_processes -= 1
print "Process failed"
# Si no han terminado, esperar el tiempo establecido
if count_processes == maximum_process_number:
print "Waiting for finished processes"
time.sleep(checking_processes_interval)
# Añadir procesos a la cola de ejecución
run_process(processes)
count_processes += 1
print "Process started", i, count_processes
# Chequear si los procesos han terminado los últimos procesos
while count_processes != 0:
for p in processes:
p.wait()
if p.returncode == 0:
results[p.pid] = "\n".join(p.stdout)
processes.remove(p)
count_processes -= 1
print "Last processes finishing"
else:
results[p.pid] = "\n".join(p.stdout)
processes.remove(p)
count_processes -= 1
print "Process failed"
if count_processes == maximum_process_number:
print count_processes,maximum_process_number
print "Waiting for last finished processes"
time.sleep(checking_processes_interval)
# Imprimir resultados
print "Output from the processes:"
pprint(results)
Total = 300000000
Time taken by the traditional way (3 serial processes) was 26 wallclock secs (26.48 usr 0.00 sys + 0.00 cusr 0.00 csys = 26.48 CPU) seconds
Total = 300000000
Time taken by 3 parallel threads was 9 wallclock secs (25.94 usr 0.00 sys + 0.00 cusr 0.00 csys = 25.94 CPU) seconds
Total = 300000000
Time taken by a loop of 10000 times 3 threads was 104 wallclock secs (103.41 usr 1.01 sys + 0.00 cusr 0.00 csys = 104.42 CPU) seconds
use threads;
use Benchmark;
use Config;
$Config{useithreads} or die('Recompile Perl with threads to run this program.');
# Subroutine to test multithreading
sub calc {
my ($number) = @_;
my $i=0;
while ($i<$number) {
$i++;
}
return $i;
}
# Define a number to calculate
my $number = 100000000;
# Start timer
my $start = new Benchmark;
# Run subroutines in the traditional way
my $res1 = calc($number);
my $res2 = calc($number);
my $res3 = calc($number);
my $total = $res1 + $res2 + $res3;
# End timer
my $end = new Benchmark;
# Calculate difference of times
my $diff = timediff($end, $start);
# Print final result
print "\nTotal = $total\n";
# Report benchmark
print "Time taken by the traditional way (3 serial processes) was ", timestr($diff, 'all'), " seconds\n\n";
# Start timer
$start = new Benchmark;
# Create multiple threads running each one the subroutine
my $thr1 = threads->create(\&calc, $number);
my $thr2 = threads->create(\&calc, $number);
my $thr3 = threads->create(\&calc, $number);
# Check subroutines ending and retrieve results
$res1 = $thr1->join();
$res2 = $thr2->join();
$res3 = $thr3->join();
$total = $res1 + $res2 + $res3;
# End timer
$end = new Benchmark;
# Calculate difference of times
$diff = timediff($end, $start);
# Print final result
print "Total = $total\n";
# Report benchmark
print "Time taken by 3 parallel threads was ", timestr($diff, 'all'), " seconds\n\n";
# Start timer
$start = new Benchmark;
# Divide the process
$total = 0;
my $divide = 10000;
$number = $number / $divide;
# Create multiple threads running each one the subroutine
for (my $i=0; $i<$divide; $i++){
$thr1 = threads->create(\&calc, $number);
$thr2 = threads->create(\&calc, $number);
$thr3 = threads->create(\&calc, $number);
# Check subroutines ending and retrieve results
$res1 = $thr1->join();
$res2 = $thr2->join();
$res3 = $thr3->join();
$total += $res1 + $res2 + $res3;
}
# End timer
$end = new Benchmark;
# Calculate difference of times
$diff = timediff($end, $start);
# Print final result
print "Total = $total\n";
# Report benchmark
print "Time taken by a loop of 10000 times 3 threads was ", timestr($diff, 'all'), " seconds\n\n";