There are instances when tasks are most naturally generated deep
inside nested loops. Often, this occurs in parallelizing existing
sequential applications.
In such circumstances, it may be difficult to
re-write the code to create a function GenerateTaskInput()
, since
that would require turning the loops inside out. (If you don't know what this
refers to, then you probably don't need the raw interface.)
On a first reading, you may wish to first look at the example for either a `for' loop or `while' loop, depending on the type of loop that you are parallelizing. Then return to the formal descriptions of the `TOP-C' raw functions. This chapter assumes familiarity with the basic concepts of section Overview of `TOP-C/C++' and section Writing `TOP-C' Applications.
TOPC_raw_submit_task_input(input)
serving
the role of GenerateTaskInput()
. The slave blocks inside
TOPC_raw_begin_master_slave()
and executes do_task() and
update_shared_data() until the master executes
TOPC_raw_end_master_slave()
. At that time, the slave
unblocks. The slave does nothing inside
TOPC_raw_end_master_slave()
.
TOPC_raw_begin_master_slave()
and TOPC_raw_end_master_slave()
; Typical usage is:
TOPC_raw_submit_task_input(TOPC_MSG(&input_data,
sizeof(input_data)) );
The argument, input, corresponds to what would be returned by
GenerateTaskInput()
in the routine TOPC_master_slave()
.
input will be processed by DoTask()
and its siblings,
just as in TOPC_master_slave()
).
There can be multiple occurrences of
TOPC_raw_submit_task_input()
.
TOPC_raw_begin_master_slave()
and
TOPC_raw_end_master_slave()
;
If no tasks are outstanding, returns false immediately. Otherwise, it
blocks until a task does return. It calls application
callback, CheckTaskResult()
, and then returns true.
Assume that we are parallelizing a code fragment of the following form.
The variables i
and j
will be the input to
DoTask()
, and any data structures indexed by i
and
j
(for example array
in array[i][j]
) will be
part of the shared data.
float array[ROWS][COLS]; ... for ( i = 0; i < 10; i++ ) { for ( j = 0; j < 10; j++ ) { /* do_task: */ ... /* update: */ array[i][j] = ...; } }
Assume that the labels do_task
and update
above
correspond to the callback functions DoTask()
and
UpdateSharedData()
. Then the code is parallelized below.
float array[ROWS][COLS]; typedef struct {int i_val; int j_val;} input_t; void *DoTask(input_t *buf) { int i = (*buf).i_val, j = (*buf).j_val; /* do_task: */ ... } void *CheckTaskResult(input_t *buf, output_t *buf2) { /* update: */ array[i][j] = ...; return NO_ACTION; } main(int argc, char **argv) { TOPC_init( &argc, &argv ); TOPC_raw_begin_master_slave(DoTask, CheckTaskResult, UpdateSharedData); if (TOPC_is_master()) { for ( i = 0; i < 10; i++ ) { for ( j = 0; j < 10; j++ ) { input_t input; input.i_val = i; input.j_val = j; TOPC_raw_submit_task_input( TOPC_MSG(&input, sizeof(input)) ); } } } TOPC_raw_end_master_slave(); TOPC_finalize(); }
Assume that we are parallelizing a code fragment of the following form
and input
is a pointer.
while ( (input = next_input()) != NULL ) { /* do_task: */ ... /* update: */ ... }
Assume that the labels do_task
and update
above
correspond to the callback functions DoTask()
and
UpdateSharedData()
. Then the code is parallelized below,
where input_size
must be specified by the application before it
is used.
TOPC_init( &argc, &argv ); TOPC_raw_begin_master_slave(DoTask, CheckTaskResult, UpdateSharedData); if (TOPC_is_master()) { while ( (input = next_input()) != NULL || TOPC_raw_wait_for_task_result() ) { TOPC_raw_submit_task_input( TOPC_MSG(input, input_size) ); } } TOPC_raw_end_master_slave(); TOPC_finalize();
Note that the code inside the raw begin/end block is executed only by the master in the code above.
If the buffer, input
, contains pointers to other data, then you
will need to marshal the data before calling TOPC_MSG()
.
See section Task Input and Task Output Buffers.
Go to the first, previous, next, last section, table of contents.