gem5/splash2/codes/apps/radiosity/taskman.C
Sanchayan Maity 0f4b39775c Fix splash2 benchmark
During the last commit of splash2 benchmark it seems before committing
when we ran "make clean", it effectively undid what the patch at below
link did
http://www.capsl.udel.edu/splash/Download.html

Fix this since without this it is not possible to build the arcane
splash2 benchmark.
2017-04-26 21:33:02 +05:30

778 lines
23 KiB
C

/*************************************************************************/
/* */
/* Copyright (c) 1994 Stanford University */
/* */
/* All rights reserved. */
/* */
/* Permission is given to use, copy, and modify this software for any */
/* non-commercial purpose as long as this copyright notice is not */
/* removed. All other uses, including redistribution in whole or in */
/* part, are forbidden without prior written permission. */
/* */
/* This software is provided with absolutely no warranty and no */
/* support. */
/* */
/*************************************************************************/
/************************************************************************
*
* Task management.
*
* This module has the following functions.
* (1) Allocate/free a task object.
* (2) Enqueue/decuque a task object.
*
*************************************************************************/
#include <stdio.h>
EXTERN_ENV;
include(radiosity.h)
struct {
char pad1[PAGE_SIZE]; /* padding to avoid false-sharing
and allow page-placement */
long n_local_free_task ;
Task *local_free_task ;
long crnt_taskq_id ;
char pad2[PAGE_SIZE]; /* padding to avoid false-sharing
and allow page-placement */
} task_struct[MAX_PROCESSORS];
/***************************************************************************
****************************************************************************
*
* Methods for Task object
*
****************************************************************************
****************************************************************************/
/***************************************************************************
*
* process_tasks()
*
* Process tasks in the task queue. Task type is specified by the mask.
* Multiple task types may be specified by bit-oring the task type.
*
****************************************************************************/
#define QUEUES_VISITED (n_taskqueues)
#define DEQUEUE_TASK(q,v,p) (dequeue_task((q),(v),p))
void process_tasks(long process_id)
{
Task *t ;
t = DEQUEUE_TASK( taskqueue_id[process_id], QUEUES_VISITED, process_id ) ;
retry_entry:
while( t )
{
switch( t->task_type )
{
case TASK_MODELING:
process_model( t->task.model.model, t->task.model.type, process_id ) ;
break ;
case TASK_BSP:
define_patch( t->task.bsp.patch, t->task.bsp.parent, process_id ) ;
break ;
case TASK_FF_REFINEMENT:
ff_refine_elements( t->task.ref.e1, t->task.ref.e2, 0, process_id ) ;
break ;
case TASK_RAY:
process_rays( t->task.ray.e, process_id ) ;
break ;
case TASK_VISIBILITY:
visibility_task( t->task.vis.e, t->task.vis.inter,
t->task.vis.n_inter, t->task.vis.k, process_id ) ;
break ;
case TASK_RAD_AVERAGE:
radiosity_averaging( t->task.rad.e, t->task.rad.mode, process_id ) ;
break ;
default:
fprintf( stderr, "Panic:process_tasks:Illegal task type\n" );
}
/* Free the task */
free_task( t, process_id ) ;
/* Get next task */
t = DEQUEUE_TASK( taskqueue_id[process_id], QUEUES_VISITED, process_id ) ;
}
/* Barrier. While waiting for other processors to finish, poll the task
queues and resume processing if there is any task */
LOCK(global->pbar_lock);
/* Reset the barrier counter if not initialized */
if( global->pbar_count >= n_processors )
global->pbar_count = 0 ;
/* Increment the counter */
global->pbar_count++ ;
UNLOCK(global->pbar_lock);
/* barrier spin-wait loop */
while( global->pbar_count < n_processors )
{
/* Wait for a while and then retry dequeue */
if( _process_task_wait_loop() )
break ;
/* Waited for a while but other processors are still running.
Poll the task queue again */
t = DEQUEUE_TASK( taskqueue_id[process_id], QUEUES_VISITED, process_id ) ;
if( t )
{
/* Task found. Exit the barrier and work on it */
LOCK(global->pbar_lock);
global->pbar_count-- ;
UNLOCK(global->pbar_lock);
goto retry_entry ;
}
}
BARRIER(global->barrier, n_processors);
}
long _process_task_wait_loop()
{
long i ;
long finished = 0 ;
/* Wait for a while and then retry */
for( i = 0 ; i < 1000 && ! finished ; i++ )
{
if( ((i & 0xff) == 0) && ((volatile long)global->pbar_count >= n_processors) )
finished = 1 ;
}
return( finished ) ;
}
/***************************************************************************
*
* create_modeling_task()
* create_bsp_task()
* create_ff_refine_task()
* create_ray_task()
* create_visibility_task()
* create_radavg_task()
*
****************************************************************************/
void create_modeling_task(Model *model, long type, long process_id)
{
/* Implemented this way (routine just calls another routine)
for historical reasons */
process_model( model, type, process_id ) ;
return ;
}
void create_bsp_task(Patch *patch, Patch *parent, long process_id)
{
/* Implemented this way (routine just calls another routine) for historical reasons */
define_patch( patch, parent, process_id ) ;
return ;
}
void create_ff_refine_task(Element *e1, Element *e2, long level, long process_id)
{
Task *t ;
/* Check existing parallelism */
if( taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
{
/* Task queue is too long. Solve it immediately */
ff_refine_elements( e1, e2, level, process_id ) ;
return ;
}
/* Create a task */
t = get_task(process_id) ;
t->task_type = TASK_FF_REFINEMENT ;
t->task.ref.e1 = e1 ;
t->task.ref.e2 = e2 ;
t->task.ref.level = level ;
/* Put in the queue */
enqueue_task( taskqueue_id[process_id], t, TASK_INSERT ) ;
}
void create_ray_task(Element *e, long process_id)
{
/* Check existing parallelism */
if( ((e->n_interactions + e->n_vis_undef_inter)
< N_inter_parallel_bf_refine)
|| taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
{
/* Task size is small, or the queue is too long.
Solve it immediately */
process_rays( e, process_id ) ;
return ;
}
/* Put in the queue */
enqueue_ray_task( taskqueue_id[process_id], e, TASK_INSERT, process_id ) ;
}
void enqueue_ray_task(long qid, Element *e, long mode, long process_id)
{
Task *t ;
/* Create task object */
t = get_task(process_id) ;
t->task_type = TASK_RAY ;
t->task.ray.e = e ;
/* Put in the queue */
enqueue_task( qid, t, mode ) ;
}
void create_visibility_tasks(Element *e, void (*k)(), long process_id)
{
long n_tasks ;
long remainder ; /* Residue of MOD(total_undefs)*/
long i_cnt ;
Interaction *top, *tail ;
Task *t ;
long total_undefs = 0 ;
long tasks_created = 0 ;
/* Check number of hard problems */
for( top = e->vis_undef_inter ; top ; top = top->next )
if( top->visibility == VISIBILITY_UNDEF )
total_undefs++ ;
if( total_undefs == 0 )
{
/* No process needs to be created. Call the continuation
immediately */
(*k)( e, process_id ) ;
return ;
}
/* Check existing parallelism */
if( (total_undefs < N_visibility_per_task)
|| taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
{
/* Task size is small, or the queue is too long.
Solve it immediately. */
visibility_task( e, e->vis_undef_inter,
e->n_vis_undef_inter, k, process_id ) ;
return ;
}
/* Create multiple tasks. Hard problems (i.e. where visibility comp is
really necessary) are divided into 'n_tasks' groups by residue
number division (or Bresenham's DDA) */
/* Note: once the first task is enqueued, the vis-undef list may be
modified while other tasks are being created. So, any information
that is necessary in the for-loop must be read from the element
and saved locally */
n_tasks = (total_undefs + N_visibility_per_task - 1)
/ N_visibility_per_task ;
remainder = 0 ;
i_cnt = 0 ;
for( top = e->vis_undef_inter, tail = top ; tail ; tail = tail->next )
{
i_cnt++ ;
if( tail->visibility != VISIBILITY_UNDEF )
continue ;
remainder += n_tasks ;
if( remainder >= total_undefs )
{
/* Create a task */
/* For the last task, append following (easy) interactions
if there is any */
tasks_created++ ;
if( tasks_created >= n_tasks )
for( ; tail->next ; tail = tail->next, i_cnt++ ) ;
/* Set task descriptor */
t = get_task(process_id) ;
t->task_type = TASK_VISIBILITY ;
t->task.vis.e = e ;
t->task.vis.inter = top ;
t->task.vis.n_inter = i_cnt ;
t->task.vis.k = k ;
/* Enqueue */
enqueue_task( taskqueue_id[process_id], t, TASK_INSERT ) ;
/* Update pointer and the residue variable */
top = tail->next ;
remainder -= total_undefs ;
i_cnt = 0 ;
}
}
}
void create_radavg_task(Element *e, long mode, long process_id)
{
/* Check existing parallelism */
if( (e->n_interactions < N_inter_parallel_bf_refine)
|| taskq_too_long(&global->task_queue[ taskqueue_id[process_id] ]) )
{
/* Task size is too small or queue is too long.
Solve it immediately */
radiosity_averaging( e, mode, process_id ) ;
return ;
}
/* Put in the queue */
enqueue_radavg_task( taskqueue_id[process_id], e, mode, process_id ) ;
}
void enqueue_radavg_task(long qid, Element *e, long mode, long process_id)
{
Task *t ;
/* Create task object */
t = get_task(process_id) ;
t->task_type = TASK_RAD_AVERAGE ;
t->task.rad.e = e ;
t->task.rad.mode = mode ;
/* Put in the queue */
enqueue_task( qid, t, TASK_INSERT ) ;
}
/***************************************************************************
*
* enqueue_task()
* dequeue_task()
*
****************************************************************************/
void enqueue_task(long qid, Task *task, long mode)
{
Task_Queue *tq ;
tq = &global->task_queue[ qid ] ;
/* Lock the task queue */
LOCK(tq->q_lock);
if( tq->tail == 0 )
{
/* The first task in the queue */
tq->tail = task ;
tq->top = task ;
tq->n_tasks = 1 ;
}
else
{
/* Usual case */
if( mode == TASK_APPEND )
{
tq->tail->next = task ;
tq->tail = task ;
tq->n_tasks++ ;
}
else
{
task->next = tq->top ;
tq->top = task ;
tq->n_tasks++ ;
}
}
/* Unlock the task queue */
UNLOCK(tq->q_lock);
}
Task *dequeue_task(long qid, long max_visit, long process_id)
/*
* Attempts to dequeue first from the specified queue (qid), but if no
* task is found the routine searches max_visit other queues and returns
* a task. If a task is taken from another queue, the task is taken from
* the tail of the queue (usually, larger amount of work is involved than
* the task at the top of the queue and more locality can be exploited
* within the stolen task).
*/
{
Task_Queue *tq ;
Task *t = 0 ;
Task *prev ;
long visit_count = 0 ;
long sign = -1 ; /* The first retry will go backward */
long offset ;
/* Check number of queues to be visited */
if( max_visit > n_taskqueues )
max_visit = n_taskqueues ;
/* Get next task */
while( visit_count < max_visit )
{
/* Select a task queue */
tq = &global->task_queue[ qid ] ;
/* Check the length (test-test&set) */
if( tq->n_tasks > 0 )
{
/* Lock the task queue */
LOCK(tq->q_lock);
if( tq->top )
{
if( qid == taskqueue_id[process_id] )
{
t = tq->top ;
tq->top = t->next ;
if( tq->top == 0 )
tq->tail = 0 ;
tq->n_tasks-- ;
}
else
{
/* Get tail */
for( prev = 0, t = tq->top ; t->next ;
prev = t, t = t->next ) ;
if( prev == 0 )
tq->top = 0 ;
else
prev->next = 0 ;
tq->tail = prev ;
tq->n_tasks-- ;
}
}
/* Unlock the task queue */
UNLOCK(tq->q_lock);
break ;
}
/* Update visit count */
visit_count++ ;
/* Compute next taskqueue ID */
offset = (sign > 0)? visit_count : -visit_count ;
sign = -sign ;
qid += offset ;
if( qid < 0 )
qid += n_taskqueues ;
else if( qid >= n_taskqueues )
qid -= n_taskqueues ;
}
return( t ) ;
}
/***************************************************************************
*
* get_task() Create a new instance of Task
* free_task() Free a Task object
*
****************************************************************************/
Task *get_task(long process_id)
{
Task *p ;
Task_Queue *tq ;
long i ;
long q_id ;
long retry_count = 0 ;
/* First, check local task queue */
if( task_struct[process_id].local_free_task == 0 )
{
/* If empty, allocate task objects from the shared list */
q_id = taskqueue_id[process_id] ;
while( task_struct[process_id].local_free_task == 0 )
{
tq = &global->task_queue[ q_id ] ;
if( tq->n_free > 0 )
{
LOCK(tq->f_lock);
if( tq->free )
{
/* Scan the free list */
for( i = 1, p = tq->free ;
(i < N_ALLOCATE_LOCAL_TASK) && p->next ;
i++, p = p->next ) ;
task_struct[process_id].local_free_task = tq->free ;
task_struct[process_id].n_local_free_task = i ;
tq->free = p->next ;
tq->n_free -= i ;
p->next = 0 ;
UNLOCK(tq->f_lock);
break ;
}
UNLOCK(tq->f_lock);
}
/* Try next task queue */
if( ++q_id >= n_taskqueues )
q_id = 0 ;
/* Check retry count */
if( ++retry_count > MAX_TASKGET_RETRY )
{
fprintf( stderr, "Panic(P%ld):No free task\n",
process_id ) ;
fprintf( stderr, " Local %ld\n", task_struct[process_id].n_local_free_task ) ;
fprintf( stderr, " Q0 free %ld\n",
global->task_queue[0].n_free ) ;
fprintf( stderr, " Q0 task %ld\n",
global->task_queue[0].n_tasks ) ;
exit(1) ;
}
}
}
/* Delete from the queue */
p = task_struct[process_id].local_free_task ;
task_struct[process_id].local_free_task = p->next ;
task_struct[process_id].n_local_free_task-- ;
/* Clear pointer just in case.. */
p->next = 0 ;
return( p ) ;
}
void free_task(Task *task, long process_id)
{
Task_Queue *tq ;
Task *p, *top ;
long i ;
/* Insert to the local queue */
task->next = task_struct[process_id].local_free_task ;
task_struct[process_id].local_free_task = task ;
task_struct[process_id].n_local_free_task++ ;
/* If local list is too long, export some tasks */
if( task_struct[process_id].n_local_free_task >= (N_ALLOCATE_LOCAL_TASK * 2) )
{
tq = &global->task_queue[ taskqueue_id[process_id] ] ;
for( i = 1, p = task_struct[process_id].local_free_task ;
i < N_ALLOCATE_LOCAL_TASK ; i++, p = p->next ) ;
/* Update local list */
top = task_struct[process_id].local_free_task ;
task_struct[process_id].local_free_task = p->next ;
task_struct[process_id].n_local_free_task -= i ;
/* Insert in the shared list */
LOCK(tq->f_lock);
p->next = tq->free ;
tq->free = top ;
tq->n_free += i ;
UNLOCK(tq->f_lock);
}
}
/***************************************************************************
*
* init_taskq()
*
* Initialize task free list and the task queue.
* This routine must be called when only one process is active.
*
****************************************************************************/
void init_taskq(long process_id)
{
long i ;
long qid ;
long task_index = 0 ;
long task_per_queue ;
long n_tasks ;
/* Reset task assignment index */
task_struct[process_id].crnt_taskq_id = 0 ;
/* Initialize task queues */
task_per_queue = (MAX_TASKS + n_taskqueues - 1) / n_taskqueues ;
for( qid = 0 ; qid < n_taskqueues ; qid++ )
{
/* Initialize free list */
if (task_index + task_per_queue > MAX_TASKS )
n_tasks = MAX_TASKS - task_index ;
else
n_tasks = task_per_queue ;
for( i = task_index ; i < task_index + n_tasks - 1 ; i++ )
global->task_buf[i].next = &global->task_buf[i+1] ;
global->task_buf[ i ].next = 0 ;
global->task_queue[ qid ].free = &global->task_buf[ task_index ] ;
global->task_queue[ qid ].n_free = n_tasks ;
/* Initialize task queue */
global->task_queue[ qid ].top = 0 ;
global->task_queue[ qid ].tail = 0 ;
global->task_queue[ qid ].n_tasks = 0 ;
/* Initialize locks */
LOCKINIT(global->task_queue[ qid ].q_lock);
LOCKINIT(global->task_queue[ qid ].f_lock);
/* Update index for next queue */
task_index += n_tasks ;
}
/* Initialize local free lists */
task_struct[process_id].n_local_free_task = 0 ;
task_struct[process_id].local_free_task = 0 ;
}
/***************************************************************************
*
* check_task_counter()
*
* Check task counter and return TRUE if this is the first task.
*
****************************************************************************/
long check_task_counter()
{
long flag = 0 ;
LOCK(global->task_counter_lock);
if( global->task_counter == 0 )
/* First processor */
flag = 1 ;
global->task_counter++ ;
if( global->task_counter >= n_processors )
global->task_counter = 0 ;
UNLOCK(global->task_counter_lock);
return( flag ) ;
}
/***************************************************************************
*
* assign_taskq()
*
* Assign process its task queue.
*
****************************************************************************/
long assign_taskq(long process_id)
{
long qid ;
qid = task_struct[process_id].crnt_taskq_id++ ;
if( task_struct[process_id].crnt_taskq_id >= n_taskqueues )
task_struct[process_id].crnt_taskq_id = 0 ;
return( qid ) ;
}
/***************************************************************************
*
* print_task()
* print_taskq()
*
* Print contents of a task.
*
****************************************************************************/
void print_task(Task *task)
{
if( task == 0 )
{
printf( "Task (NULL)\n" ) ;
return ;
}
switch( task->task_type )
{
case TASK_MODELING:
printf( "Task (Model)\n" ) ;
break ;
case TASK_BSP:
printf( "Task (BSP)\n" ) ;
break ;
case TASK_FF_REFINEMENT:
printf( "Task (FF Refinement)\n" ) ;
break ;
case TASK_RAY:
printf( "Task (Ray) (patch ID %ld)\n",
task->task.ray.e->patch->seq_no ) ;
break ;
case TASK_VISIBILITY:
printf( "Task (Visibility) (patch ID %ld)\n",
task->task.vis.e->patch->seq_no ) ;
break ;
case TASK_RAD_AVERAGE:
printf( "Task (RadAvg)\n" ) ;
break ;
default:
fprintf( stderr, "Task(Illegal task type %ld)\n", task->task_type );
}
}
void print_taskq(Task_Queue *tq)
{
Task *t ;
printf( "TaskQ: %ld tasks in the queue\n", taskq_length(tq) ) ;
for( t = taskq_top(tq) ; t ; t = t->next )
{
printf( " " ) ;
print_task( t ) ;
}
}