Hello, world
A trivial coNCePTuaL program

As our first example of coNCePTuaL programming, here's how to write a coNCePTuaL program in which every task (meaning a process, thread, or some other unit of program control) outputs the string Hello, world! to the standard output device.

The code

The following is a complete Hello, world program written in coNCePTuaL:

All tasks output "Hello, world!".

coNCePTuaL is intended for writing parallel programs, implying that a typical coNCePTuaL program comprises multiple tasks that execute concurrently. Each task is identified by a task number, a unique integer ranging from zero to one less than the number of tasks in the program. To make our Hello, world program more interesting, let's have each task output its task number:

All tasks mytask output "Hello from task " and mytask and "!".

In the above, each task stores its task number in the variable mytask and subsequently outputs mytask.

Invoking the compiler

The coNCePTuaL compiler is unique in that is provides multiple code generators, called backends. Each backend lets the user specify a target language and communication library for which to generate code. Let's compile our Hello, world program into C and use Unix-domain datagrams for communication:

$ ncptl --backend=c_udgram helloworld.ncptl
# Loading the c_udgram backend from /usr/local/lib/python2.4/site-packages/conceptual/codegen_c_udgram.py ...
# Reading a coNCePTuaL program from helloworld.ncptl ...
# Lexing ...
# Parsing ...
# Analyzing program semantics ...
# Compiling helloworld.ncptl using the c_udgram backend ...
# Compiling and linking the result ...
gcc -I/usr/local/include -g -O2 tmp-dRzoa.c -L/usr/local/lib -lncptl -luuid -lm -lpopt -o helloworld
# Deleting tmp-dRzoa.c ...
# Files generated: helloworld

The compiler converted helloworld.ncptl to a temporary C file (tmp-dRzoa.c), passed that to the C compiler, produced an executable called helloworld, and deleted the temporary C file. (The --keep-ints compiler option tells the compiler to keep intermediate files. If you're interested, you can look at the generated helloworld.c file, but be forewarned: it's not pretty.)

Running the generated executable

Let's specify that we want our parallel program to run with eight tasks:

$ ./helloworld --tasks=8
Hello from task 1!
Hello from task 2!
Hello from task 0!
Hello from task 4!
Hello from task 6!
Hello from task 3!
Hello from task 5!
Hello from task 7!

All tasks run concurrently. Hence, the lines of output appear in no particular order.

How did we know to use --tasks? Easy; all coNCePTuaL programs automatically provide support for a --help option:

$ ./helloworld --help
Usage: helloworld [OPTION...]
  -C, --comment=<string>      Additional commentary to write to the log file,
                              @FILE to import commentary from FILE, or
                              !COMMAND to import commentary from COMMAND (may
                              be specified repeatedly)
  -L, --logfile=<string>      Log-file template [default: "helloworld-%p.log"]
  -N, --no-trap=<string>      List of signals which should not be trapped
                              [default: ""]
  -T, --tasks=<number>        Number of tasks to use [default: 1]
  -W, --watchdog=<number>     Number of minutes after which to kill the job
                              (-1=never) [default: -1]

Help options:
  -?, --help                  Show this help message
  --usage                     Display brief usage message

Not bad for one line of coNCePTuaL code, eh?

Invoking the compiler (again)

The most common use of coNCePTuaL is to produce parallel programs using MPI as the communication layer. Let's recompile helloworld.ncptl using MPI instead of Unix-domain datagrams:

$ ncptl --backend=c_mpi helloworld.ncptl
# Loading the c_mpi backend from /usr/local/lib/python2.4/site-packages/conceptual/codegen_c_mpi.py ...
# Reading a coNCePTuaL program from helloworld.ncptl ...
# Lexing ...
# Parsing ...
# Analyzing program semantics ...
# Compiling helloworld.ncptl using the c_mpi backend ...
# Compiling and linking the result ...
mpicc -I/tmp/ncptl/include -g -O2 tmpcFU2lS.c -L/tmp/ncptl/lib -lncptl -luuid -lm -lpopt -o helloworld
# Deleting tmpcFU2lS.c ...
# Files generated: helloworld

All we had to do was recompile with a different value for --backend to completely replace all socket calls with MPI calls. (If you like reading ugly code, you may want to compare and contrast the MPI version of helloworld.c file to the datagram version of helloworld.c.)

Summary of concepts

Let's review what we learned in this exercise:

  1. A task is coNCePTuaL's unit of control and typically corresponds to a process. A coNCePTuaL program comprises one or more tasks.
  2. Each task has a unique task number (starting from zero). A task can bind its task number to a variable and refer to it.
  3. All tasks execute concurrently with no implied order among them.
  4. The same coNCePTuaL source code can compile to different language and communication-library combinations merely by specifying an appropriate flag to the compiler.
  5. For convenience, generated executables automatically support a --help option.
Scott Pakin, pakin@lanl.gov