File: joshi.html

package info (click to toggle)
lg-issue65 2-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 3,780 kB
  • ctags: 230
  • sloc: sh: 201; perl: 133; makefile: 34
file content (1273 lines) | stat: -rw-r--r-- 58,970 bytes parent folder | download
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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
<!--startcut  ==============================================-->
<!-- *** BEGIN HTML header *** -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML><HEAD>
<title>Parallel Processing on Linux with PVM and MPI LG #65</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
ALINK="#FF0000">
<!-- *** END HTML header *** -->

<CENTER>
<A HREF="http://www.linuxgazette.com/">
<H1><IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.png" 
	WIDTH="600" HEIGHT="124" border="0"></H1></A> 

<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="jenkins.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue65/joshi.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom"  ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="lilly.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom"  ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
<P>
</CENTER>

<!--endcut ============================================================-->

<H4 ALIGN="center">
"Linux Gazette...<I>making Linux just a little more fun!</I>"
</H4>

<P> <HR> <P> 
<!--===================================================================-->

<center>
<H1><font color="maroon">Parallel Processing on Linux with PVM and MPI</font></H1>
<H4>By <a href="mailto:jurahul@hotmail.com">Rahul U. Joshi</a></H4>
</center>
<P> <HR> <P>  

<!-- END header -->




<EM>This article aims to provide an introduction to PVM and MPI, two widely used
software systems for implementing parallel message passing programs. They enable us to use a group of heterogeneous UNIX/LINUX computers connected by a network as a single machine for solving a large problem.</EM>

<H2><A NAME="s1">1. Introduction to Parallel Processing</A></H2>

<P>Parallel processing is a form of computing in which a number of activities are
carried out concurrently so that the effective time required to solve the
problem is reduced. In the previous days, parallel processing was used for such
thing as large scale simulations (e.g. molecular simulations, simulation of the
explosion of an atomic bomb etc), solving large number crunching and data
processing problems (e.g. compiling the census data) etc. However, as the cost
of hardware is decreasing rapidly, parallel processing is being uses more and
more in routine tasks. Multiple processor servers have been in existence for
a long time. Parallel processing is also used in your own PC too. For example,
a graphics processor working along with the main processor to render graphics
on your monitor is also a form of parallel processing.
<P>
<P>However, apart from the hardware facilities for parallel processing, some
software support too is required so that we can run the programs in parallel
and coordinate their execution. Such a coordination is necessary due to the
dependencies of the parallel programs on one other. This will become 
clearer when we work through an example. The most widely used method to achieve
such coordination is <EM> message passing </EM> in which the programs coordinate
their execution and in general communicate with each other by passing <EM>message's</EM> to one other. So, for example, a program may tell another program,
``Ok! Here is the intermediate result you need to proceed.'' If all this sounds
too abstract, lets proceed with a very simple example.
<H2><A NAME="s2">2. A Very Simple Problem</A></H2>

<P>In this section, we will consider a very simple problem and consider how we
can use parallel processing to speed up its execution. The problem is to find
the sum of a list of integers stored in an array. Let us say that there are
100 integers stored in an array say <CODE>items</CODE>. Now, how do we parallelize
this program? That is, we must first find out a way in which this problem can
be solved by a number of programs working concurrently. Many a times, due to
<EM> data dependencies</EM>, parallelization becomes a difficult problem. For
example, if you want to evaluate <EM>(a + b) * c</EM>, which involves two
operations, we cannot do them concurrently, the addition must be done before
the multiplication. Fortunately, for the problem that we have chosen, 
parallelization is easy. Suppose that 4 program or processors will be working
simultaneously to solve the addition problem. Then the simplest strategy would
be to break the array <CODE>items</CODE> into 4 parts and have each program process
one part. Thus the parallelization of the problem is as follows:
<OL>
<LI> Four programs say P0, P1, P2 and P3 will solve the problem.</LI>
<LI> P0 will find the sum of array elements <CODE>items[0]</CODE> to 
<CODE>items[24]</CODE>. Similarly, P1 will find the sum of <CODE>items[25]</CODE>
to <CODE>items[49]</CODE>, P2 <CODE>items[50]</CODE> to <CODE>items[74]</CODE> and
P3 <CODE>items[75]</CODE> to <CODE>items[99]</CODE>.</LI>
<LI> After these programs have executed, there must be some other program to
find the sum of the 4 results obtained and give the final answer. Also,
the elements of the array <CODE>items</CODE> are not known to the programs
P0 to P3 and hence some program must tell these programs the values
of the elements. Thus, apart from P0 to P3, we will require one more program
that distributes data, collects results and coordinates execution.
We call such a program as <EM>master</EM> and the programs P0 to P3 as
<EM>slaves</EM> and this organization as the <EM>master - slave paradigm</EM>.</LI>
</OL>
<P>With this organization in mind, let us write the algorithms for the master
and the slave programs.

<HR NOSHADE>
<PRE>
/* Algorithm for the master program */
initialize the array `items'.

/* send data to the slaves */
for i = 0 to 3
    Send items[25*i] to items[25*(i+1)-1] to slave Pi
end for

/* collect the results from the slaves */
for i = 0 to 3
    Receive the result from slave Pi in result[i]
end for

/* calculate the final result */
sum = 0
for i = 0 to 3
    sum = sum + result[i]
end for

print sum
</PRE>
<HR NOSHADE>
The algorithm for the slave can be written as follows.
<HR NOSHADE>
<PRE>
/* Algorithm for the slave program */

Receive 25 elements from the master in some array say `items'

/* calculate intermediate result */
sum = 0
for i = 0 to 24
    sum = sum + items[i]
end for

