blob: 940adb34f98784fdf4665cd74ef1bd8a281d1843 [file] [log] [blame]
/*
* Implements dynamic task queues to provide load balancing
* Sanjeev Kumar --- December, 2004
*/
#ifndef __TASKQ_H__
#define __TASKQ_H__
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif
#define MAX_DIMENSION 3
__BEGIN_DECLS
// An identifier that uniquely identifies a thread. The TaskQThreadId for the
// main thread is 0. The TaskQThreadId ranges from 0 to "numThreads-1". So it
// can be used to index arrays.
typedef long TaskQThreadId;
// A task is a unit of work. A task has two componenets: A function (called
// "task function" and a set of arguments (called "task arguments"). They
// following are valid types for a task function. When the task is executed
// by the run time library, it executes the task function with the task's
// arguments. In addition, it provides a threadId.
// Note: The threadId that is provided to the task function (identity of the
// thread executing the task) is not necessarily the same as the threadId
// that was supplied to the taskQEnqueue function (which is the identity of
// the thread enqueuing the task).
typedef void *TaskQTask; // Generic task type
typedef void (*TaskQTask1) (TaskQThreadId threadId, void *arg1);
typedef void (*TaskQTask2) (TaskQThreadId threadId, void *arg1, void *arg2);
typedef void (*TaskQTask3) (TaskQThreadId threadId, void *arg1, void *arg2, void *arg3);
// These functions can be used to set the parameters in the task queue
// implementations. The different parameters affect different
// implementations. Setting a parameter that is specific a particular
// implementation is treated as a "nop" (as is therefore harmless) on the
// other implementations.
// These have to be called before TaskQInit to have an effect.
// Usually you don't have to use this function---the default values will work
// just fine.
enum TaskQParam { TaskQInvalidParam,
// parameters for Distributed implementations
TaskQThreadsPerQueue, // Number of threads per queue (default=1)
// End of parameters
TaskQNumParams
};
void taskQSetParam (enum TaskQParam param, long value);
long taskQGetParam (enum TaskQParam param);
// This function initializes the taskQ library and starts the worker threads.
// This has to be called before calling any other taskQ functions (Except the
// taskQ functions that set the various parameters).
// numThreads: Total number of parallel threads (including the main
// thread)
// maxTasks: The maximum number of tasks that can be in the taskQ at any
// point. Bad things happen when you specify this wrong (understate it).
// This should be executed only once and that too by the main thread.
void taskQInit (int numThreads, int maxTasks);
// These functions are used to enqueue a task. You have to call the
// appropriate function depending on whether your task function takes 1, 2,
// or 3 arguments.
// taskFunction: The task function
// threadId: This should be the TaskQThreadId of the thread that is
// enqueuing the task (as stated above). Note: It cannot be used to
// specify the thread you want the task to executed on.
// args[1..3]: The task arguments
void taskQEnqueueTask1 (TaskQTask1 taskFunction, TaskQThreadId threadId, void *arg1);
void taskQEnqueueTask2 (TaskQTask2 taskFunction, TaskQThreadId threadId, void *arg1, void *arg2);
void taskQEnqueueTask3 (TaskQTask3 taskFunction, TaskQThreadId threadId, void *arg1, void *arg2, void *arg3);
// This function is used to enqueue a set of tasks at once. Suppose
// you are operating on a grid (with at most MAX_DIMENSIONS). If you
// want to break down the grid into tiles where each tile is an
// independant task, you can enqueue all the tasks at once.
// taskFunction: The task function. To satisfy the C type system, a type
// cast should be used. For instance, if the numDimensions is 2, then
// a task function (say taskFn2) of type TaskQTask2 should be defined
// and cast into TaskQTask when it is supplied as an argument to
// taskQEnqueueGrid.
// numDimensions: The number of dimensions in the grid.
// dimensionSize: Size of the grid in each dimension. Only indices 0 to
// numDimensions-1 need to have valid values.
// tileSize: Size of the tile in each dimension. Only indices 0 to
// numDimensions-1 need to have valid values.
void taskQEnqueueGrid (TaskQTask taskFuntion, TaskQThreadId threadId, long numDimensions,
long dimensionSize[MAX_DIMENSION], long tileSize[MAX_DIMENSION]);
// This function is used to wait for all the tasks that have been enqueued to
// be compeleted.
// Implementation Note: Currently, the worker threads (as well as the main
// thread) start executing the tasks only after the taskQWait function is
// invoked by the main thread. This might change in the future and so a
// program should not rely on this for correctness. However, this is stated
// here because it is sometimes easier to understand the behavior and
// performance of the program when you know this.
// This should be executed only by the main thread.
void taskQWait (void);
// This function returns a rough estimate of the number of tasks
// that are currently queued up in the task queues.
// Note 1: This is just a hint.
// Note 2: This is currently implemented only for the Central Queues.
// Note 3: This is an experimental feature and might disappear in the future.
int taskQPendingTasksHint (void);
// This function performs clean up actions and should be the last taskQ
// function to be executed by the program.
// This should be executed only once and that too by the main thread.
void taskQEnd (void);
// These functions reset and print some statistics collected by taskQ library.
// This should be executed only by the main thread.
void taskQResetStats (void);
void taskQPrnStats (void);
__END_DECLS
// These macros can be used to eliminate task queueing code entirely from
// the program. This is useful to measure the performance of the serial
// version of the program without any task queuing overhead. These work only
// for the Baseline and the Grid flavors of task queues.
#ifdef SERIAL_TASKQ
#define taskQInit(nt,mt) { if ( nt != 1) { printf( "ERROR: Serial version. numThreads should be 1\n"); exit(1); } }
#define taskQEnqueueTask1(tf,tId,a) { (tf)(0,a); }
#define taskQEnqueueTask2(tf,tId,a,b) { (tf)(0,a,b); }
#define taskQEnqueueTask3(tf,tId,a,b,c) { (tf)(0,a,b,c); }
#define taskQWait()
#define taskQResetStats()
#define taskQPrintStats()
#define taskQEnd()
#define taskQEnqueueGrid(tf,tId,nd,max,step) { \
if ( MAX_DIMENSION != 3) { \
printf( "ERROR: Serial version not implemented for MAX_DIMENSION != 3.\n"); \
exit(1); \
} \
long __TQ__i, __TQ__j[MAX_DIMENSION], __TQ__max[MAX_DIMENSION], __TQ__step[MAX_DIMENSION]; \
for ( __TQ__i = 0; __TQ__i < MAX_DIMENSION; __TQ__i++) { \
if ( __TQ__i < nd) { \
__TQ__max[__TQ__i] = max[__TQ__i]; \
__TQ__step[__TQ__i] = step[__TQ__i]; \
} else { \
__TQ__max[__TQ__i] = 1; \
__TQ__step[__TQ__i] = 1; \
} \
} \
for ( __TQ__j[0] = 0; __TQ__j[0] < __TQ__max[0]; __TQ__j[0] += __TQ__step[0]) { \
for ( __TQ__j[1] = 0; __TQ__j[1] < __TQ__max[1]; __TQ__j[1] += __TQ__step[1]) { \
for ( __TQ__j[2] = 0; __TQ__j[2] < __TQ__max[2]; __TQ__j[2] += __TQ__step[2]) { \
tf( 0, ( void *)__TQ__j[0], ( void *)__TQ__j[1], ( void *)__TQ__j[2]); \
} \
} \
} \
}
#endif
#endif