3. Converse Master-Slave Library

3.1 Introduction

CMS is the implementation of the master-slave (or manager-worker or agenda) parallel programming paradigm on top of Converse .

3.2 Available Functions

Following functions are available in this library:

typedef int (*CmsWorkerFn) (void *, void *);
Prototype for the worker function. See below.

typedef int (*CmsConsumerFn) (void *, int);
Prototype for the consumer function. See below.

void CmsInit(CmsWorkerFn worker, int max);
This function must be called before firing any tasks for the workers. max is the largest possible number of tasks you will fire before calling either CmsAwaitResponses or CmsProcessResponses next. (So the system know how many it may have to buffer).

int worker(void *t, void **r)
The user writes this function. Its name does not have to be worker; It can be anything. worker can be any function that the use writes to perform the task on the slave processors. It must allocate and compute the response data structure, and return a pointer to it, by assigning to r; It must also return the size of the response data structure as its return value.

void CmsFireTask(int ref, void * t, int size)
Creates task to be worked on by a worker. The task description is pointed to by t, and goes on for size bytes. ref must be a unique serial number between 0 and max (see CmsInit).

void CmsAwaitResponses(void);
This call allows the system to use processor 0 as a worker. It returns after all the tasks have sent back their responses. The responses themselves can be extracted using CmsGetResponse.

void *CmsGetResponse(int ref);
Extracts the response associated with the reference number ref from the system's buffers.

void CmsProcessResponses(CmsConsumerFn consumer);
Instead of using CmsAwaitResponses/CmsGetResponse pair, you can use this call alone. It turns the control over to the CMS system on processor 0, so it can be used as a worker. As soon as a response is available on processor 0, cms calls the user specified consumer function with two parameters: the response (a void *) and an integer refnum. (Question: should the size of the response be passed as a parameter to the consumer? User can do that as an explicit field of the response themselves, if necessary.)

void CmsExit(void);
Must be called on all processors to terminate execution.

Once either CmsProcessResponses or CmsAwaitResponses returns, you may fire the next batch of tasks via CmsFireTask again.

3.3 Example Program

#include "cms.h"

#define MAX 10

typedef struct {
    float a;
} Task;

typedef struct {
    float result;
} Response;

Task t;

int worker(Task *t, Response **r)
    /* do work and generate a single response */
    int i;
    Task *t1;
    int k;

    CmiPrintf("%d: in worker %f \n", CmiMyPe(), t->a);
    *r = (Response *) malloc(sizeof(Response));
    (*r)->result = t->a * t->a;
    return sizeof(Response);

int consumer(Response * r, int refnum)
    CmiPrintf("consumer: response with refnum = %d is %f\n", refnum,

main(int argc, char *argv[])
    int i, j, k, ref;
    /* 2nd parameter is the max number of tasks 
     * fired before "awaitResponses"
    CmsInit((CmsWorkerFn)worker, 20);
    if (CmiMyPe() == 0) { /* I am the manager */
        CmiPrintf("manager inited\n");
        for (i = 0; i < 3; i++) { /* number of iterations or phases */
          /* prepare the next generation of problems to solve */
          /* then, fire the next batch of tasks for the worker */
            for (j = 0; j < 5; j++) {
                t.a = 10 * i + j;
                ref = j;  /* a ref number to associate with the task, */
                /* so that the reponse for this task can be identified. */
                CmsFireTask(ref, &t, sizeof(t));
          /* Now wait for the responses */
            CmsAwaitResponses();  /* allows proc 0 to be used as a worker. */
            /* Now extract the resoneses from the system */
            for (j = 0; j < 5; j++) {
                Response *r = (Response *) CmsGetResponse(j);
                CmiPrintf("Response %d is: %f \n", j, r->result);
          /* End of one mast-slave phase */
            CmiPrintf("End of phase %d\n", i);

    CmiPrintf("Now the consumerFunction mode\n");

    if (CmiMyPe() == 0) { /* I am the manager */
       for (i = 0; i < 3; i++) {
           t.a = 5 + i;
           CmsFireTask(i, &t, sizeof(t));
       /* Also allows proc. 0 to be used as a worker. 
        * In addition, responses will be processed on processor 0 
        * via the "consumer" function as soon as they are available