send `sum' as the intermediate result to the master
</PRE>
<HR NOSHADE>

<H2><A NAME="s3">3. Implementing with PVM</A></H2>

<P>Now that the basic algorithm has been designed, let us now consider how we
can implement it. What hardware shall we run this program on? Clearly, very
few of us have access to special machines designed to run parallel programs.
However, no special hardware requirements are there in order to implement this
program. A single computer or a group of interconnected computers will do,
thanks to PVM, a software system that enables us to use interconnected computers
for parallel program execution. PVM stands for Parallel Virtual Machine. It
enables you to create number of programs or processes that run concurrently on same or different machines
and provided functions with which you can pass messages among the processes for
coordination. Even if you have a single computer, PVM will work on it, although
there will be no ``real'' parallel processing as such. However, for learning
purpose, that should be fine. Later on I will describe how to do ``real''
parallel processing using the PVM.
<P>In order to use the PVM system, you need to install the PVM software on your
Linux system. In case you are using Red Hat Linux, then the RPM package for
PVM is included on the CD, so that you can install it as you normally install
other packages. Assuming that you have installed PVM system on your machine,
create the following directories(s) in your home directory: 
<CODE>~/pvm3/bin/LINUX/</CODE>. Why ? Because PVM requires that some of the 
executables you create be copied in this directory. Once you have done this,
your setup is ready. Test this by giving the command <CODE>pvm</CODE> on the
prompt. This will start the <EM>PVM Console</EM> from which you can give
commands to the PVM system and query status information. If everything is set
OK, you will see the <CODE>pvm&gt;</CODE> prompt. Here enter the command <CODE>conf</CODE>.
The output should look something like this.
<PRE>
pvm&gt; conf
conf
1 host, 1 data format
                    HOST     DTID     ARCH   SPEED       DSIG
               joshicomp    40000    LINUX    1000 0x00408841
</PRE>
<P>What does this mean? The PVM System allows you to consider a group of 
interconnected LINUX system to be viewed as a ``virtual'' computer having much
higher computing capacity than the individual machines. Thus, PVM will
distribute the processes among a number of computers. However, by default, PVM
considers that only the host that you are working on is to be included in the
PVM machine, i.e. all processes you create will be scheduled to run on the
same host. The <CODE>conf</CODE> command shows what hosts or nodes are in the
PVM. Currently, there is only one. Later on, we will see how to add more hosts.
Presently, exit the PVM console by giving the command <CODE>halt</CODE>
<P>
<P>
<H2>3.1 A Demonstration Program</H2>

<P>Now that you are ensured that the PVM system has been properly installed,
let us see how to write the programs. Programs for the PVM system can be
written in both FORTRAN and C. We will be using the C language. To use the
PVM system, you include some calls to the PVM functions in your C program
along with the other statements and link the PVM library with your programs.
To get you started with PVM, let us write a simple program in which there
will be a master and a slave. The master will send the slave some string,
which the slave will convert to upper case and send back to the master. The
master and the slave programs are given as follows. To compile the programs,
give the command <CODE>make -f makefile.demo</CODE>. 

<P> <A HREF="misc/joshi/Pvmmpi.tgz">[Click here for a tar file containing the
program listings.]</A>

<HR NOSHADE>
<PRE>
      1 /* -------------------------------------------------------------------- *
      2  * master_pvm.c                                                         *
      3  *                                                                      *
      4  * This is the master program for the simple PVM demonstration.         *
      5  * -------------------------------------------------------------------- */
      6 #include &lt;stdio.h&gt;
      7 #include &lt;stdlib.h&gt;
      8 #include &lt;pvm3.h&gt;           /* declares PVM constants and functions */
      9 #include &lt;string.h&gt;
        
     10 int main()
     11 {
     12     int mytid;              /* our task ID          */
     13     int slave_tid;          /* task ID of the slave */
     14     int result;
     15     char message[] = "hello pvm";
     16     
     17     /* enroll ourselves into the PVM system and get our ID */
     18     mytid = pvm_mytid();
        
     19     /* spawn the slave */
     20     result = pvm_spawn("slave_pvm", (char**)0, PvmTaskDefault, 
     21                         "", 1, &amp;slave_tid);
        
     22     /* check if the slave was spawned successfully          */
     23     if(result != 1)
     24     {
     25         fprintf(stderr, "Error: Cannot spawn slave.\n");
        
     26         /* clean up and exit from the PVM system            */
     27         pvm_exit();
     28         exit(EXIT_FAILURE);
     29     }
        
     30     /* initialize the data buffer to send data to slave     */
     31     pvm_initsend(PvmDataDefault);
        
     32     /* ``pack'' the string into the data buffer             */
     33     pvm_pkstr(message);
        
     34     /* send the string to the slave with a message tag of 0 */
     35     pvm_send(slave_tid, 0);
        
     36     /* wait and receive the result string from the slave    */
     37     pvm_recv(slave_tid, 0);
        
     38     
     39     /* ``unpack'' the result from the slave                 */
     40     pvm_upkstr(message);
        
     41     /* show the result from the slave                       */
     42     printf("Data from the slave : %s\n", message);
        
     43     /* clean up and exit from the PVM system                */
     44     pvm_exit();
     45     
     46     exit(EXIT_SUCCESS);
     47 } /* end main() */
        
     48 /* end master_pvm.c */
</PRE>
<HR NOSHADE>
<PRE>
      1 /* -------------------------------------------------------------------- *
      2  * slave_pvm.c                                                          *
      3  *                                                                      *
      4  * This is the slave program for the simple PVM demonstration           *
      5  * -------------------------------------------------------------------- */
      6 #include &lt;stdio.h&gt;
      7 #include &lt;ctype.h&gt;
      8 #include &lt;stdlib.h&gt;
      9 #include &lt;pvm3.h&gt;
        
     10 #define MSG_LEN     20
     11 void convert_to_upper(char*);
        
     12 int main()
     13 {
     14     int mytid;
     15     int parent_tid;
     16     char message[MSG_LEN];
        
     17     /* enroll ourselves into the PVM system         */
     18     mytid = pvm_mytid();
        
     19     /* get the task ID of the master                */
     20     parent_tid = pvm_parent();
        
     21     /* receive the original string from master      */
     22     pvm_recv(parent_tid, 0);
     23     pvm_upkstr(message);
        
     24     /* convert the string to upper case             */
     25     convert_to_upper(message);
        
     26     /* send the converted string to the master      */
     27     pvm_initsend(PvmDataDefault);
        
     28     pvm_pkstr(message);
     29     pvm_send(parent_tid, 0);
        
     30     /* clean up and exit from the PVM system        */
     31     pvm_exit();
     32     
     33     exit(EXIT_SUCCESS);
     34 } /* end main() */
        
     35 /* function to convert the given string into upper case */
     36 void convert_to_upper(char* str)
     37 {
     38     while(*str != '\0')
     39     {
     40         *str = toupper(*str);
     41         str++;
     42     }
     43 } /* end convert_to_upper() */
        
     44 /* end slave_pvm.c */
</PRE>
<HR NOSHADE>
<PRE>
      1 # Make file for the demo PVM program
        
      2 .SILENT :
      3 # paths fro PVM include files and libraries
      4 INCDIR=-I/usr/share/pvm3/include
      5 LIBDIR=-L/usr/share/pvm3/lib/LINUX
        
      6 # link the PVM library
      7 LIBS=-lpvm3
      8 CFLAGS=-Wall
      9 CC=gcc
     10 TARGET=all
        
     11 # this is where the PVM executables go
     12 PVM_HOME=$(HOME)/pvm3/bin/LINUX
        
     13 all : $(PVM_HOME)/master_pvm $(PVM_HOME)/slave_pvm
        
     14 $(PVM_HOME)/master_pvm : master_pvm.c
     15     $(CC) -o $(PVM_HOME)/master_pvm master_pvm.c $(CFLAGS) $(LIBS) \
     16           $(INCDIR) $(LIBDIR)
        
     17 $(PVM_HOME)/slave_pvm : slave_pvm.c
     18     $(CC) -o $(PVM_HOME)/slave_pvm slave_pvm.c $(CFLAGS) $(LIBS) \
     19           $(INCDIR) $(LIBDIR)
</PRE>
<HR NOSHADE>
<P>
<P>Once your programs have been
compiled, you must copy them into the <CODE>~/pvm3/bin/LINUX</CODE> directory. 
(The makefile does it by default). Now to run the programs, you must first
start the PVM system. To do this give the command <CODE>pvm</CODE> to start the
PVM Console. Now at the <CODE>pvm&gt;</CODE> prompt, type <CODE>quit</CODE>. The output
will be as follows:
<PRE>
pvm&gt; quit
quit

Console: exit handler called
pvmd still running.
</PRE>

Notice the last line, indicating that the PVM daemon (<CODE>pvmd</CODE>) is still
running. To run the PVM programs, you need to run the PVM daemon which manages
the exchange of messages and that what we are doing here. Once the PVM daemon
is running, you can run the program by the following commands:
<PRE>
[rahul@joshicomp rahul]$ cd ~/pvm3/bin/LINUX/
[rahul@joshicomp LINUX]$ ./master_pvm
Data from the slave : HELLO PVM
[rahul@joshicomp LINUX]$
</PRE>
<P>
<P>Notice that the string is now in upper case as expected.
<P>
<H2>3.2 Explanation of the program</H2>

<P>In this section, we will see exactly how this program works. First of all
to use PVM function, you need to include a header file <CODE>pvm3.h</CODE> in
your programs. This is done in line 8 of <CODE>master_pvm.c</CODE> and in
line 9  of <CODE>slave_pvm.c</CODE>. Also when compiling the programs, you need
to link it with the PVM library. This is done by specifying the <CODE>-lpvm3</CODE>
option to the compiler, as done in line 7 of <CODE>makefile.demo</CODE>. Also, you
need to specify to the compiler the paths of header and library files, as
is done on lines 4 and 5 of the makefile.
<P>
<P>In the master program, we first get the <EM>task ID</EM> of the master by 
calling the PVM function <CODE>pvm_mytid()</CODE>. The
PVM system assigns each process a unique 32 bit integer called as its <EM>task
ID</EM> in the same way as Linux assigns each process a process ID. The task
ID helps us identify the process with which we need to communicate. However,
the master does not uses its task ID (stored in <CODE>mytid</CODE>) ever. Our
intention here is just to call the function <CODE>pvm_mytid()</CODE>. This
function enrolls the process into the PVM system and generates a unique task
ID for the process. If we do not explicitly enroll the process, PVM 
automatically enrolls our process on the first call to any PVM function. Next
we use <CODE>pvm_spawn()</CODE> to create the slave process. The first parameter,
<CODE>"slave_pvm"</CODE> is the name of the executable for the slave. The second
parameter is the arguments that you wish to the pass to the slaves (similar to 
<CODE>argv</CODE> in normal C). Since we do not want to send any arguments, we
set this value to 0. The third parameter is a flag with which we can control
how and where PVM starts the slave. Since we have only a single machine, we
set this flag to <CODE>PvmTaskDefault</CODE>, specifying PVM to use default
criteria while spawning the slave. The fourth parameter is the name of the
host or the architecture on which we wish to run the program and here it is
kept empty. It is used to specify the host or the architecture when the flag
is other than <CODE>PvmTaskDefault</CODE>.The fifth parameter specifies the number 
of slaves to spawn and the sixth parameter is a pointer to an array in which 
the IDs of the slaves will be returned. This function returns the number of 
slaves actually spawned which we check for correctness.
<P>
<P>A message in PVM consists of basically two parts, the data and a <EM>tag</EM>
that identifies the type of the message. The tag helps us distinguish between
different messages. For example, in the addition example, which we are going
to implement, suppose that you are expecting that each slave will send to the 
master an integer which is the sum of the elements it added. It is also
quite possible that some slave may encounter some error and may want to send
the master an integer which indicates the error code. How does the master
distinguish whether an integer it received from the slave is an intermediate
result or an error code? This is where tags come in picture. You may assign
the message for intermediate result a tag say <CODE>MSG_RESULT</CODE> which you
will <CODE>#define</CODE> in some header file and a tag say <CODE>MSG_ERROR</CODE> for
the message indicating error. The master will then look at the message tags
to decide whether the message contains intermediate result or error.
<P>
<P>To send a message, you first need to ``initialize'' the send buffer. This is
done by calling the <CODE>pvm_initsend()</CODE> function. The parameter to
this function specifies the ``encoding'' scheme to be used. When we want to
exchange data between machines with different architectures (like say between
a Pentium machine and a SPARC Workstation) then we need to encode the data at
the sending end and decode at the receiving end so that data is properly
delivered. The parameter to <CODE>pvm_initsend()</CODE> specifies the encoding
scheme to be used. The value <CODE>PvmDataDefault</CODE> specifies an encoding
scheme which enables data to be safely exchanged between heterogeneous
architectures. Once the buffer has been initialized, we need to put data into
the buffer and encode it. In our case, the data is a string, so we use the
function <CODE>pvm_pkstr()</CODE> to ``pack'' i.e. encode and put the data into
the buffer. If we had to send an integer, there is a different function
<CODE>pvm_pkint()</CODE>. Similarly, there are functions for other data types.
Once the data is packed, we call <CODE>pvm_send()</CODE> to send the message. 
The first argument is the ID of the process to which the message is to be sent
and the second argument is the message tag. Since there is only one type of 
message here, we set the tag to 0. 
<P>
<P>Once the data is sent to the slave, the slave will process it and return it
to the master as we shall see. So we now call <CODE>pvm_recv()</CODE> to receive
the data from the slave. Again, the parameters are the task ID from which
the message is expected and the tag of the expected message. If the desired
message has not yet been sent, this function waits and does not return. Thus,
in effect, the master is now waiting for the slave to process the data. Once
the message arrives, the data is still in the receive buffer. It needs to be
``unpacked'' i.e decoded to get the original message. This decoding is done
by the <CODE>pvm_upkstr()</CODE> function. We then display the processes string.
<P>
<P>Before the PVM program exits, it must tell the PVM system that it is leaving
the PVM system so that resources occupied by the process can be released. This
is done by calling the <CODE>pvm_exit()</CODE> function. After that, the master
exits.
<P>
<P>The slave program is easy to understand. First it finds the task ID of the
master (which is also its parent as the master spawned the slave) by calling
the function <CODE>pvm_parent()</CODE>. It then receives the message string from
the master, converts it to uppercase and send the resulting string to the
master.
<H2>3.3 The Addition Program</H2>

<P>Now that you know some basics of a PVM program, let us implement the addition
algorithm we developed using PVM. There will be one master and 4 slaves. The
master will first spawn 4 slaves and send each one their part of data. The 
slaves will add the data and send the results to the master. Thus, two
types of messages are exchanged, one when the master send data to slaves, for
which we will use the tag <CODE>MSG_DATA</CODE> and the other when the slaves
send results to master, for which we will use the tag <CODE>MSG_RESULT</CODE>.
The rest is simple. The master and the slave programs are given below.
<HR NOSHADE>
<PRE>
      1 /* -------------------------------------------------------------------- *
      2  * common.h                                                             *
      3  *                                                                      *
      4  * This header file defines some common constants.                      *
      5  * -------------------------------------------------------------------- */
      6 #ifndef COMMON_H
      7 #define COMMON_H
    
      8 #define NUM_SLAVES      4                   /* number of slaves     */
      9 #define SIZE            100                 /* size of total data   */
     10 #define DATA_SIZE       (SIZE/NUM_SLAVES)   /* size for each slave  */
    
     11 #endif
     12 /* end common.h */
</PRE>
<HR NOSHADE>
<PRE>
      1 /* -------------------------------------------------------------------- *
      2  * tags.h                                                               *
      3  *                                                                      *
      4  * This header file defines the tags that will be used for messages.    *
      5  * -------------------------------------------------------------------- */
      6 #ifndef TAGS_H
      7 #define TAGS_H
    
      8 #define MSG_DATA            101     /* data from master to slave    */
      9 #define MSG_RESULT          102     /* result from slave to master  */
    
     10 #endif
    
     11 /* end tags.h */
</PRE>
<HR NOSHADE>
<PRE>
  1 /* -------------------------------------------------------------------- *
  2  * master_add.c                                                         *
  3  *                                                                      *
  4  * Master program for adding the elements of an array by using PVM      *
  5  * -------------------------------------------------------------------- */
  6 #include &lt;stdio.h&gt;
  7 #include &lt;stdlib.h&gt;
  8 #include &lt;pvm3.h&gt;           /* PVM constants and declarations   */
  9 #include "tags.h"           /* tags for messages                */
 10 #include "common.h"         /* common constants                 */
    
 11 int get_slave_no(int*, int);
    
 12 int main()
 13 {
 14     int mytid;
 15     int slaves[NUM_SLAVES]; /* array to store the task IDs of slaves    */
 16     int items[SIZE];        /* data to be processes                     */
 17     int result, i, sum;
 18     int results[NUM_SLAVES];    /* results from the slaves              */
    
 19     /* enroll into the PVM system   */
 20     mytid = pvm_mytid();
    
 21     /* initialize the array `items' */
 22     for(i = 0; i &lt; SIZE; i++)
 23         items[i] = i;
    
 24     /* spawn the slaves             */
 25     result = pvm_spawn("slave_add", (char**)0, PvmTaskDefault,
 26                        "", NUM_SLAVES, slaves);
    
 27     /* check if proper number of slaves are spawned     */
 28     if(result != NUM_SLAVES)
 29     {
 30         fprintf(stderr, "Error: Cannot spawn slaves.\n");
 31         pvm_exit();
 32         exit(EXIT_FAILURE);
 33     }
    
 34     /* distribute the data among the slaves     */
 35     for(i = 0; i &lt; NUM_SLAVES; i++)
 36     {
 37         pvm_initsend(PvmDataDefault);
 38         pvm_pkint(items + i*DATA_SIZE, DATA_SIZE, 1);
 39         pvm_send(slaves[i], MSG_DATA);
 40     }
    
 41     /* receive the results from the slaves      */
 42     for(i = 0; i &lt; NUM_SLAVES; i++)
 43     {
 44         int bufid, bytes, type, source;
 45         int slave_no;
 46         
 47         /* receive message from any of the slaves       */
 48         bufid = pvm_recv(-1, MSG_RESULT);
    
 49         /* get information about the message            */
 50         pvm_bufinfo(bufid, &amp;bytes, &amp;type, &amp;source);
 51         
 52         /* get the slave number that sent the message   */
 53         slave_no = get_slave_no(slaves, source);
    
 54         /* unpack the results at appropriate position   */
 55         pvm_upkint(results + slave_no, 1, 1);
 56     }
    
 57     /* find the final result            */
 58     sum = 0;
 59     for(i = 0; i &lt; NUM_SLAVES; i++)
 60         sum += results[i];
    
 61     printf("The sum is %d\n", sum);
    
 62     /* clean up and exit from the PVM system    */
 63     pvm_exit();
    
 64     exit(EXIT_SUCCESS);
 65 } /* end main() */
 66         
 67 /* function to return the slave number of a slave given its task ID */
 68 int get_slave_no(int* slaves, int task_id)
 69 {
 70     int i;
    
 71     for(i = 0; i &lt; NUM_SLAVES; i++)
 72         if(slaves[i] == task_id)
 73             return i;
    
 74     return -1;
 75 } /* end get_slave_no() */
    
 76 /* end master_add.c */

