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');