AvgLdAvg.pl 0100700 0000332 0000012 00000003047 07504537413 011002 0 ustar wheel #!/usr/bin/perl
# example PerlDSM application; finds the average load average among all
# nodes
use DSMClnt;
package main;
# shared variables:
$SumAvgs; # sum of all load averages
# global non-shared variables:
$NumNodes; # total number of application nodes
$MyNode; # number of this node
$SvrSkt; # server socket ID for this node
# check in with server
($SvrSkt,$NumNodes,$MyNode) = DSMClnt::DSMCheckIn();
print "total of ", $NumNodes, " nodes, of which I am number ",$MyNode, "\n";
# tie shared variables
# arguments (the variable name is given first but is not considered an
# argument to tie()): variable name; 'DSMClnt'; variable type (0 for
# scalar, 1 for array, 2 for hash); single-quoted variable name (if
# array or hash, then base name with $ sign only); subscript (-1 if
# scalar); server socket
tie $SumAvgs,'DSMClnt',0,'$SumAvgs',-1;
tie $LOCK[0],'DSMClnt',1,'$LOCK',0;
tie $BARR,'DSMClnt',0,'$BARR',-1;
# initialize sum to 0 if I am node 0
if ($MyNode == 0) {
$SumAvgs = 0.0;
}
# barrier
$Dummy = $BARR; # left-hand side irrelevant
# get load average at this node
system 'w > tmpout';
open TMP,"tmpout";
$Line =
Professor Norm Matloff (Please mail any questions to
Norm Matloff.) Contents: However, I In the parallel processing community it is commonly felt that the
shared-memory paradigm produces clearer programs than does message
passing, and a number of software distributed shared memory (DSM)
packages have been developed, such as Treadmarks. DSM gives programmers the
illusion of shared memory in programming for networks of workstations. I have developed such a package for Perl, called PerlDSM. The goal here
is again clarity (and not necessarily speed). A number of Perl applications
are distributed in nature, i.e. work via having multiple copies of
the same Perl script work on different network nodes, cooperating with each
other via messages passed through the network. I aim to make development
and maintenance of such scripts clearer and easier, by providing a
shared-memory framework. PerlDSM also has value in settings in which only a single machine is used,
if one is considering writing a script which uses Perl threads. Arguably a
PerlDSM version would be clearer, easier to write and easier to debug than a
threads version. Due to the fact that speed is a secondary consideration in PerlDSM, the
current version has only a single home for each variable (the server), and
uses Sequential Consistency. Go to the PerlDSM home
page. There really isn't anything to install. Just choose a directory for the
files DSMSvr.pl and DSMClnt.pm. We assume a rudimentary familiarity with shared-memory programming. The best way to learn PerlDSM is to read the sample programs AvgLdAvg.pl
and Primes.pl (read AvgLdAvg.pl first). In reading them, look for the
following general structure: There must be a call to tie() for the lock and barrier variables $LOCK
and $BARR. In the current version, there is only one of each. The lack of
multiple lock variables means that the same one must be used at different
points in the application code, guarding different application variables.
This may cause some loss of efficiency, but as mentioned before our primary
emphasis is not on speed anyway. Shared variables are created using calls to Perl's tie() function.
See the comments in AvgLdAvg.pl for details.
PerlDSM Documentation
Dept. of Computer Science
University of California at Davis
Davis, CA 95616
What is PerlDSM?
Obtaining PerlDSM:
Installation:
Writing a PerlDSM Application:
call to DSMClnt::DSMCheckIn(), to initialize the system
calls to tie(), one for each shared variable, to "declare" sharing
application code, including lock and barrier operations
You must start the server before the application. On the node at which you want to run the server, type
perl DSMSvr.pl portnumber numnodes debugflag
where portnumber is at least 1024, numnodes is the number of copies being run of the application script, and debugflag is "d" if you wish to debug the server.
Then at each node where you wish to run the application, start the application, by typing
perl appname.pl hostip portnumber app_args
where appname.pl is the application, hostip is the name of the machine on which the server is running, and app_args is your list of application-specific arguments.
Of course, if either the server or application script is not in your current directory, use the -I option of perl.
Note that references are meaningless for shared variables.
In the current version of PerlDSM, shared arrays and hashes must have only scalar elements, e.g. an array of integers. Also, since the current version implements arrays and hashes as tied scalars, operations such as push and shift cannot be used with shared arrays and hashes.
DSMClnt.pm 0100600 0000332 0000012 00000006504 07504476753 010625 0 ustar wheel use IO::Socket; # note: not enough to have "use Socket" package DSMClnt; # class variables $NumNodes; # number of application nodes $SvrSkt; # socket from this application node to the server # subroutine to set up socket to server sub DSMCheckIn { # turn buffering off $| = 1; my $SrvrIP = $ARGV[0]; # name of the server my $Port = $ARGV[1]; # open socket; note that $SvrSkt will contain the socket ID for # the application # node which is currently executing this code, # i.e. it will be # different for each node $SvrSkt = new IO::Socket::INET (PeerAddr=>$SrvrIP, PeerPort=>$Port, Proto=>'tcp'); print $SvrSkt "checkin 0 0\n"; my $Response = <$SvrSkt>; ($NumNodes,$MyNode) = split(" ",$Response); return ($SvrSkt,$NumNodes,$MyNode); } sub DSMExit { print $SvrSkt "close 0 0\n"; close $SvrSkt; exit; } # each shared variable in a PerlDSM application program is represented # internally as an object of this class (note that there will be a # different object at each node, since the application program is # running at each node), and tied to an ordinary Perl variable used by # the application program; arguments to "tie" are (the tied variable is # listed first but is not considered an argument): # the class name (in single quotes), 'DSMClnt' # variable type -- 0 for scalar, 1 for array, 2 for hash # the name of the tied/shared variable in single quotes; if array or # hash element, give only the base, e.g. '$x' instead of $x[i]' # subscript, if array or hash element, or -1 for scalar sub TIESCALAR { # get arguments my $Class = shift; my $VarType = shift; my $VarName = shift; my $Subscript = shift; # the instance variables for this class will be: # variable type (0, 1, 2) # variable name/name base # subscript (-1 for scalar) my $R = {VrTyp=>$VarType, VrNm=>$VarName, SbScrpt=>$Subscript}; # notify server about this variable, including subscript if any my $FullName = GetFullName($VarType,$VarName,$Subscript); print $SvrSkt "create ", $FullName, " 0\n"; my $Response = <$SvrSkt>; # ACK # bless the reference to type, name and subscript with the package name bless $R, $Class; return $R; } sub FETCH { # get reference to this object from the argument my $R = shift; my $VarType = $R->{VrTyp}; my $VarName = $R->{VrNm}; my $Subscript = $R->{SbScrpt}; my $FullName = GetFullName($VarType,$VarName,$Subscript); print $SvrSkt "read ", $FullName, " 0\n"; my $Response = <$SvrSkt>; return $Response; } sub STORE { # get reference to this object from the argument my $R = shift; my $VarType = $R->{VrTyp}; my $VarName = $R->{VrNm}; my $Subscript = $R->{SbScrpt}; # set the data to the second argument, dereferencing the pointer my $D = shift; my $FullName = GetFullName($VarType,$VarName,$Subscript); print $SvrSkt "write ", $FullName, " ", $D, "\n"; my $Response = <$SvrSkt>; } sub GetFullName { my $Type = shift; my $Name = shift; my $Sb = shift; if ($Type == 0) { return $Name; } if ($Type == 1) { $Name .= "["; $Name .= $Sb; $Name .= "]"; return $Name; } $Name .= "{"; $Name .= $Sb; $Name .= "}"; return $Name; } 1; # needed due to quirk in Perl DSMSvr.pl 0100600 0000332 0000012 00000011521 07504537315 010461 0 ustar wheel #!/usr/bin/perl # server for PerlDSM use IO::Socket; use IO::Select; # globals: $ListenSocket; # listening socket, to accept new connections $Sock; # individual socket $Slct; # object used in the "select" operation $NumNodes; # number of application nodes $NumNodesLeft; # number of application nodes still connected $NextNode = 0; # ID number to be assigned to the next node to check in $Debug; # true if we are in debug mode %SharedVars; # hash for the shared variables and their values @LockQueue = (); # array of queues (i.e. 2-dim. array) of # sockets waiting for locks @CondQueue = (); # array of queues of # sockets waiting for condition variables @Barrier = (0,0); # 2-element array for 2-phase barrier $BarrParity = 0; # records which element of @Barrier we're using now @BarrQueue; # queue of sockets waiting for the barrier # set up network access InitNet(); # get command-line arguments $NumNodes = $ARGV[1]; $NumNodesLeft = $NumNodes; $Debug = ($ARGV[2] eq "d"); # accept connections from all the application nodes foreach $I (1..$NumNodes) { $Sock = $ListenSocket->accept(); if ($Debug) { print "accepted socket $$Sock\n"; } $Slct->add($Sock); } # in each iteration of the loop, find all the sockets that have data to # read, place those sockets in the array @Sockets, and read from them while (@Sockets = $Slct->can_read()) { # read from each socket that is ready for $Sock (@Sockets) { $Line = <$Sock>; if ($Debug) { print "from socket $$Sock: ", $Line; } ($Op,$VarName,$Value) = split(" ", $Line); if ($Op eq "create") { Create($VarName); } elsif ($Op eq "read") { Read($VarName); } elsif ($Op eq "write") { Write($VarName,$Value) } elsif ($Op eq "close") { Close(); } elsif ($Op eq "checkin") { CheckIn(); } } } sub InitNet { $| = 1; # turn buffering off my $Port = $ARGV[0]; # get port number $ListenSocket = IO::Socket::INET->new(Proto=>'tcp', LocalPort=>$Port, Listen=>1, Reuse=>1) || die $!; # set up object for "select" operation $Slct = IO::Select->new($ListenSocket); } sub CheckIn { my $TheirNode = $NextNode++; print $Sock $NumNodes, " ", $TheirNode, "\n"; } sub Create { $VarName = shift; if (defined($SharedVars{$VarName})) { print $Sock "established previously, OK\n"; if ($Debug) { print "$VarName established previously, OK\n"; } } else { $SharedVars{$VarName} = -1; print $Sock "OK\n"; if (substr($VarName,0,5) eq '$LOCK') { push(@LockQueue,[]); } elsif (substr($VarName,0,5) eq '$COND') { push(@CondQueue,[]); } if ($Debug) { print "$VarName created\n"; } } } sub Read { $VarName = shift; if (substr($VarName,0,5) eq '$LOCK') { Lock($VarName); } elsif (substr($VarName,0,5) eq '$COND') { StartWait($VarName); } elsif ($VarName eq '$BARR') { DoBarrier(); } else { print $Sock $SharedVars{$VarName}, "\n"; } } sub Write { $VarName = shift; if (substr($VarName,0,5) eq '$LOCK') { Unlock($VarName); } elsif (substr($VarName,0,5) eq '$COND') { ReleaseWait($VarName); } else { $Value = shift; $SharedVars{$VarName} = $Value; print $Sock "OK\n"; } } sub Lock { $VarName = shift; if ($SharedVars{$VarName} == 0 || $SharedVars{$VarName} == -1) { $SharedVars{$VarName} = 1; print $Sock "go ahead\n"; } else { $Indx = GetIndex($VarName); push(@{@LockQueue[$Indx]},$Sock); if ($Debug) { print "pushing socket $$Sock onto lock queue\n"; } } } sub Unlock() { $VarName = shift; $SharedVars{$VarName} = 0; print $Sock "unlock completed\n"; $Indx = GetIndex($VarName); if (scalar(@{@LockQueue[$Indx]}) > 0) { $WaitingSock = shift(@{@LockQueue[$Indx]}); $SharedVars{$VarName} = 1; if ($Debug) { print "shifting socket $$WaitingSock out of lock queue\n"; } print $WaitingSock "lock free; go ahead\n"; } } sub DoBarrier { $Barrier[$BarrParity]++; if ($Barrier[$BarrParity] == $NumNodes) { $Barrier[$BarrParity] = 0; $BarrParity = 1 - $BarrParity; foreach $I (1..$NumNodes-1) { $WaitingSock = shift(@BarrQueue); print $WaitingSock "barrier reached by all; go ahead\n"; } print $Sock "barrier reached by all; go ahead\n"; } else { push(@BarrQueue,$Sock); } } sub Close { $Slct->remove($Sock); close $Sock; $NumNodesLeft--; if ($NumNodesLeft == 0) { exit; } } sub GetIndex { $VrNm = shift; $Left = index($VrNm,"["); $Right = index($VrNm,"]"); return substr($VrNm,$Left+1,$Right-$Left-1); } Primes.pl 0100700 0000332 0000012 00000004217 07504537463 010613 0 ustar wheel #!/usr/bin/perl # example PerlDSM application; finds the number of primes from 2 to N # note: not intended to be efficient at all; just an example of how to # use PerlDSM with array variables use DSMClnt; package main; # shared variables: $NextI; # next value of $I to cross out multiples of @Prime; # $Prime[I] = 0 if I composite, else = 1 # global non-shared variables: $NumNodes; # total number of application nodes $MyNode; # number of this node $SvrSkt; # server socket ID for this node $N; # range of primes to be checked $Lim; # sqrt(N) $IHaveChecked = 0; # number of $I values this node has checked # check in with server ($SvrSkt,$NumNodes,$MyNode) = DSMClnt::DSMCheckIn(); print "total of ", $NumNodes, " nodes, of which I am number ",$MyNode, "\n"; $N = $ARGV[2]; $Lim = int(sqrt($N)); # tie shared variables tie $NextI,'DSMClnt',0,'$NextI',-1; foreach $I (2..$N) { tie $Prime[$I],'DSMClnt',1,'$Prime',$I; } tie $LOCK[0],'DSMClnt',1,'$LOCK',-1; tie $BARR,'DSMClnt',0,'$BARR',-1; # initialize Prime array; assume each $I prime until proven otherwise if ($MyNode == 0) { $NextI = 2; foreach $I (2..$N) { $Prime[$I] = 1; } } # barrier (wait for node 0 to finish initialization) $Dummy = $BARR; # left-hand side irrelevant while (1) { # get next $I value, and atomically increment $Dummy = $LOCK[0]; $I = $NextI++; $LOCK[0] = 0; $IHaveChecked++; # don't cross out multiples of $I if $I > sqrt($N) last if ($I > $Lim); # don't cross out multiples of $I if $I known to be composite if ($Prime[$I] == 1) { # cross out all multiples of $I $UpperBd = int($N/$I); if ($UpperBd*$I < $N) { $UpperBd++; } foreach $K (2..$UpperBd) { $J = $K * $I; if ($J <= $N) { $Prime[$J] = 0; } } } } # wait for everyone to finish, then write answer if I am node 0 $Dummy = $BARR; print "I checked ", $IHaveChecked, " values of I\n"; if ($MyNode == 0) { $Count = 0; foreach $I (2..$N) { if ($Prime[$I] == 1) { $Count++; } } print "number of primes = ", $Count, "\n"; } $Dummy = $BARR; DSMClnt::DSMExit();