</PRE>
<HR NOSHADE>
<PRE>
  1 /* -------------------------------------------------------------------- *
  2  * slave_add.c                                                          *
  3  *                                                                      *
  4  * Slave program for adding elements of an array using PVM              *
  5  * -------------------------------------------------------------------- */
  6 #include &lt;stdlib.h&gt;
  7 #include &lt;pvm3.h&gt;
  8 #include "tags.h"
  9 #include "common.h"
    
 10 int main()
 11 {
 12     int mytid, parent_tid;
 13     int items[DATA_SIZE];           /* data sent by the master  */
 14     int sum, i;
 15     
 16     /* enroll into the PVM system       */
 17     mytid = pvm_mytid();
    
 18     /* get the task ID of the master    */
 19     parent_tid = pvm_parent();
    
 20     /* receive the data from the master */
 21     pvm_recv(parent_tid, MSG_DATA);
 22     pvm_upkint(items, DATA_SIZE, 1);
    
 23     /* find the sum of the elements     */
 24     sum = 0;
 25     for(i = 0; i &lt; DATA_SIZE; i++)
 26         sum = sum + items[i];
    
 27     /* send the result to the master    */
 28     pvm_initsend(PvmDataDefault);
 29     pvm_pkint(&amp;sum, 1, 1);
 30     pvm_send(parent_tid, MSG_RESULT);
    
 31     /* clean up and exit from PVM       */
 32     pvm_exit();
 33     
 34     exit(EXIT_SUCCESS);
 35 } /* end main() */

