in reply to Re^2: Refactoring: Better Variable Names For Better Understanding? [SOLVED]
in thread Refactoring: Better Variable Names For Better Understanding? [SOLVED]
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; }
|
|---|