-
Notifications
You must be signed in to change notification settings - Fork 0
/
README
292 lines (225 loc) · 11.3 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
NAME
Parallel::MPI::Simple
SYNOPSIS
mpirun -np 2 perl script.pl
#!perl
use Parallel::MPI::Simple;
MPI_Init();
my $rank = MPI_Comm_rank(MPI_COMM_WORLD);
if ($rank == 1) {
my $msg = "Hello, I'm $rank";
MPI_Send($msg, 0, 123, MPI_COMM_WORLD);
}
else {
my $msg = MPI_Recv(1, 123, MPI_COMM_WORLD);
print "$rank received: '$msg'\n";
}
MPI_Finalise();
COMPILING AND RUNNING
Please view the README file in the module tarball if you are having
trouble compiling or running this module.
INTRODUCTION
Perl is not a strongly typed language, Perl does not enforce data
structures of a fixed size or dimensionality, Perl makes things easy.
Parallel processing solves problems faster and is commonly programmed
using a message passing paradigm. Traditional message passing systems
are designed for strongly typed languages like C or Fortran, there exist
implementations of these for Perl but they concentrate on perfectly
mimicing the standards forcing the poor Perl programmer to use strongly
typed data despite all his best instincts.
This module provides a non-compliant wrapper around the widely
implemented MPI libraries, allowing messages to consist of arbitarily
nested Perl data structures whose size is limited by available memory.
This hybrid approach should allow you to quickly write programs which
run anywhere which supports MPI (both Beowulf and traditional MPP
machines).
Message Passing and Multiprocessing
The message passing paradigm is simple and easy to use. Multiple
versions of the same program are run on multiple processors (or nodes).
Each running copy should call "MPI_Init" to announce that it is running.
It can then find out who it is by calling "MPI_Comm_rank" and who else
it can talk to by calling "MPI_Comm_size". Using this information to
decide what part it is to play in the ensuing computation, it the
exchanges messages, or parcels of data, with other nodes allowing all to
cooperate.
Once the computation is finished, the node calls "MPI_Finalize" and
exits cleanly, ready to run another day.
These processes are all copies of the *same* perl script and are invoked
using: "mpirun -np [number of nodes] perl script.pl" .
Remember you may need to start a daemon before mpirun will work, for
"mpich" this is often as easy as running: "mpd &".
Starting and Stopping a process
A process must formally enter and leave the MPI pool by calling these
functions.
MPI_Init
MPI_Init()
Initialises the message passing layer. This should be the first "MPI_*"
call made by the program and ideally one of the first things the program
does. After completing this call, all processes will be synchronised and
will become members of the "MPI_COMM_WORLD" communicator. It is an error
for programs invoked with "mpirun" to fail to call "MPI_Init" (not to
mention being a little silly).
MPI_Finalize
MPI_Finalize()
Shuts down the message passing layer. This should be called by every
participating process before exiting. No more "MPI_*" calls may be made
after this function has been called. It is an error for a program to
exit *without* calling this function.
Communicators
All processes are members of one or more *communicators*. These are like
channels over which messages are broadcast. Any operation involving more
than one process will take place in a communicator, operations involving
one communicator will not interfere with those in another.
On calling "MPI_Init" all nodes automatically join the "MPI_COMM_WORLD"
communicator. A communicator can be split into smaller subgroups using
the "MPI_Comm_split" function.
MPI_COMM_WORLD
$global_comm = MPI_COMM_WORLD;
Returns the global communicator shared by all processes launched at the
same time. Can be used as a "constant" where a communicator is required.
Most MPI applications can get by using only this communicator.
MPI_Comm_rank
$rank = MPI_Comm_rank($comm);
Returns the rank of the process within the communicator given by $comm.
Processes have ranks from 0..(size-1).
MPI_Comm_size
$size = MPI_Comm_size($comm);
Returns the number of processes in communicator $comm.
MPI_Comm_compare
$result = MPI_Comm_compare($comm1, $comm2);
Compares the two communicators $comm1 and $comm2. $result will be equal
to:
MPI_IDENT : communicators are identical
MPI_CONGRUENT: membership is same, ranks are equal
MPI_SIMILAR : membership is same, ranks not equal
MPI_UNEQUAL : at least one member of one is not in the other
MPI_Comm_dup
$newcomm = MPI_Comm_dup($comm);
Duplicates $comm but creates a new context for messages.
MPI_Comm_split
$newcomm = MPI_Comm_split($comm, $colour, $key);
Every process in $comm calls "MPI_Comm_split" at the same time. A new
set of communicators is produced, one for each distinct value of
$colour. All those processes which specified the same value of $colour
end up in the same comminicator and are ranked on the values of $key,
with their original ranks in $comm being used to settle ties.
If $colour is negative (or "MPI_UNDEFINED"), the process will not be
allocated to any of the new communicators and "undef" will be returned.
MPI_Comm_free
MPI_Comm_free($comm, [$comm2, ...] );
Frees the underlying object in communicator $comm, do not attempt to do
this to MPI_COMM_WORLD, wise to do this for any other comminicators that
you have created. If given a list of comminicators, will free all of
them, make sure there are no duplicates...
Communications operations
MPI_Barrier
MPI_Barrier($comm);
Waits for every process in $comm to call MPI_Barrier, once done, all
continue to execute. This causes synchronisation of processes. Be sure
that every process does call this, else your computation will hang.
MPI_Send
MPI_Send($scalar, $dest, $msg_tag, $comm);
This takes a scalar (which can be an anonymous reference to a more
complicated data structure) and sends it to process with rank $dest in
communicator $comm. The message also carries $msg_tag as an identfier,
allowing nodes to receive and send out of order. Completion of this call
does not imply anything about the progress of the receiving node.
MPI_Recv
$scalar = MPI_Recv($source, $msg_tag, $comm);
Receives a scalar from another process. $source and $msg_tag must both
match a message sent via MPI_Send (or one which will be sent in future)
to the same communicator given by $comm.
if ($rank == 0) {
MPI_Send([qw(This is a message)], 1, 0, MPI_COMM_WORLD);
}
elsif ($rank == 1) {
my $msg = MPI_Recv(1,0,MPI_COMM_WORLD);
print join(' ', @{ $msg } );
}
Will output "This is a message". Messages with the same source,
destination, tag and comminicator will be delivered in the order in
which they were sent. No other guarantees of timeliness or ordering can
be given. If needed, use "MPI_Barrier".
MPI_Bcast
$data = MPI_Bcast($scalar, $root, $comm);
This sends $scalar in process $root from the root process to every other
process in $comm, returning this scalar in every process. All non-root
processes should provide a dummy message (such as "undef"), this is a
bit ugly, but maintains a consistant interface between the other
communication operations. The scalar can be a complicated data
structure.
if ($rank == 0) { # send from 0
my $msg = [1,2,3, {3=>1, 5=>6} ];
MPI_Bcast( $msg, 0, MPI_COMM_WORLD);
}
else { # everything else receives, note dummy message
my $msg = MPI_Bcast(undef, 0, MPI_COMM_WORLD);
}
MPI_Gather
# if root:
@list = MPI_Gather($scalar, $root, $comm);
#otherwise
(nothing) = MPI_Gather($scalar, $root, $comm);
Sends $scalar from every process in $comm (each $scalar can be
different, root's data is also sent) to the root process which collects
them as a list of scalars, sorted by process rank order in $comm.
MPI_Scatter
$data = MPI_Scatter([N items of data], $root, $comm);
Sends list of scalars (anon array as 1st arg) from $root to all
processes in $comm, with process of rank N-1 receiving the Nth item in
the array. Very bad things might happen if number of elements in array
!= N. This does not call the C function at any time, so do not expect
any implicit synchronisation.
MPI_Allgather
@list = MPI_Allgather($scalar, $comm);
Every process receives an ordered list containing an element from every
other process. Again, this is implemented without a call to the C
function.
MPI_Alltoall
@list = MPI_Alltoall([ list of scalars ], $comm);
Simillar to Allgather, each process (with rank *rank*) ends up with a
list such that element *i* contains the data which started in element
*rank* of process *i*s data.
MPI_Reduce
$value = MPI_Reduce($input, \&operation, $comm);
Every process receives in $value the result of performing &operation
between every processes $input. If there are three processes in $comm,
then "$value = $input_0 op $input_1 op $input_2".
Operation should be a sub which takes two scalar values (the $input
above) and returns a single value. The operation it performs should be
commutative and associative, otherwise the result will be undefined.
For instance, to return the sum of some number held by each process,
perform:
$sum = MPI_Reduce($number, sub {$_[0] + $_[1]}, $comm);
To find which process holds the greatest value of some number:
($max, $mrank) = @{ MPI_Reduce([$number, $rank],
sub { $_[0]->[0] > $_[1]->[0] ? $_[0] : $_[1]}
, $comm) };
PHILOSOPHY
I have decided to loosely follow the MPI calling and naming conventions
but do not want to stick strictly to them in all cases. In the interests
of being simple, I have decided that all errors should result in the
death of the MPI process rather than huge amounts of error checking
being foisted onto the module's user.
Many of the MPI functions have not been implemented, some of this is
because I feel they would complicate the module (I chose to have a
single version of the Send command, for instance) but some of this is
due to my not having finished yet. I certainly do not expect to provide
process topologies or inter-communicators, I also do not expect to
provide anything in MPI-2 for some time.
ISSUES
This module has been tested on a variety of platforms. I have not been
able to get it running with the mpich MPI implementation in a clustered
environment.
In general, I expect that most programs using this module will make use
of little other than "MPI_Init", "MPI_Send", "MPI_Recv",
"MPI_COMM_WORLD", "MPI_Barrier", "MPI_Comm_size", "MPI_Comm_rank" and
"MPI_Finalize".
Please send bugs to github:
<https://github.com/quidity/p5-parallel-mpi-simple/issues>
AUTHOR
Alex Gough (alex@earth.li)
COPYRIGHT
This module is copyright (c) Alex Gough, 2001,2011.
You may use and redistribute this software under the Artistic License as
supplied with Perl.