</PRE>
<HR NOSHADE>
<PRE>
  1 # Make file for the PVM program for addition - makefile.add
    
  2 .SILENT :
  3 # paths fro PVM include files and libraries
  4 INCDIR=-I/usr/share/pvm3/include
  5 LIBDIR=-L/usr/share/pvm3/lib/LINUX
    
  6 # link the PVM library
  7 LIBS=-lpvm3
  8 CFLAGS=-Wall
  9 CC=gcc
 10 TARGET=all
    
 11 # this is where the PVM executables go
 12 PVM_HOME=$(HOME)/pvm3/bin/LINUX
    
 13 all : $(PVM_HOME)/master_add $(PVM_HOME)/slave_add
    
 14 $(PVM_HOME)/master_add : master_add.c common.h tags.h
 15     $(CC) -o $(PVM_HOME)/master_add master_add.c $(CFLAGS) $(LIBS) \
 16           $(INCDIR) $(LIBDIR)
 17   
 18 $(PVM_HOME)/slave_add : slave_add.c common.h tags.h
 19     $(CC) -o $(PVM_HOME)/slave_add slave_add.c $(CFLAGS) $(LIBS) \
 20          $(INCDIR) $(LIBDIR)
</PRE>
<HR NOSHADE>
<P>
<P>Let us consider the slave program first, because it is simple. The slave
receives the 25 array elements from the master in the array <CODE>items</CODE>,
finds their sum and sends the result to the master with the message tag
as <CODE>MSG_RESULT</CODE>. Now consider the master. We define an array 
<CODE>slaves</CODE> of size <CODE>NUM_SLAVES</CODE> which will store the task ID's of
the slaves spawned by the parent. There is another array <CODE>results</CODE> in
which the results from the slaves are stored. The master first initializes
the array <CODE>items</CODE> and then spawns the slaves. After that it distributes
the data among the slaves. In the call to <CODE>pvm_pkint()</CODE> on line 38,
the first parameter is the pointer to the array in which the integers are
stored, the second is the number of integers to pack and the third is the
``stride.'' Stride means how many elements to skip when packing. When it is 1,
consecutive elements are packed. When it is 2, PVM will skip 2 elements when
packing with the result that all even numbered elements (0, 2, 4 ...) will
be packed. Here we keep its value as 1. 
<P>
<P>Once the data has been distributed among the slaves, the master has to wait
till the slaves return the intermediate results. One possibility when
accepting the results is that the master will first collect the result from 
slave 0 (i.e slave whose task ID is stored in <CODE>slave[0]</CODE>), then from
slave 1 and so on. However, this may not be an efficient approach. For example,
it may be that slave 0 is working on a slower machine than slaves 1, 2 and 3.
In that case, since the master is waiting from slave 0, the results from
slaves 1, 2 and 3 are yet to be collected even though the calculations are
completed. In this case it may be fine, but consider the situation in which
the slave, when finished doing one job is given another job. In that case, we
would like to give a slave its next job immediately after it has completed its
current job. Thus, the master must be in a position to respond messages from
any of the slaves. This is what is being done here.
<P>In the call to <CODE>pvm_recv()</CODE> on line 48, we know that the first 
parameter is the task ID of the message source. If this value is kept -1, it 
signifies a <EM>wild card</EM> i.e. messages from any process with message tag
<CODE>MSG_RESULT</CODE> will be received by the master. The received message
along with some control information is stored in a buffer called as <EM>active
receive buffer</EM>. The call returns a unique ID for this buffer. Now, we
want to know who is the sender of the message so that we can assign the message
data to the appropriate element of the array <CODE>results</CODE>. The function
<CODE>pvm_bufinfo()</CODE> returns information about the message in the buffer,
such as the message tag, the number of bytes and the senders task ID. Once we
have the senders task ID, we set the appropriate element of the <CODE>results</CODE>
array to the integer sent by the slave. The rest of the program should be
easy to understand.
<P>
<H2>3.4 Working with PVM</H2>

