Jump to content

malpp

From Hugo's wiki

malpp is a preprocessor abstraction layer for libmalleable: it lets you make an MPI program malleable (able to change its number of processes while it runs) by adding comments above its loops.

Example

With malpp, a malleable sum of squares only needs directives:

long i = 0, iters = 10, sum = 0;

// mal: loop i->iters acc=sum
for (; i < iters; i++) {
    sum += i * i;
    // mal: check
}

// mal: single
printf("sum=%ld\n", sum);

Without it:

mal_init();

const long total = 10;
long i, limit, sum = 0;
MalFor f = mal_for(total, i, limit);
mal_attach_acc(f, sum);

for (; i < limit; i++) {
    sum += i * i;
    mal_check_for(f);
}

mal_finalize();

if (mal_rank() == 0)
    printf("sum=%ld\n", sum);

Building

malpp is a flex and bison program:

$ make        # builds ./malpp
$ make test   # also needs MPI; clones and builds libmalleable

Usage

It reads a file (or stdin) and writes the expanded source to stdout:

$ malpp program.c > program_malleable.c

Or from a Makefile:

program_malleable.c: program.c
    malpp $^ > $@
Tip Build the generated code with -O1 or higher: the dispatch scaffolding malpp emits is correct unoptimized, but only folds away with optimization.

Directives

mal: loop

// mal: loop i->iters declares a single linear loop. The variables are declared outside, the iterator starts at zero, and // mal: check must appear in the body.

acc=var adds an accumulator for loops that count or sum. It must be a type libmalleable can reduce (int, long, long long, unsigned, unsigned long, float, double), and after the loop it is only meaningful on rank 0.

mal: single

// mal: single guards the statement below it so only rank 0 runs it.

mal: decide

// mal: decide=<func> sets a custom resize decision function. Guard its definition with #ifdef MALPP so the sequential build still compiles. // mal: decide=<path>:<func> loads it at runtime from a shared library instead, with no guard needed.

See also