Previous: Introduction to Distributed Computing Up: Distributed Hello World
Distributed Hello World
Let us modify the `Hello world' example presented in
Chapter
to say hello from a set
of processor objects. We use three files:
// dist\_Greeter.h
#include <iostream.h>
global class Greeter { // global identifies a processor object type
public:
Greeter() {}
void say_hi (int id);
};
// dist\_Hello.cc++
#include <stdlib.h>
#include "dist_Greeter.h"
// argv[1] -- \# of Greetings desired (integer)
// argv[2]..argv[1+argv[1]] --
// machines on which processor objects should be located (strings)
int main (int argc, char** argv)
{
int P = atoi(argv[1]); // P becomes \# of processor objects to be created
parfor(int p=0; p<P; p++) {
Greeter *global G;
proc_t placement = proc_t("dist_Greeter.out",argv[2+p]);
// placement of processor object is specified by a (definition,location) pair
G = new (placement) Greeter();
G->say_hi(p);
delete G;
}
return 0;
}
// dist\_Greeter.cc++
#include "dist_Greeter.h"
void Greeter::say_hi (int id)
{
cout << "Hello World from Processor Object#" << id << endl;
}
From our shell we compile two executables:
>cc++ dist_Greeter.cc++ -ptype=Greeter -o dist_Greeter.out
>cc++ dist_Hello.cc++ -o dist_Hello.out
We must now start PVM, a communication library that CC++
uses. Basically this means invoking pvmd with a file (the
hostfile) that lists the machines we plan to use in our computation.
The release notes for the CC++ compiler provide more detailed
instructions for executing distributed CC++ computations than given
here.
We execute
>pvmd hostfile
and then place pvmd in the background.
Finally we are ready! We run dist_Hello.out, telling it how
many greetings we want, and a corresponding list of locations from
which we want greetings. For instance, here at Caltech we might write
>dist_Hello.out 3 fides hebe fides
As described in the section of the release notes entitled
``Running a Distributed CC++ Program'', the standard output from
dist_Hello.out will be piped to a file in the tmp directory of
the machine you started pvmd from.
Let us examine the parts of this program that are not standard C++.
- The keyword global qualifying the class declaration on
line 2. This identifies Greeter as a processor object type.
Each object of type Greeter will be a separate address space.
Note that other than the word global, class Greeter looks
like any other class: processor objects have constructors,
destructors, private, public and protected members, and can be
inherited. Processor objects are explained in
Chapter
.
- The keyword global qualifying the pointer declaration on
line 16. This identifies G as a global pointer. A global
pointer can reference memory in other processor objects, and thus is
the basic mechanism for communication in CC++. Global pointers are
explained in Chapter
.
- The object placement of type proc_t on line 17.
proc_t is an implementation-defined type that specifies
placement of a processor object. In our implementation of CC++,
proc_t contains two fields: an executable name and a machine name.
The executable name states where the definition of the processor
object can be found, and the machine name states on what machine that
processor object should be created. We compiled the definition of
type Greeter into dist_Greeter.out, and we take the
th
argument in array argv as the machine name.
- The allocation G=new (placement) Greeter() on line 19.
This creates an object of type Greeter, placed according to the
proc_t placement. Like all calls to new, a pointer
to the newly created object is returned. Since that object is a
processor object, by definition it resides in another address space,
and therefore G must be a global pointer.
- The function call G->say_hi(p) on line 20. This invokes
the member function say_hi on the object referenced by G.
Since G references another processor object, the function will
be executed on that processor object. This is known as a remote
procedure call, or RPC. say_hi takes an argument, which is
transferred to the processor object where the function is to execute.
If say_hi had a return type, the value returned would be
transferred back. The mechanism for controlling how data is
transferred is explained in Chapter
.
- The deallocation delete G on line 21. This destroys the
processor object referenced by G. All variables inside the
processor object are destroyed, and any member functions of the
processor object currently executing are halted. The execution of
dist_Greeter.out on machine argv[2+p] is terminated.
Deallocation of a processor object is trickier than that of other objects:
a processor object might have other member functions executing when
the destructor is run. This will be discussed in
Chapter
.
- The compilation cc++ dist_Greeter.cc++ -ptype=Greeter -o
dist_Greeter.out. In CC++, a processor object type is defined by an
executable. The -ptype= linker option names the processor
object type that this executable defines. When a processor object
is created, CC++ checks to insure that the type specified in the
new statement and the type assigned to the executable match. Here we define
the processor object type Greeter by the executable created
from compiling dist_Greeter.cc++. The actual name of the executable
doesn't matter.
The next few chapters will explore the concepts presented above in
greater detail. Global pointers, data transfer functions, and
processor objects will be examined.