<P>In case you are interested, you can think of some problems for which you can
write parallel programs. Many a times, due to bugs etc., you may need to clean
up the state of the things before starting. The PVM Console provides with
the command <CODE>halt</CODE> that kills the PVM daemon. Then all the PVM processes
will halt or you can halt them with the Linux <CODE>kill</CODE> command. In case
you have a network of Linux machines interconnected by say a LAN, then you
can also do ``real'' parallel processing. First of all, install PVM on all the
hosts you wish to use and then use the <CODE>add</CODE> command in the PVM Console
to add hosts to the virtual machine. Then PVM will schedule some of the 
processes to run on these hosts, so that real parallel processing is achieved.
<P>
<H2><A NAME="s4">4. Implementing with MPI</A></H2>

<P>We have seen in the previous section the implementation of the addition
program using the PVM. Now let us consider another approach that can be
used in developing parallel programs. This approach is using the MPI
library. MPI stands for <EM>Message Passing Interface</EM>. It is a standard
developed to enable us to write portable message passing applications. It
provides functions for exchanging messages and many other activities as well.
It must be noted that unlike PVM which is a software system, MPI is a standard,
so that many implementations of the MPI standard exist. We will use an
implementation of MPI called LAM which stands for <EM>Local Area Multicomputer</EM>. It is also available on the Red Hat Linux CD as an RPM package, so
installation may not be a problem.
<P>
<P>After you have installed the RPM package, go to the <CODE>/usr/boot</CODE>
directory and create a file named <CODE>conf.lam</CODE> and type in a single line 
in it: <CODE>lamd $inet_topo</CODE>. The same directory will also have a file
named <CODE>bhost.def</CODE> else create it and type in a single line in it:
<CODE>localhost</CODE>. Now to test whether everything is working correctly,
type at the prompt, <CODE>lamboot</CODE>. You will get the following response:
<PRE>
[rahul@joshicomp boot]$ lamboot

LAM 6.3.1/MPI 2 C++/ROMIO - University of Notre Dame

[rahul@joshicomp boot]$
</PRE>
<P>
<P>If the output indicates an error, then there is some problem with the 
installation, either follow the above steps or see the <EM>lamboot(1)</EM>
manual page for troubleshooting.
<P>
<P>Assuming that LAM/MPI is properly installed on your system, let us again
write a small demonstration program for MPI. 
<P>
<H2>4.1 A Demonstration MPI Program</H2>

