NAME
MCE::Shared - MCE extension for sharing data between workers
VERSION
This document describes MCE::Shared version 1.699_008
SYNOPSIS
# OO construction
use MCE::Shared Sereal => 1;
my $ar = MCE::Shared->array( @list );
my $cv = MCE::Shared->condvar( 0 );
my $fh = MCE::Shared->handle( '>>', \*STDOUT );
my $ha = MCE::Shared->hash( @pairs );
my $db = MCE::Shared->minidb();
my $oh = MCE::Shared->ordhash( @pairs );
my $qu = MCE::Shared->queue( await => 1, fast => 0 );
my $va = MCE::Shared->scalar( $value );
my $nu = MCE::Shared->sequence( $begin, $end, $step, $fmt );
my $ob = MCE::Shared->share( $blessed_object );
# Tie construction
use MCE::Flow;
use MCE::Shared Sereal => 1;
use feature 'say';
tie my $var, 'MCE::Shared', 'initial value';
tie my @ary, 'MCE::Shared', qw(a list of values);
tie my %has, 'MCE::Shared', (key1 => 'value', key2 => 'value');
tie my $cnt, 'MCE::Shared', 0;
tie my @foo, 'MCE::Shared';
tie my %bar, 'MCE::Shared';
my $m1 = MCE::Mutex->new;
mce_flow {
max_workers => 4
},
sub {
my ($mce) = @_;
my ($pid, $wid) = (MCE->pid, MCE->wid);
## Locking is required when multiple workers update the same element.
## This requires 2 trips to the manager process (fetch and store).
$m1->synchronize( sub {
$cnt += 1;
});
## Locking is not necessary when updating unique elements.
$foo[ $wid - 1 ] = $pid;
$bar{ $pid } = $wid;
return;
};
say "scalar : $cnt";
say " array : $_" for (@foo);
say " hash : $_ => $bar{$_}" for (sort keys %bar);
-- Output
scalar : 4
array : 37847
array : 37848
array : 37849
array : 37850
hash : 37847 => 1
hash : 37848 => 2
hash : 37849 => 3
hash : 37850 => 4
DESCRIPTION
This module provides data sharing for MCE supporting threads and processes.
MCE::Shared
enables extra functionality on systems with IO::FDPass
. Without it, MCE::Shared is unable to send file descriptors to the shared-manager process for queue
, condvar
, and possibly handle
.
As of this writing, the IO::FDPass module is not a requirement for running MCE::Shared nor is the check made during installation. The reason is that IO::FDPass
is not possible on Cygwin and not sure about AIX.
The following is a suggestion for systems without IO::FDPass
. This restriction applies to queue
, condvar
, and handle
only.
use MCE::Shared;
# Construct shared queue(s) and condvar(s) first.
# These contain GLOB handles - freezing not allowed.
my $q1 = MCE::Shared->queue();
my $q2 = MCE::Shared->queue();
my $cv1 = MCE::Shared->condvar();
my $cv2 = MCE::Shared->condvar();
# Start the shared-manager manually.
MCE::Shared->start();
# The shared-manager process knows of STDOUT, STDERR, STDIN
my $fh1 = MCE::Shared->handle(">>", \*STDOUT); # ok
my $fh2 = MCE::Shared->handle("<", "/path/to/sequence.fasta"); # ok
my $h1 = MCE::Shared->hash();
Otherwise, sharing is immediate and not delayed with IO::FDPass
. It is not necessary to share queue
and condvar
first or worry about starting the shared-manager process.
use MCE::Shared;
my $h1 = MCE::Shared->hash(); # shares immediately
my $q1 = MCE::Shared->queue(); # IO::FDPass sends file descriptors
my $cv = MCE::Shared->condvar(); # IO::FDPass sends file descriptors
my $h2 = MCE::Shared->ordhash();
DATA SHARING
- array
- condvar
- handle
- hash
- minidb
- ordhash
- queue
- scalar
- sequence
-
array
,condvar
,handle
,hash
,minidb
,ordhash
,queue
,scalar
, andsequence
are sugar syntax for constructing a shared object.# long form use MCE::Shared; use MCE::Shared::Array; use MCE::Shared::Hash; my $ar = MCE::Shared->share( MCE::Shared::Array->new() ); my $ha = MCE::Shared->share( MCE::Shared::Hash->new() ); # short form use MCE::Shared; my $ar = MCE::Shared->array( @list ); my $cv = MCE::Shared->condvar( 0 ); my $fh = MCE::Shared->handle( '>>', \*STDOUT ); my $ha = MCE::Shared->hash( @pairs ); my $db = MCE::Shared->minidb(); my $oh = MCE::Shared->ordhash( @pairs ); my $qu = MCE::Shared->queue( await => 1, fast => 0 ); my $va = MCE::Shared->scalar( $value ); my $nu = MCE::Shared->sequence( $begin, $end, $step, $fmt );
- num_sequence
-
num_sequence
is an alias forsequence
.
OBJECT SHARING
-
This class method transfers the blessed-object to the shared-manager process and returns a
MCE::Shared::Object
containing theSHARED_ID
. The object must not contain anyGLOB
's orCODE_REF
's or the transfer will fail.Unlike
threads::shared
, objects are not deeply shared. The shared object is accessable only through the underlying OO interface.use MCE::Shared; use Hash::Ordered; my ($ho_shared, $ho_unshared); $ho_shared = MCE::Shared->share( Hash::Ordered->new() ); $ho_shared->push( @pairs ); # OO interface only $ho_shared->mset( @pairs ); $ho_unshared = $ho_shared->export(); # back to unshared $ho_unshared = $ho_shared->destroy(); # including destruction
The following provide long and short forms for constructing a shared array, hash, or scalar object.
use MCE::Shared; use MCE::Shared::Array; # Loading helper classes is not necessary use MCE::Shared::Hash; # when using the shorter form. use MCE::Shared::Scalar; my $a1 = MCE::Shared->share( MCE::Shared::Array->new( @list ) ); my $a3 = MCE::Shared->share( [ @list ] ); # sugar syntax my $a2 = MCE::Shared->array( @list ); my $h1 = MCE::Shared->share( MCE::Shared::Hash->new( @pairs ) ); my $h3 = MCE::Shared->share( { @pairs } ); # sugar syntax my $h2 = MCE::Shared->hash( @pairs ); my $s1 = MCE::Shared->share( MCE::Shared::Scalar->new( 20 ) ); my $s2 = MCE::Shared->share( \do{ my $o = 20 } ); my $s4 = MCE::Shared->scalar( 20 );
PDL SHARING
- pdl_byte
- pdl_short
- pdl_ushort
- pdl_long
- pdl_longlong
- pdl_float
- pdl_double
- pdl_ones
- pdl_sequence
- pdl_zeroes
- pdl_indx
- pdl
-
pdl_byte
,pdl_short
,pdl_ushort
,pdl_long
,pdl_longlong
,pdl_float
,pdl_double
,pdl_ones
,pdl_sequence
,pdl_zeroes
,pdl_indx
, andpdl
are sugar syntax for PDL construction take place under the shared-manager process.use PDL; use PDL::IO::Storable; # must load for freezing/thawing use MCE::Shared; # must load MCE::Shared after PDL # not efficient from memory copy/transfer and unnecessary destruction my $ob1 = MCE::Shared->share( zeroes( 256, 256 ) ); # efficient my $ob1 = MCE::Shared->zeroes( 256, 256 );
- ins_inplace
-
The
ins_inplace
method applies to shared PDL objects. It supports two forms for writing bits back into the PDL object residing under the shared-manager process.# --- action taken by the shared-manager process # ins_inplace( 2 args ): $this->slice( $arg1 ) .= $arg2; # ins_inplace( >2 args ): ins( inplace( $this ), $what, @coords ); # --- use case $o->ins_inplace( ":,$start:$stop", $result ); # 2 args $o->ins_inplace( $result, 0, $seq_n ); # >2 args
The MCE-Cookbook on Github provides a couple working PDL demonstrations for further reading.
COMMON API
- blessed
-
Returns the real
blessed
name, provided by the shared-manager process.use Scalar::Util qw(blessed); use MCE::Shared; use MCE::Shared::Ordhash; use Hash::Ordered; my $oh1 = MCE::Shared->share( MCE::Shared::Ordhash->new() ); my $oh2 = MCE::Shared->share( Hash::Ordered->new() ); print blessed($oh1), "\n"; # MCE::Shared::Object print blessed($oh2), "\n"; # MCE::Shared::Object print $oh1->blessed(), "\n"; # MCE::Shared::Ordhash print $oh2->blessed(), "\n"; # Hash::Ordered
- destroy
-
Exports optionally, but destroys the shared object entirely from the shared-manager process.
my $exported_ob = $shared_ob->destroy(); $shared_ob; # becomes undef
- export
- export ( keys )
-
Exports the shared object into a non-shared object. One must export when passing the shared object into any dump routine. Otherwise, the data
${ SHARED_ID }
is all one will see.use MCE::Shared; use MCE::Shared::Ordhash; sub _dump { require Data::Dumper unless $INC{'Data/Dumper.pm'}; no warnings 'once'; local $Data::Dumper::Varname = 'VAR'; local $Data::Dumper::Deepcopy = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Purity = 1; local $Data::Dumper::Sortkeys = 0; local $Data::Dumper::Terse = 0; print Data::Dumper::Dumper($_[0]) . "\n"; } # these do the same thing my $oh1 = MCE::Shared->share( MCE::Shared::Ordhash->new() ); my $oh2 = MCE::Shared->ordhash(); _dump($oh1); # ${ 1 } # SHARED_ID value _dump($oh2); # ${ 2 } _dump($oh1->export()); # actual structure and content _dump($oh2->export());
export
can optionally take a list of indices/keys for what to export. This applies to shared array, hash, and ordhash.use MCE::Shared; my $h1 = MCE::Shared->hash( # shared hash qw/ I Heard The Bluebirds Sing by Marty Robbins / # k v k v k v k v ); my $h2 = $h1->export( qw/ I The / ); # non-shared hash _dump($h2); __END__ $VAR1 = bless( { 'I' => 'Heard', 'The' => 'Bluebirds' }, 'MCE::Shared::Hash' );
- next
- rewind
- rewind ( "query string" )
- rewind ( begin, end, [ step, format ] )
-
next
andrewind
enable parallel iteration between workers for shared array, hash, ordhash, and sequence. Callrewind
after running to reset the pointer.use MCE::Hobo; use MCE::Shared; my $ob = MCE::Shared->array( 'a' .. 'j' ); sub parallel { my ($id) = @_; while (defined (my $item = $ob->next)) { print "$id: $item\n"; sleep 1; } } MCE::Hobo->new( \¶llel, $_ ) for 1 .. 3; # ... do other work ... $_->join() for MCE::Hobo->list(); -- Output 1: a 2: b 3: c 2: f 1: d 3: e 2: g 3: i 1: h 2: j
There are two forms for iterating through a shared hash or ordhash object. The
next
method is wantarray-aware providing key and value in list context and value only in scalar context.use MCE::Hobo; use MCE::Shared; my $ob = MCE::Shared->ordhash( map {( "key_$_" => "val_$_" )} "a" .. "j" ); sub iter1 { my ($id) = @_; while ( my ($key, $val) = $ob->next ) { print "$id: $key => $val\n"; sleep 1; } } sub iter2 { my ($id) = @_; while ( defined (my $val = $ob->next) ) { print "$id: $val\n"; sleep 1; } } MCE::Hobo->new(\&iter1, $_) for 1 .. 3; $_->join() for MCE::Hobo->list(); $ob->rewind(); MCE::Hobo->new(\&iter2, $_) for 1 .. 3; $_->join() for MCE::Hobo->list();
Although the shared-manager process iterates orderly, there is no guarantee for the amount of time required by workers. Thus, output may not be ordered.
-- Output 1: key_a => val_a 2: key_b => val_b 3: key_c => val_c 1: key_d => val_d 3: key_f => val_f 2: key_e => val_e 1: key_g => val_g 3: key_i => val_i 2: key_h => val_h 1: key_j => val_j 1: val_a 2: val_b 3: val_c 3: val_f 1: val_d 2: val_e 3: val_h 1: val_g 2: val_i 3: val_j
- store ( key, value )
-
Deep-sharing non-blessed structure(s) is possible with
store
only.store
, an alias toSTORE
, converts non-blessed deeply-structures to shared objects recursively.use MCE::Shared; my $h1 = MCE::Shared->hash(); my $h2 = MCE::Shared->hash(); # auto-shares deeply $h1->store( 'key', [ 0, 2, 5, { 'foo' => 'bar' } ] ); $h2->{key}[3]{foo} = 'baz'; # via auto-vivification my $v1 = $h1->get('key')->get(3)->get('foo'); # bar my $v2 = $h2->get('key')->get(3)->get('foo'); # baz my $v3 = $h2->{key}[3]{foo}; # baz
Each level in a deeply structure requires a separate trip to the shared-manager processs. There is a faster way if the app calls for just
HoH
and/orHoA
. The includedMCE::Shared::Minidb
module provides optimized methods for working withHoH
andHoA
structures.See MCE::Shared::Minidb.
SERVER API
- start
-
Starts the shared-manager process. This is done automatically.
MCE::Shared->start();
- stop
-
Stops the shared-manager process wiping all shared data content. This is not typically done by the user, but rather by
END
automatically when the script terminates.MCE::Shared->stop();
- init
-
This is called automatically by each MCE/Hobo worker immediately after being spawned. The effect is extra parallelism during inter-process communication. The optional ID (an integer) is modded in a round-robin fashion.
MCE::Shared->init( ID ); MCE::Shared->init();
INDEX
AUTHOR
Mario E. Roy, <marioeroy AT gmail DOT com>