Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Re^2: Refactoring: Better Variable Names For Better Understanding? [SOLVED]

by marioroy (Prior)
on Jun 06, 2015 at 01:31 UTC ( [id://1129277]=note: print w/replies, xml ) Need Help??


in reply to Re: Refactoring: Better Variable Names For Better Understanding? [SOLVED]
in thread Refactoring: Better Variable Names For Better Understanding? [SOLVED]

The GD module is nearly 2x faster than Imager. Not sure why. Anyway, here is the parallel version using GD. Workers are spawned prior to the image being created by the manager process.

use strict; use warnings; use GD; use MCE; # based on original http://www.alfrog.com/mandel.html # karlgoethebier: code refactor # marioroy: parallelization my $width = 1280; my $height = 1024; my $iterations = 20; my @palette; my $image; my $mce = MCE->new( use_threads => 0, # MCE defaults to threads on Windows max_workers => 'auto', chunk_size => 8, gather => sub { my ( $x, $y, $color ); while ( ($x, $y, $color) = splice(@_, 0, 3) ) { $image->setPixel( $x, $y, $palette[ $color ] ); } }, user_func => sub { my ( $mce, $chunk_ref, $chunk_id ) = @_; my ( $re_c, $im_c, $re_z, $im_z, $color, $temp ); my ( @set_data ); for my $x ( $chunk_ref->[0] .. $chunk_ref->[1] ) { for my $y ( 0 .. $height - 1 ) { $re_c = ( $x - 3 * $width / 4 ) / ( $width / 3 ); $im_c = ( $y - $height / 2 ) / ( $width / 3 ); $re_z = $im_z = $color = 0; while ( 1 ) { $temp = $re_z; $re_z = $re_z * $re_z - $im_z * $im_z + $re_c; $im_z = 2 * $temp * $im_z + $im_c; ++$color; last if $re_z * $re_z + $im_z * $im_z > 4; if ( $color == $iterations ) { $color = 0; last; } } push @set_data, $x, $y, $color; } } MCE->gather( @set_data ); } ); # spawn MCE workers $mce->spawn; # init image and color palette $image = new GD::Image( $width, $height ); push @palette, $image->colorAllocate( 0, 0, 0 ); # black for ( 1 .. $iterations ) { my ( $r, $g, $b ) = map { int rand 255 } 1 .. 3; push @palette, $image->colorAllocate( $r, $g, $b ); } # compute mandelbrot $mce->process({ bounds_only => 1, sequence => [ 0, $width - 1 ] }); $mce->shutdown; # save image open my $fh, '>mandelbrot.png' || exit 1; binmode $fh; print $fh $image->png; close $fh;
  • Comment on Re^2: Refactoring: Better Variable Names For Better Understanding? [SOLVED]
  • Download Code

Replies are listed 'Best First'.
Re^3: Refactoring: Better Variable Names For Better Understanding? [SOLVED]
by marioroy (Prior) on Jun 06, 2015 at 06:40 UTC

    Update: Corrected a couple 1 off errors.

    Update: Compute upper half only. Makes a copy afterwards, flips vertically, and copies into bottom half. There is a one time delay from Inline::C compiling the C code. Remember to run again.

    Update: Draw lines when possible.

    Update: Reduced IPC.

    Finally, providing a demonstration combining MCE::Flow + GD + Inline::C.

    use strict; use warnings; use MCE::Flow Sereal => 1; use MCE::Queue Fast => 1; use GD; # based on original code at http://www.alfrog.com/mandel.html # karlgoethebier: initial code refactor, thanks perlmonks # marioroy: parallelization via MCE::Flow (2 user tasks) + Inline::C # process upper half only (reduces work for writer by 50%) # draw lines when possible (reduces work for writer by 56x) # reduced # of times to enqueue/dequeue to lower IPC # also see tips by BrowserUk at: # http://www.perlmonks.org/?node_id=1128885 use Inline 'C' => Config => CCFLAGSEX => '-O2'; use Inline 'C' => <<'END_C'; int width, height, iterations, middle; void c_init( int _width, int _height, int _iterations ) { width = _width; height = _height; iterations = _iterations; middle = _height / 2; } AV * c_mandel( int x1, int x2 ) { AV *ret = newAV(); SV *line_size; double re_c, im_c, re_z, im_z, temp; int x, y, color, last_color; for (x = x1; x <= x2; x++) { for (y = 0; y <= middle; y++) { re_c = (double) ( x - 3 * width / 4 ) / ( width / 3 ); im_c = (double) ( y - height / 2 ) / ( width / 3 ); re_z = im_z = (double) 0.0; color = 0; while ( 1 ) { temp = (double) re_z; re_z = (double) re_z * re_z - im_z * im_z + re_c; im_z = (double) 2 * temp * im_z + im_c; ++color; if ( re_z * re_z + im_z * im_z > 4.0 ) break; if ( color == iterations ) { color = 0; break; } } if ( y && color == last_color ) { // increment line size, writer draws a line when > 0 line_size = *av_fetch( ret, -1, TRUE ); sv_setiv( line_size, SvIV( line_size ) + 1 ); } else { av_push( ret, newSViv( x ) ); av_push( ret, newSViv( y ) ); av_push( ret, newSViv( color ) ); av_push( ret, newSViv( 0 ) ); last_color = color; } } } return sv_2mortal( ret ); } END_C # fasten your seat belt, enjoy the ride :) my $width = 1280 * 1; my $height = 1024 * 1; my $iterations = 20; my $Q = MCE::Queue->new(); my $num_mandels = 3; my $num_writers = 1; # must be 1 # init C and MCE c_init( $width, $height, $iterations ); MCE::Flow::init { bounds_only => 1, chunk_size => 16, max_workers => [ $num_mandels, $num_writers ], task_name => [ 'tsk_mandel', 'tsk_writer' ], user_begin => sub { my ( $mce, $task_id, $task_name ) = @_; $mce->{ret} = [] if $task_name eq 'tsk_mandel'; }, user_end => sub { my ( $mce, $task_id, $task_name ) = @_; if ( $task_name eq 'tsk_mandel' ) { $Q->enqueue( MCE->freeze( $mce->{ret} ) ) if @{ $mce->{ret} } +; $Q->enqueue( undef ); } } }; # compute mandelbrot (user tasks; sequence begin, end ) MCE::Flow::run_seq( \&mandel, \&writer, 0, $width - 1 ); MCE::Flow::finish; # for MCE providers sub mandel { my ( $mce, $chunk_ref, $chunk_id ) = @_; push @{ $mce->{ret} }, @{ c_mandel( @{ $chunk_ref } ) }; if ( @{ $mce->{ret} } > 12000 ) { $Q->enqueue( MCE->freeze( $mce->{ret} ) ); $mce->{ret} = []; } } # for MCE consumer sub writer { my ( $mce ) = @_; # init image and color palette my $image = new GD::Image( $width, $height ); my @palette = $image->colorAllocate( 0, 0, 0 ); # black for ( 1 .. $iterations ) { my ( $r, $g, $b ) = map { int rand 255 } 1 .. 3; push @palette, $image->colorAllocate( $r, $g, $b ); } # process draw requests while (1) { my $ret = $Q->dequeue; if (!defined $ret) { last unless --$num_mandels; next; } my $data = MCE->thaw( $ret ); my $size = @{ $data }; for ( my $i = 0; $i < $size; $i += 4 ) { if ( $data->[$i+3] ) { # draw line if line_size > 0 $image->line( $data->[$i], # x1 $data->[$i+1], # y1 $data->[$i], # x2 $data->[$i+1] + $data->[$i+3], # y2 $palette[ $data->[$i+2] ] # color ); } else { # otherwise, set pixel $image->setPixel( $data->[$i], # x $data->[$i+1], # y $palette[ $data->[$i+2] ] # color ); } } } # copy upper half, flip vertically, then copy into bottom half my $middle = int( $height / 2 ); my $temp = new GD::Image( $width, $middle ); $temp->copy( $image, 0, 0, 0, 0, $width, $middle ); $temp->flipVertical(); $image->copy( $temp, 0, $middle + 1, 0, 0, $width, $middle ); # save image open my $fh, '>mandelbrot.png' || return 1; binmode $fh; print $fh $image->png; close $fh; }
Re^3: Refactoring: Better Variable Names For Better Understanding? [SOLVED]
by marioroy (Prior) on Jun 06, 2015 at 03:05 UTC

    Update: Corrected a couple 1 off errors.

    Update: Computes the upper half only. Makes a copy afterwards, flips vertically, and copies into bottom half.

    Update: Draw lines when possible.

    The following is constructed using MCE::Flow with 2 tasks (many providers and one writer). Spawning workers early is not necessary for this demonstration. I went with GD due to running faster than Imager. However, one can easily change to Imager inside the writer function.

    use strict; use warnings; use MCE::Flow Sereal => 1; use MCE::Queue Fast => 1; use GD; # based on original code at http://www.alfrog.com/mandel.html # karlgoethebier: initial code refactor, thanks perlmonks # marioroy: parallelization via MCE::Flow (2 user tasks) # process upper half only (reduces work for writer by 50%) # draw lines when possible (reduces work for writer by 56x) # also see tips by BrowserUk at: # http://www.perlmonks.org/?node_id=1128885 # fasten your seat belt, enjoy the ride :) my $width = 1280 * 1; my $height = 1024 * 1; my $iterations = 20; my $Q = MCE::Queue->new(); my $num_mandels = 3; my $num_writers = 1; # must be 1 # init MCE MCE::Flow::init { bounds_only => 1, chunk_size => 16, max_workers => [ $num_mandels, $num_writers ], task_name => [ 'tsk_mandel', 'tsk_writer' ], user_end => sub { my ( $mce, $task_id, $task_name ) = @_; $Q->enqueue(undef) if ( $task_name eq 'tsk_mandel' ); } }; # compute mandelbrot (user tasks; sequence begin, end ) MCE::Flow::run_seq( \&mandel, \&writer, 0, $width - 1 ); MCE::Flow::finish; # for MCE providers sub mandel { my ( $mce, $chunk_ref, $chunk_id ) = @_; my ( $re_c, $im_c, $re_z, $im_z, $color, $last_color, $temp ); my ( @ret, $middle ); $middle = int( $height / 2 ); for my $x ( $chunk_ref->[0] .. $chunk_ref->[1] ) { for my $y ( 0 .. $middle ) { $re_c = ( $x - 3 * $width / 4 ) / ( $width / 3 ); $im_c = ( $y - $height / 2 ) / ( $width / 3 ); $re_z = $im_z = $color = 0; while ( 1 ) { $temp = $re_z; $re_z = $re_z * $re_z - $im_z * $im_z + $re_c; $im_z = 2 * $temp * $im_z + $im_c; ++$color; last if $re_z * $re_z + $im_z * $im_z > 4; if ( $color == $iterations ) { $color = 0; last; } } if ( $y && $color == $last_color ) { # increment line size, writer draws a line when > 0 $ret[-1]++; } else { push @ret, $x, $y, $color, 0; $last_color = $color; } } } # freezing here to prevent double freezing/thawing $Q->enqueue( MCE->freeze( \@ret ) ); } # for MCE consumer sub writer { my ( $mce ) = @_; # init image and color palette my $image = new GD::Image( $width, $height ); my @palette = $image->colorAllocate( 0, 0, 0 ); # black for ( 1 .. $iterations ) { my ( $r, $g, $b ) = map { int rand 255 } 1 .. 3; push @palette, $image->colorAllocate( $r, $g, $b ); } # process draw requests while (1) { my $ret = $Q->dequeue; if (!defined $ret) { last unless --$num_mandels; next; } my $data = MCE->thaw( $ret ); my $size = @{ $data }; for ( my $i = 0; $i < $size; $i += 4 ) { if ( $data->[$i+3] ) { # draw line if line_size > 0 $image->line( $data->[$i], # x1 $data->[$i+1], # y1 $data->[$i], # x2 $data->[$i+1] + $data->[$i+3], # y2 $palette[ $data->[$i+2] ] # color ); } else { # otherwise, set pixel $image->setPixel( $data->[$i], # x $data->[$i+1], # y $palette[ $data->[$i+2] ] # color ); } } } # copy upper half, flip vertically, then copy into bottom half my $middle = int( $height / 2 ); my $temp = new GD::Image( $width, $middle ); $temp->copy( $image, 0, 0, 0, 0, $width, $middle ); $temp->flipVertical(); $image->copy( $temp, 0, $middle + 1, 0, 0, $width, $middle ); # save image open my $fh, '>mandelbrot.png' || return 1; binmode $fh; print $fh $image->png; close $fh; }

    The examples demonstrate using the Core MCE API and MCE::Flow.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1129277]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2024-04-25 23:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found