Broadcast "Hello World" Program: A Parallel "Hello World" Program

Figure: Control Flow of the Broadcast "Hello World" Program

This version of "Hello World" is basically the same as the Array "Hello World" program except that all of the Hello chare objects will be told to say "Hello" at the same time instead of each of them doing it one-by-one. The figure to the right shows the control flow of the program. The source code is located below. Here are the differences from the Array "Hello World" program:

  • broadcast: In the Array "Hello World" program, the last thing the Main::Main() constructor did was tell the first element of the helloArray to sayHi(). In this version of "Hello World," Main::Main tells the entire array of Hello chare objects to sayHi(). This is done by calling Hello::sayHi() on the array itself (instead of just a single element of the array).
  • sayHi(): The Hello::sayHi() entry method no longer has the if statement that tests if the object is the last object in the chare array. Instead, every element of the chare arrays invokes done() on the Main chare object (sends a message).
  • done(): The Main::done() entry method is now invoked multiple times. The Main chare object has a counter, doneCount, that counts the number of times Main::done() has been called. Once this count reaches the number of elements in the Hello chare array, the program exits.
  • numElements: Since the Main chare object is the only object that needs access to the numElements value, it has been made into a member variable of the Main chare class.

Broadcast "Hello World" Code

The source code for this example can be found here (BroadcastHelloWorld.tar.gz).

The "Hello" Chare Class

Header File (hello.h)
#ifndef __HELLO_H__
#define __HELLO_H__

class Hello : public CBase_Hello {

 public:

  /// Constructors ///
  Hello();
  Hello(CkMigrateMessage *msg);

  /// Entry Methods ///
  void sayHi(int from);
};

#endif //__HELLO_H__
Interface File (hello.ci)
module hello {

  array [1D] Hello {
    entry Hello();
    entry void sayHi(int);
  };

};
Source File (hello.C)
#include "hello.decl.h"

#include "hello.h"
#include "main.decl.h"

extern /* readonly */ CProxy_Main mainProxy;

Hello::Hello() {
  // Nothing to do when the Hello chare object is created.
  // This is where member variables would be initialized
  // just like in a C++ class constructor.
}

// Constructor needed for chare object migration (ignore for now)
// NOTE: This constructor does not need to appear in the ".ci" file
Hello::Hello(CkMigrateMessage *msg) { }

void Hello ::sayHi(int from) {

  // Have this chare object say hello to the user.
  CkPrintf("\"Hello\" from Hello chare # %d on "
           "processor %d (told by %d).\n",
           thisIndex, CkMyPe(), from);

  // Report to the Main chare object that this chare object
  // has completed its task.
  mainProxy.done();
}

#include "hello.def.h"

The "Main" Chare Class

Header File (main.h)
#ifndef __MAIN_H__
#define __MAIN_H__

class Main : public CBase_Main {

 private:
  /// Member Variables (Object State) ///
  int numElements;
  int doneCount;

 public:
  /// Constructors ///
  Main(CkArgMsg* msg);
  Main(CkMigrateMessage* msg);

  /// Entry Methods ///
  void done();
};

#endif //__MAIN_H__
Interface File (main.ci)
mainmodule main {

  readonly CProxy_Main mainProxy;

  extern module hello;

  mainchare Main {
    entry Main(CkArgMsg* msg);
    entry void done();
  };

};
Source File (main.C)
#include "main.decl.h"

#include "main.h"
#include "hello.decl.h"

/* readonly */ CProxy_Main mainProxy;

// Entry point of Charm++ application
Main::Main(CkArgMsg* msg) {

  // Initialize the local member variables
  doneCount = 0;    // Set doneCount to 0
  numElements = 5;  // Default numElements to 5

  // There should be 0 or 1 command line arguements.
  // If there is one, it is the number of "Hello"
  // chares that should be created.
  if (msg->argc > 1)
    numElements = atoi(msg->argv[1]);

  // We are done with msg so delete it.
  delete msg;

  // Display some info about this execution
  // for the user.
  CkPrintf("Running \"Hello World\" with %d elements "
           "using %d processors.\n",
           numElements, CkNumPes());

  // Set the mainProxy readonly to point to a
  // proxy for the Main chare object (this
  // chare object).
  mainProxy = thisProxy;

  // Create the array of Hello chare objects.
  CProxy_Hello helloArray = CProxy_Hello::ckNew(numElements);

  // Invoke the "sayHi()" entry method on all of the
  // elements in the helloArray array of chare objects.
  helloArray.sayHi(-1);
}

// Constructor needed for chare object migration (ignore for now)
// NOTE: This constructor does not need to appear in the ".ci" file
Main::Main(CkMigrateMessage* msg) { }

// When called, the "done()" entry method will increment the doneCount.
// Once all of the Hello chare objects have indiciated that they have
// completed their tasks, "done()" will cause the program to exit.
void Main::done() {
  // Increment the doneCount. If all of the Hello chare
  // objects have indicated that they are done, then exit.
  // Otherwise, continue waiting for the Hello chare objects.
  doneCount++;
  if (doneCount >= numElements)
    CkExit();
}

#include "main.def.h"

Makefile

The makefile for this program is the same as it is for the Array "Hello World" program.

Makefile
CHARMDIR = [put Charm++ install directory here]
CHARMC = $(CHARMDIR)/bin/charmc $(OPTS)

default: all
all: hello

hello : main.o hello.o
   $(CHARMC) -language charm++ -o hello main.o hello.o

main.o : main.C main.h main.decl.h main.def.h hello.decl.h
   $(CHARMC) -o main.o main.C

main.decl.h main.def.h : main.ci
   $(CHARMC) main.ci

hello.o : hello.C hello.h hello.decl.h hello.def.h main.decl.h
   $(CHARMC) -o hello.o hello.C

hello.decl.h hello.def.h : hello.ci
   $(CHARMC) hello.ci

clean:
   rm -f main.decl.h main.def.h main.o
   rm -f hello.decl.h hello.def.h hello.o
   rm -f hello charmrun

Output

The only difference in the output of this program and the Array "Hello World" program is that all of the Hello chare objects are told to sayHi() by the Main chare object. This is reflected by the fact that all the output lines below have "told by -1" in them.

$ ./charmrun +p3 ./hello 10
Running "Hello World" with 10 elements using 3 processors.
"Hello" from Hello chare # 0 on processor 0 (told by -1).
"Hello" from Hello chare # 3 on processor 0 (told by -1).
"Hello" from Hello chare # 6 on processor 0 (told by -1).
"Hello" from Hello chare # 9 on processor 0 (told by -1).
"Hello" from Hello chare # 1 on processor 1 (told by -1).
"Hello" from Hello chare # 4 on processor 1 (told by -1).
"Hello" from Hello chare # 7 on processor 1 (told by -1).
"Hello" from Hello chare # 2 on processor 2 (told by -1).
"Hello" from Hello chare # 5 on processor 2 (told by -1).
"Hello" from Hello chare # 8 on processor 2 (told by -1).
Figure: Output of the Broadcast "Hello World" Program