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++.

  1. 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 .

  2. 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 .

  3. 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.

  4. 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.

  5. 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 .

  6. 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 .

  7. 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.

paolo@cs.caltech.edu