# IBM Visualization Data Explorer Programmer's Reference

[ Bottom of Page | Previous Page | Next Page | Table of Contents | Partial Table of Contents | Index ]

# Chapter 10. Writing Modules for a Parallel Environment

Partial Table-of-Contents

- 10.1 A Parallel Version of the Add Module
- 10.2 A Parallel Version of the AverageCell Module

  Writing a "parallel" module involves considerations beyond those encountered in using the Module Builder.

  ## 10.1 A Parallel Version of the Add Module

  The Add module created in the example in 5.1 , "Add Module Example--Add a Number to Every Data Value" would work correctly on partitioned data because the code generated by the Module Builder automatically provides recursive traversal. However, it would not run in parallel on a parallel-architecture machine. To create an "addparallel" module, copy the following files to the directory you want to work in:

      /usr/local/dx/samples/program_guide/Makefile_workstation
      /usr/local/dx/samples/program_guide/add_parallel.c
      /usr/local/dx/samples/program_guide/addpar.mdf

  Now rename the makefile to `Makefile` and enter `make add_par`.

  To run this module in Data Explorer (from the directory to which the files were copied), enter:

      dx  -mdf ./addpar.mdf -exec ./dxexec

  This command starts Data Explorer (the `addpar.mdf` file tells the graphical user interface about AddParallel and its inputs and outputs).

  You can now run any visual program that uses the AddParallel module. One such program is `/usr/local/dx/samples/program_guide/add_parallel.net` .

  The AddParallel module:

  - Encapsulates the Field-level processing in the subroutine `task` in this example.

  - Calls `DXCreateTaskGroup` just before recursively traversing the Object in `m_AddParallel`.

  - Adds the tasks for processing the Fields during recursive traversal by calling `DXAddTask`.

  - Calls `DXExecuteTaskGroup` just after recursive traversal. At this point, the tasks that are defined will be scheduled on multiple processors. If any of the tasks returns an error, that error will be returned from `DXExecuteTaskGroup`.

      01   #include <dx/dx.h>
      02
      03   static Error DoAdd(Object o, float x);
      04
      05   m_AddParallel(Object *in, Object *out)
      06   {
      07       Object o = NULL;
      08       float x;

  Copy the structure of `in[0]`.

      09       if (!in[0])
      10           DXErrorGoto(ERROR_BAD_PARAMETER, "missing object");
      11       o = DXCopy(in[0], COPY_STRUCTURE);
      12       if (!o)
      13           goto error;

  Extract floating-point parameter from `in[1]` (default 0).

      14       if (!in[1])
      15           x = 0;
      16       else if (!DXExtractFloat(in[1], &x))
      17           DXErrorGoto(ERROR_BAD_PARAMETER, "bad addend");

  Create the task Group, call `DoAdd()` for recursive traversal, and then execute the task Group.

      18        DXCreateTaskGroup();
      19        if (!DoAdd(o, x)) {
      20          DXAbortTaskGroup()
      21          goto error;
      22        }
      23        if (!DXExecuteTaskGroup())
      24          goto error;

  A successful return or return on error.

      25        out[0] = o;
      26        return OK;
      27
      28    error:
      29        DXDelete(o);
      30        return ERROR;
      31    }
      32
      33

  The argument block for passing parameters to the task routine:

      34    struct arg {
      35        Field field;
      36        float x;
      37    }

  The following task routine does the actual work of processing a Field. `DXAddTask` instructs the executive to call this routine once for each Field. The executive will pass to `task` the argument block pointer that was specified when `DXAddTask` itself was called.

      01
      02   static Error
      03   task(Pointer p)
      04   {
      05       struct arg *arg = (struct arg *)p;
      06       Field field;
      07       float x, *from, *to;
      08       int i, n;
      09       Array a;

  Extract the arguments.

      10       field = arg->field;
      11       x = arg->x;

  Extract, typecheck, and get the data from the "data" component.

      12       a = (Array) DXGetComponentValue(field, "data");
      13       if (!a)
      14           DXErrorReturn(ERROR_MISSING_DATA, "field has no data");
      15       if (!DXTypeCheck(a, TYPE_FLOAT, CATEGORY_REAL, 0))
      16           DXErrorReturn(ERROR_BAD_TYPE, "data is not floating point");
      17       from = (float *) DXGetArrayData(a);

  Create a new Array, allocate space to it, and put it in the Field.

      18       DXGetArrayInfo(a, &n, NULL, NULL, NULL, NULL);
      19       a = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 0);
      20       if (!DXAddArrayData(a, 0, n, NULL))
      21           return ERROR;
      22       to = (float *) DXGetArrayData(a);
      23       DXSetComponentValue(field, "data", (Object)a);

  The following loop adds `x` to obtain the result.

      24       for (i=0; i<n; i++)
      25           to[i] = from[i] + x;

  Clean up the Field.

      26           DXChangedComponentValues(field, "data");
      27           DXEndField(field);
      28
      29           return OK;
      30       }

  The recursive traversal routine follows. Note that at this point (and for each Field) it does not process the Field but calls `DXAddTask`, specifying the routine that will be called in parallel to do the actual work.

  The Data Explorer programming interface is designed so that, in general, the programmer does need to use explicit locks. For information about local and global memory allocation, see 13.3 , "Memory Allocation".

      01   static
      02   Error
      03   DoAdd(Object o, float x)
      04   {
      05       struct arg arg;
      06       int i, n;
      07       Object oo;

  Determine the class of the object.

      08       switch (DXGetObjectClass(o)) {
      09
      10       case CLASS_FIELD:

  Add the task for this Field.

      11           arg.field = (Field)o;
      12           arg.x = x;
      13           if (!DXAddTask(task, &arg, sizeof arg, 0.0))
      14               return ERROR;
      15           break;
      16
      17       case CLASS_GROUP:

  Traverse Groups recursively.

      18            for (i=0; oo=DXGetEnumeratedMember((Group)o, i, NULL); i++)
      19                if (!DoAdd(oo, x))
      20                    return ERROR;
      21            break;
      22        }
      23
      24        return OK;
      25    }

  [ Top of Page | Previous Page | Next Page | Table of Contents | Partial Table of Contents | Index ]
  [Data Explorer Documentation | QuickStart Guide | User's Guide | User's Reference | Programmer's Reference | Installation and Configuration Guide ]

  [Data Explorer Home Page]

  [IBM Home Page | Order | Search | Contact IBM | Legal ]