in reply to Threads From Hell #3: Missing Some Basic Prerequisites [Solved]

Karl later mentioned a Mandelbrot. I searched the web for a parallel example and converted the code to Perl using MCE. Like the previous example, am using the sequence generator for parallelizing the outer loop.

It takes 21.327 seconds to run serially (commented out) and 5.795 seconds with MCE from a Core i7 at 2.6 GHz (Haswell).

use strict; use warnings; use MCE::Flow Sereal => 1; my ($m, $n, $count_max) = (500, 500, 2000); my ($x_max, $x_min, $y_max, $y_min) = ( 1.25, -2.25, 1.75, -1.75 ); #--------------------------------------------------------------------# # Perl version of Mandelbrot_OpenMP by John Burkardt. # http://people.sc.fsu.edu/~jburkardt/c_src/mandelbrot_openmp/ # mandelbrot_openmp.html # # Carry out the iteration for each pixel, determining count. sub mandelbrot { my ($r, $g, $b, $m1, $m2) = @_; my ($x, $x1, $x2, $y, $y1, $y2); my ($c, %count, $i, $j, $k); for $i ( $m1 .. $m2 ) { for $j ( 0 .. $n - 1 ) { $x = ( ( $j - 1 ) * $x_max + ( $m - $j ) * $x_min ) / ( $m - 1 ); $y = ( ( $i - 1 ) * $y_max + ( $n - $i ) * $y_min ) / ( $n - 1 ); $count{$i}{$j} = 0; $x1 = $x; $y1 = $y; for $k ( 1 .. $count_max ) { $x2 = ( $x1 * $x1 ) - ( $y1 * $y1 ) + $x; $y2 = 2 * $x1 * $y1 + $y; if ( $x2 < -2.0 || 2.0 < $x2 || $y2 < -2.0 || 2.0 < $y2 ) { $count{$i}{$j} = $k; last; } $x1 = $x2; $y1 = $y2; } if ( ( $count{$i}{$j} % 2 ) == 1 ) { $r->{$i}{$j} = 255; $g->{$i}{$j} = 255; $b->{$i}{$j} = 255; } else { $c = int( 255.0 * sqrt(sqrt(sqrt($count{$i}{$j} / $count_max))) ); $r->{$i}{$j} = $g->{$i}{$j} = 3 * $c / 5; $b->{$i}{$j} = $c; } } } return; } #--------------------------------------------------------------------# # Return the smaller of two numbers. sub min { $_[0] < $_[1] ? $_[0] : $_[1]; } # Write image to an ASCII PPM file. sub write_to_ppm { my ($r, $g, $b, $ofile) = @_; my ($FH, $i, $j, $jlo, $jhi); open $FH, '>', $ofile or die "cannot open file ($ofile): $!\n"; print $FH "P3\n"; print $FH "$n $m\n"; print $FH "255\n"; for ( $i = 0; $i < $m; $i++ ) { for ( $jlo = 0; $jlo < $n; $jlo += 4 ) { $jhi = min( $jlo + 4, $n ); for ( $j = $jlo; $j < $jhi; $j++ ) { printf $FH " %d %d %d", $r->{$i}{$j}, $g->{$i}{$j}, $b->{$i}{$j} } print $FH "\n"; } } close $FH; } #--------------------------------------------------------------------# my ($r, $g, $b) = ({}, {}, {}); # Running serially. # mandelbrot($r, $g, $b, 0, $m - 1); # Run parallel. # # The bounds_only option applies to sequence of numbers # which means to compute the begin and end boundaries only, # not the numbers in between. Thus, workers receive 2 # numbers in @{ $chunk_ref }. MCE::Flow::init( chunk_size => 5, max_workers => 'auto', bounds_only => 1, gather => sub { my ($rr, $gg, $bb, $m1, $m2) = @_; for my $i ($m1 .. $m2) { for my $j (0 .. $n - 1) { $r->{$i}{$j} = $rr->{$i}{$j}; $g->{$i}{$j} = $gg->{$i}{$j}; $b->{$i}{$j} = $bb->{$i}{$j}; } } } ); # Same as mce_flow_s sub { ... }, 0, $m - 1; MCE::Flow::run_seq( sub { my ($mce, $chunk_ref, $chunk_id) = @_; my ($rr, $gg, $bb, $m1, $m2) = ({}, {}, {}, @{ $chunk_ref }); mandelbrot ($rr, $gg, $bb, $m1, $m2); MCE->gather($rr, $gg, $bb, $m1, $m2); }, 0, $m - 1 ); # Shutdown MCE workers. MCE::Flow::finish; # Write image to an ASCII PPM file. write_to_ppm($r, $g, $b, 'mandelbrot.ppm');