Con los modernos ordenadores que tenemos con procesadores de 2, 4, 8 o más núcleos podemos pensar en paralelizar procesos fácilmente en Perl. ¿Qué significa esto? Básicamente que el tiempo que tardará en correr nuestro programa se reducirá proporcionalmente al número de núcleos, si a cada núcleo le mandamos tareas diferentes a realizar (threads)
Pero hay que ser cuidadoso, no todos los programas son adecuados para ser paralelizados. Un programa paralelizable en threads requiere que sus procesos puedan ser independientes y una vez finalizados podamos recoger y unificar sus resultados sin problemas. Además paralelizar significa multiplicar las copias en memoria de las variables, por lo cual sólo podremos paralelizar procesos que no consuman grandes cantidades de memoria o el proceso de copia ralentizará los cálculos.
El código mostrado a continuación realiza una suma de números de tres formas diferentes:
Resultado:
Código:
Pero hay que ser cuidadoso, no todos los programas son adecuados para ser paralelizados. Un programa paralelizable en threads requiere que sus procesos puedan ser independientes y una vez finalizados podamos recoger y unificar sus resultados sin problemas. Además paralelizar significa multiplicar las copias en memoria de las variables, por lo cual sólo podremos paralelizar procesos que no consuman grandes cantidades de memoria o el proceso de copia ralentizará los cálculos.
El código mostrado a continuación realiza una suma de números de tres formas diferentes:
- De forma tradicional, con 3 procesos de suma en serie.
- Usando 3 threads que calculan simultáneamente.
- Usando un bucle con 3 threads que calculan simultáneamente.
Resultado:
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
Código:
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";
Unas aclaraciones...
ResponderEliminarLos "hilos de ejecución" (threads) sí comparten el espacio de memoria y los recursos. Cuando un hilo modifica algo, todos los demás pueden notar ese cambio (ver definición de "Hilo de ejecución" en Wikipedia).
Otra cosa son los threads en Perl.
De hecho, no deberían ni llamarse threads, si no más bien "procesos" independientes que comparten el código. El ejemplo que siempre se da es que, por defecto, los threads de Perl no comparten la memoria (el contenido de las variables) a no ser que lo indiquemos explícitamente.
Más información en perlthrtut.