<P>We will again write a simple master - slave program in which we are supposed to
evaluate the expression <EM>(a + b) * (c - d)</EM>. The master will read the
values of <EM>a, b, c,</EM> and <EM>d</EM> from the user and one slave will
calculate <EM>(a + b)</EM> and the other one will calculate <EM>(c - d)</EM>.
The program is as follows.
<P>
<HR NOSHADE>
<PRE>
  1 /* -------------------------------------------------------------------- *
  2  * mpi_demo.c                                                           *
  3  *                                                                      *
  4  * A simple MPI demonstration program to evaluate an expression.        *
  5  * -------------------------------------------------------------------- */
  6 #include &lt;stdio.h&gt;
  7 #include &lt;stdlib.h&gt;
  8 #include &lt;lam/mpi.h&gt;            /* for MPI constants and functions      */
    
  9 #define MSG_DATA        100     /* message from master to slaves        */
 10 #define MSG_RESULT      101     /* message from slave to master         */
    
 11 #define MASTER          0       /* rank of master                       */
 12 #define SLAVE_1         1       /* rank of first slave                  */
 13 #define SLAVE_2         2       /* rank of second slave                 */
    
 14 /* functions to handle the tasks of master, and the two slaves          */
 15 void master(void);
 16 void slave_1(void);
 17 void slave_2(void);
    
 18 int main(int argc, char** argv)
 19 {
 20     int myrank, size;
 21     
 22     /* initialize the MPI system                                        */
 23     MPI_Init(&amp;argc, &amp;argv);
    
 24     /* get the size of the communicator i.e. number of processes        */
 25     MPI_Comm_size(MPI_COMM_WORLD, &amp;size);
    
 26     /* check for proper number of processes                             */
 27     if(size != 3)
 28     {
 29         fprintf(stderr, "Error: Three copies of the program should be run.\n");
 30         MPI_Finalize();
 31         exit(EXIT_FAILURE);
 32     }
 33     
 34     /* get the rank of the process                                      */
 35     MPI_Comm_rank(MPI_COMM_WORLD, &amp;myrank);
    
 36     /* perform the tasks according to the rank                          */
 37     if(myrank == MASTER)
 38         master();
 39     else if(myrank == SLAVE_1)
 40         slave_1();
 41     else
 42         slave_2();
    
 43     /* clean up and exit from the MPI system                            */
 44     MPI_Finalize();
    
 45     exit(EXIT_SUCCESS);
 46 } /* end main() */
    
 47 /* function to carry out the masters tasks          */
 48 void master(void)
 49 {
 50     int a, b, c, d;
 51     int buf[2];
 52     int result1, result2;
 53     MPI_Status status;
    
 54     printf("Enter the values of a, b, c, and d: ");
 55     scanf("%d %d %d %d", &amp;a, &amp;b, &amp;c, &amp;d);
    
 56     /* send a and b to the first slave              */
 57     buf[0] = a;
 58     buf[1] = b;
 59     MPI_Send(buf, 2, MPI_INT, SLAVE_1, MSG_DATA, MPI_COMM_WORLD);
    
 60     /* send c and d to the secons slave             */
 61     buf[0] = c;
 62     buf[1] = d;
 63     MPI_Send(buf, 2, MPI_INT, SLAVE_2, MSG_DATA, MPI_COMM_WORLD);
    
 64     /* receive results from the slaves              */
 65     MPI_Recv(&amp;result1, 1, MPI_INT, SLAVE_1, MSG_RESULT, 
 66              MPI_COMM_WORLD, &amp;status);
 67     MPI_Recv(&amp;result2, 1, MPI_INT, SLAVE_2, MSG_RESULT, 
 68              MPI_COMM_WORLD, &amp;status);
    
 69     /* final result                                 */
 70     printf("Value of (a + b) * (c - d) is %d\n", result1 * result2);
 71 } /* end master() */
    
 72 /* function to carry out the tasks of the first slave       */
 73 void slave_1(void)
 74 {
 75     int buf[2];
 76     int result;
 77     MPI_Status status;
 78     
 79     /* receive the two values from the master       */ 
 80     MPI_Recv(buf, 2, MPI_INT, MASTER, MSG_DATA, MPI_COMM_WORLD, &amp;status);
 81     
 82     /* find a + b                                   */
 83     result = buf[0] + buf[1];
    
 84     /* send result to the master                    */
 85     MPI_Send(&amp;result, 1, MPI_INT, MASTER, MSG_RESULT, MPI_COMM_WORLD);
 86 } /* end slave_1() */
    
 87 /* function to carry out the tasks of the second slave      */
 88 void slave_2(void)
 89 {
 90     int buf[2];
 91     int result;
 92     MPI_Status status;
 93     
 94     /* receive the two values from the master       */
 95     MPI_Recv(buf, 2, MPI_INT, MASTER, MSG_DATA, MPI_COMM_WORLD, &amp;status);
 96     
 97     /* find c - d                                   */
 98     result = buf[0] - buf[1];
    
 99     /* send result to master                        */
100     MPI_Send(&amp;result, 1, MPI_INT, MASTER, MSG_RESULT, MPI_COMM_WORLD);
101 } /* end slave_2() */
    
102 /* end mpi_demo.c */
</PRE>
<HR NOSHADE>
<PRE>
  1 # Makefile for MPI demo program - makefile.mpidemo
  2 .SILENT:
  3 CFLAGS=-I/usr/include/lam -L/usr/lib/lam
  4 CC=mpicc
    
  5 mpi_demo : mpi_demo.c
  6     $(CC) $(CFLAGS) mpi_demo.c -o mpi_demo
</PRE>
<HR NOSHADE>
    
<P>To compile this program, give the command <CODE>make -f makefile.mpidemo</CODE>.
Once you have compiled the program, to run the program you first need to 
``start'' or ``boot'' the Local Area Multicomputer system. This is done with
the <CODE>lamboot</CODE> command. After that, to run the program by giving the
following command: <CODE>mpirun -np 3 mpi_demo</CODE>.
<PRE>
[rahul@joshicomp parallel]$ lamboot

LAM 6.3.1/MPI 2 C++/ROMIO - University of Notre Dame

[rahul@joshicomp parallel]$ mpirun -np 3 mpi_demo
Enter the values of a, b, c, and d: 1 2 3 4
Value of (a + b) * (c - d) is -3
[rahul@joshicomp parallel]$
</PRE>
<P>
<H2>4.2 Explanation of the Program</H2>

