Components of a Charm++ Program
|Figure 1: Files Needed by C++ and by Charm++|
Remember that Charm++ is based on the C++ programming language and uses an object-oriented style of programming. With this in mind, let's recap on what is needed for a basic C++ application. Each class in the C++ program needs two things, a class definition and function bodies for all it's member functions. The class definition defines what member functions the class has along with what state the class has (member variables). The function bodies contain the code that is executed when one of the member functions is invoked. Traditionally, these two items are kept in separate files (the class definition in the ".h" or header file and the function bodies in the ".cpp" or source file). Keep in mind that having these items in separate files is just common practice. It is possible to have function bodies contained in the class definition, to have the class definition in the source file, and so on. Presenting these items in this manner is only meant to give the reader a clear idea of the components needed. Please note: For Charm++ programs, it is common to see the ".C" file extension used instead of the ".cpp" file extension for source files. Using either extension is fine.
Chare classes need one additional piece of information, the interface definition. The interface definition specifies which member functions can be remotely invoked by other chare objects in the global object space. This information is contained in a ".ci" or interface file.
Charmc is a Charm++ tool that wraps the native compiler. By native compiler we mean whatever compiler happens to be installed on the machine where the Charm++ application is being compiled. The purpose of charmc is to allow the makefiles used to compile Charm++ programs as portable as the Charm++ programs themselves. The programmer writes the Makefile files using the charmc command instead of directly calling the native C++ compiler. Charmc is available on all platforms that the Charm++ Runtime System is also available on. Charmc also provides some additional capability beyond just wrapping the native C++ compiler (such as wrapping the charmxi tool described below, and so on) but this will not be covered here.
Charmxi and the Interface File
|Figure 2: Compilation Process for a Chare Class|
The interface file is processed by a Charm++ tool called charmxi the result of which is a set of "decl.h" and "def.h" files. These files contain auto-generated code that hooks the programmer's application code to the Charm++ Runtime System. This auto-generated code is then included into the associated source file when the source file is compiled. Figure 2 (to the right) shows the compilation process for a single chare class. First, the interface file is processed by the charmxi tool which auto-generates some code needed by the Charm++ Runtime System. Next, the source file, with the associated #include lines for the generated files, is compiled in a manner similar to a class in C++. The result is an object file that contains the chare class.
After all of the chare classes for an application have been compiled, charmc is once again used to link all of them together. It is during this link step when the Charm++ Runtime System is also linked into the application.
Running a Charm++ Program
When charmc creates the final executable, another program called charmrun will also be created in the same directory as the executable. Charm++ applications can be executed in two ways. The first is to simply run the executable. This is referred to as standalone mode which means that the program is only being executed on the local machine using a single processor. The second is way to run a Charm++ program is to launch it using the charmrun program.
When a Charm++ program is launched using the charmrun program, the number of processors to use, amongst other things, is specified by using the +p# command line argument ( where the # is replaced by the actual number of processors to use). For example, one might use the command "charmrun +p4 ./myProgram" to launch the Charm++ program called myProgram on four processors. The processors available for use by charmrun are specified in a nodelist file.
The Nodelist File
|Figure 3: Example Nodelist File|
The nodelist file specifies what processors are available to a Charm++ program when it is launched using charmrun. Figure 3 contains an example nodelist file. The lines beginning with host specify what processors are available for use by charmrun. The processors are used in-order by the charmrun. For example, if charmrun is used to launch a Charm++ program using two processors, then a single process will be launched on each of the hosts alpha and bravo. If four processors are needed, then all of the hosts in this example nodelist file (alpha, bravo, charlie, and delta) will have a single process running on them. If a single machine/node has more than one processor, it can be listed multiple times in the nodelist file, however, the hosts are still used in order from the nodelist file. In this example, the machines alpha and bravo have two processors each. If five processors are needed by charmrun, all of the hosts will have a single process running on them except for alpha which will have two processes running on it. Please Note: If number of processors used by charmrun is greater than the number of host lines specified in the nodelist file, charmrun will loop around on the list and start using host lines from the beginning of the list. Additionally, some target platforms do not require the user to specify a nodelist file since the processors are dynamically assigned upon job creation.
The ++nodelist command line option can be used to specify the nodelist file to use when executing a Charm++ program using charmrun. If this command line options is not specified, charmrun defaults to using "~/.nodelist" for the nodelist file.