<P>To use the MPI system and functions, you first need to include the header
file <CODE>mpi.h</CODE> as is done in line 8. In case of PVM, different processes
are identified with their task ID's. In case of MPI, the MPI system assigns
each process a unique integer called as its <EM>rank</EM> beginning with 0.
The rank is used to identify a process and communicate with it. Secondly,
each process is a member of some <EM>communicator</EM>. A communicator can
be thought of as a group of processes that may exchange messages with each
other. By default, every process is a member of the communicator called
<CODE>MPI_COMM_WORLD</CODE>. Although we can create new communicators, this leads
to an unnecessary increase in complexity, so we suffice ourselves by using the
<CODE>MPI_COMM_WORLD</CODE> communicator.
<P>
<P>Any MPI program must first call the <CODE>MPI_Init()</CODE> function. This function
is used by the process to enter the MPI system and also do any specific
initialization required by the system. Next, we get the size of the 
<CODE>MPI_COMM_WORLD</CODE> communicator i.e. the number of processes in it using the <code>MPI_Comm_size()</code> function. The
first parameter is the communicator and the second is a pointer to an integer
in which the size will be returned. Here, we need exactly 3 processes, one 
master and two slaves. After that, we get the rank by calling 
<CODE>MPI_Comm_rank()</CODE>. The three processes will have ranks 0, 1 and 2. All
these processes are essentially identical i.e. there is no inherent 
master - slave relationship between them. So it is up to us to decide who will
be the master and who will be the slaves. We choose rank 0 as master and ranks
1 and 2 as slaves. It can also be seen that we have included the code for both
the master and the two slaves in the same program. Depending upon the rank,
we choose to execute the appropriate function. Note that there is no spawning
of processes as in PVM, and as we shall see, we choose to decide the number
of process to be spawned from a command line argument rather than the program
spawning slaves. Once the execution is finished,
we must call the <CODE>MPI_Finalize()</CODE> function to perform final clean up.
<P>
<P>Let us now consider the master function. After reading the values of a, b, c, 
and d from the user, the master must send a and b to slave 1 and c and d to
slave 2. Instead of sending the variables individually, we choose to pack them
up in an array and send the array of 2 integers instead. It is always better
to pack up the data you want to send into a single message rather than to send
a number of messages for individual data items, this saves the communication
overhead involved in passing the messages. Once the buffer is ready, unlike PVM,
we do not need to pack or encode the data, MPI will manage these details
internally. So we can directly call the <CODE>MPI_Send()</CODE> function to send
the data. The first parameter (line 59) is the address of the buffer, the
second one the number of elements in the message, the third is a specification
of the data type of the buffer, which here is <CODE>MPI_INT</CODE> specifying that
the buffer is an array of integers. Next comes the rank of the process to which
we want to send the message. Here it is <CODE>SLAVE_1</CODE> (#defined as 1).
Next is the <EM>message tag</EM> similar to that in case of PVM. Final parameter
is the communicator of which the receiver is a member, which in this case, is
<CODE>MPI_COMM_WORLD</CODE>.
<P>
<P>Once the data is distributed among the slaves, the master must wait for the
slaves to send the results. For simplicity, we first collect the message from
the slave 1 and then from slave 2. To receive a message, we use the 
<CODE>MPI_Recv()</CODE> function. Again, packing and decoding is handled by MPI
internally. The first argument (line 65) is the address of the buffer in which
to receive the data. The second is the size of the buffer in terms of the
number of elements, which in this case is 1. Next is the data type, which is
<CODE>MPI_INT</CODE> here. Next three parameters specify the rank of the source of
the message, the tag of the expected message and the communicator of which the
source is the member. The final argument is a pointer to a structure of type
<CODE>MPI_Status</CODE> in which some status information will be returned (however,
we ignore this information). Now that you know about the basic MPI terms,
the <CODE>slave_1()</CODE> and <CODE>slave_2()</CODE> functions should be clear.
<P>
<P>
<P>In this program, the code for the master as well as the slaves was in the same
executable file. Later on we will see how we can execute multiple executables.
From the makefile, we see that to compile the MPI program, a wrapper program
<CODE>mpicc</CODE> is provided which links the required libraries automatically.
To run the program, use the <CODE>mpirun -np 3 mpi_demo</CODE> command after 
booting the LAM. Here we specify LAM to create 3 processes, one master and two
slaves.
<P>
<H2>4.3 The Addition Program Again</H2>

<P>Let us now re implement the addition program that we designed before using MPI.
Here we will also show you how to execute separate programs in MPI. When we
use a single executable in the MPI program, we call it <EM>Single Program
Multiple Data (SPMD)</EM> application. When two or more executables are
involved, we call it <EM>Multiple Program Multiple Data (MPMD)</EM> application.
With LAM, MPMD programs are executed with the help of an <EM>application
schema</EM>. But before that, let us see the source of the master and the slave
programs.
<HR NOSHADE>
<PRE>
  1 /* -------------------------------------------------------------------- *
  2  * master_mpi.c                                                         *
  3  *                                                                      *
  4  * Master program for adding the elements of an array using MPI         *
  5  * -------------------------------------------------------------------- */
  6 #include &lt;stdio.h&gt;
  7 #include &lt;stdlib.h&gt;
  8 #include &lt;lam/mpi.h&gt;        /* MPI constants and functions              */
  9 #include "tags.h"           /* tags for different messages              */
 10 #include "common.h"         /* common constants                         */
    
 11 int main(int argc, char** argv)
 12 {
 13     int size, i, sum;
 14     int items[SIZE];
 15     int results[NUM_SLAVES];
 16     MPI_Status status;
    
 17     /* initlalize the MPI System                */
 18     MPI_Init(&amp;argc, &amp;argv);
    
 19     /* check for proper number of processes     */
 20     MPI_Comm_size(MPI_COMM_WORLD, &amp;size);
    
 21     if(size != 5)
 22     {
 23         fprintf(stderr, "Error: Need exactly five processes.\n");
 24         MPI_Finalize();
 25         exit(EXIT_FAILURE);
 26     }
    
 27     /* initialize the `items' array             */
 28     for(i = 0; i &lt; SIZE; i++)
 29         items[i] = i;
    
 30     /* distribute the data among the slaves     */
 31     for(i = 0; i &lt; NUM_SLAVES; i++)
 32         MPI_Send(items + i*DATA_SIZE, DATA_SIZE, MPI_INT, i + 1,
 33                  MSG_DATA, MPI_COMM_WORLD);
    
 34     /* collect the results from the slaves      */
 35     for(i = 0; i &lt; NUM_SLAVES; i++)
 36     {
 37         int result;
 38         
 39         MPI_Recv(&amp;result, 1, MPI_INT, MPI_ANY_SOURCE, MSG_RESULT,
 40                  MPI_COMM_WORLD, &amp;status);
 41         results[status.MPI_SOURCE - 1] = result;
 42     }
    
 43     /* find the final answer                    */
 44     sum = 0;
 45     for(i = 0; i &lt; NUM_SLAVES; i++)
 46         sum = sum + results[i];
    
 47     printf("The sum is %d\n", sum);
    
 48     /* clean up and exit the MPI system         */
 49     MPI_Finalize();
    
 50     exit(EXIT_SUCCESS);
 51 } /* and main() */
    
 52 /* end master_mpi.c */
</PRE>
<HR NOSHADE>
<PRE>
  1 /* -------------------------------------------------------------------- *
  2  * slave_mpi.c                                                          *
  3  *                                                                      *
  4  * Slave program for adding array elements using MPI.                   *
  5  * -------------------------------------------------------------------- */
  6 #include &lt;stdio.h&gt;
  7 #include &lt;stdlib.h&gt;
  8 #include &lt;lam/mpi.h&gt;        /* MPI functions and constants  */
  9 #include "tags.h"           /* message tags                 */
 10 #include "common.h"         /* common constants             */
    
 11 #define MASTER  0           /* rank of the master           */
    
 12 int main(int argc, char** argv)
 13 {
 14     int items[DATA_SIZE];
 15     int size, sum, i;
 16     MPI_Status status;
    
 17     /* initialize the MPI system            */
 18     MPI_Init(&amp;argc, &amp;argv);
    
 19     /* check for proper number of processes */
 20     MPI_Comm_size(MPI_COMM_WORLD, &amp;size);
    
 21     if(size != 5)
 22     {
 23         fprintf(stderr, "Error: Need exactly five processes.\n");
 24         MPI_Finalize();
 25         exit(EXIT_FAILURE);
 26     }
    
 27     /* receive data from the master         */
 28     MPI_Recv(items, DATA_SIZE, MPI_INT, MASTER, MSG_DATA,
 29              MPI_COMM_WORLD, &amp;status);
    
 30     /* find the sum                         */
 31     sum = 0;
 32     for(i = 0; i &lt; DATA_SIZE; i++)
 33         sum = sum + items[i];
    
 34     /* send the result to the master        */
 35     MPI_Send(&amp;sum, 1, MPI_INT, MASTER, MSG_RESULT, MPI_COMM_WORLD);
    
 36     /* clean up and exit MPI system         */
 37     MPI_Finalize();
    
 38     exit(EXIT_SUCCESS);
 39 } /* end main() */
    
 40 /* end slave_mpi.c */
</PRE>
<HR NOSHADE>
<PRE>
  1 # Makefile for MPI addition program - makefile.mpiadd
  2 .SILENT:
  3 CFLAGS=-I/usr/include/lam  -L/usr/lib/lam
  4 CC=mpicc
    
  5 all : master_mpi slave_mpi
    
  6 master_mpi : master_mpi.c common.h tags.h
  7     $(CC) $(CFLAGS) master_mpi.c -o master_mpi
    
  8 slave_mpi : slave_mpi.c common.h tags.h
  9     $(CC) $(CFLAGS) slave_mpi.c -o slave_mpi
</PRE>
<HR NOSHADE>
<P>
<P>To compile the programs, type <CODE>make -f makefile.mpiadd</CODE>. (The
files <code>common.h</code> and <code>tags.h</code> are the same as used for the PVM program.)
This will create the <CODE>master_mpi</CODE> and <CODE>slave_mpi</CODE> executables. Now how do
we tell MPI to run both these executables. This is where <EM>application
schema file</EM> comes in. The application schema file specifies the executables
to be run, the nodes on which to run and the number of copies of the executable
to run. Create a new file <CODE>add.schema</CODE> and type in it the following
lines:
<PRE>
# Application schema for the addition program using MPI
n0 master_mpi
n0 -np 4 slave_mpi
</PRE>
<P>This file specifies that MPI should start 1 copy of the master (which will have
rank 0) and 4 copies of slaves on the node n0, i.e. the local node. You can
specify many more parameters in this schema file like command line arguments 
etc., see the manual page <EM>appschema(1)</EM>. Once the schema file is ready,
you can run the programs as follows:
<PRE>
[rahul@joshicomp parallel]$ lamboot

LAM 6.3.1/MPI 2 C++/ROMIO - University of Notre Dame

[rahul@joshicomp parallel]$ mpirun add.schema
The sum is 4950
[rahul@joshicomp parallel]$
</PRE>
<P>
<P>Much of the program should be easy to understand. On line 39, when receiving
intermediate results from the slaves, we specify the source as 
<CODE>MPI_ANY_SOURCE</CODE>, since we want to respond to slaves in the order in
which they complete the calculations, as discussed earlier. In this case, the
<CODE>status</CODE> structure contains the actual source in the field 
<CODE>MPI_SOURCE</CODE>. We use this information to set the appropriate element from
the <CODE>results</CODE> array to the intermediate result received. 
<P>In case you have a network of interconnected computers, you can make programs
run on many computers by suitably modifying the application schema file. Instead
of specifying n0 as the host, specify the name of the host and the number of
processes you wish to schedule on that host. For more information about this,
see the manual pages and the references. 
<P>
<H2><A NAME="s5">5. Conclusion</A></H2>

<P>We have seen how to write parallel programs using the PVM and MPI libraries.
Since there libraries are available on many platforms and these are the defacto
standards used for implementing parallel programs, programs written with PVM
or MPI will run with little or no modification on large scale machines, if the
need arises. What we have basically concentrated on in this article is the
<EM>point to point</EM> communication functions provides by these libraries and
their use in message passing. Apart from these facilities, both PVM and MPI
provide a number of advanced features such as <EM>collective communication 
(broadcasting or multicasting), process groups and group management, reduction
functions etc.</EM> You are welcome to explore these advanced features. These
public domain softwares enable us to use a network of computers as a single large
computer, so in case you have some such large problem to solve, you may consider
using a network at your college or office. You will have to refer to the books
given below for the exact details of how such a setup may be established.
Many tutorials as well as books are available to help you. Below is a list of 
the material I referred.
<P>
<OL>
<LI><EM>PVM: Parallel Virtual Machine - A User's Guide and Tutorial for
Networked Parallel Computing</EM>, Al Geist, Adam Beguelin, 
Jack Dongarra, Robert Manchek, Weicheng Jiang and Vaidy Sunderam,
MIT Press. Available at 
<A HREF="http://www.netlib.org">www.netlib.org</A></LI>
<LI> <EM>MPI: The Complete Reference</EM>, Marc Snir, Steve Otto, 
Steven Huss-Lederman, David Waker and Jack Dongarra, MIT Press.
Available at 
<A HREF="http://www.netlib.org">www.netlib.org</A>.</LI>
<LI>  <EM>RS/6000 SP: Practical MPI Programming</EM>,Yukiya Aoyama and Jan 
Nakano, International Techical Support Organization, IBM Corporation,
<A HREF="http://www.redbooks.ibm.com">www.redbooks.ibm.com</A>.
</LI>
<LI> <EM>A Beginner's Guide to PVM Parallel Virtual Machine</EM>, Clay 
Breshears and Asim YarKhan, Joint 
Institute of Computational Science, University of Tennessee, USA.
<A HREF="http://www-jics.cs.utk.edu/PVM/pvm/_guide.html">www-jics.cs.utk.edu/PVM/pvm/_guide.html</A>.</LI>
<LI> <EM>PVM: An Introduction to Parallel Virtual Machine</EM>, Emily Angerer 
Crawford, Office of Information Technology, High Performance Computing,
<A HREF="http://www.hpc.gatech.edu/seminar/pvm.html">www.hpc.gatech.edu/seminar/pvm.html</A>.</LI>
</OL>

<P>
<H2>6. Acknowlegements</H2>
<P>
I would like to thank my project guide <em>Dr. Uday Khedker</em> for his
encouragement and help. I would like to thank the <em>Center for Developement
of Advanced Computing</em> for allowing me to run the MPI and PVM programs on
the PARAM Supercomputer and <em>Dr. Anabarsu</em> for guiding me during the
implementation.





<!-- *** BEGIN copyright *** -->
<P> <hr> <!-- P --> 
<H5 ALIGN=center>

Copyright &copy; 2001, Rahul U. Joshi.<BR>
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR> 
Published in Issue 65 of <i>Linux Gazette</i>, April 2001</H5>
<!-- *** END copyright *** -->

<!--startcut ==========================================================-->
<HR><P>
<CENTER>
<!-- *** BEGIN navbar *** -->
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="jenkins.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue65/joshi.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom"  ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="lilly.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom"  ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
<!-- *** END navbar *** -->
</CENTER>
</BODY></HTML>
<!--endcut ============================================================-->