diff --git a/src/engine.c b/src/engine.c index 7c98d36d543c8e220c7d8aa283aec94968378676..c1c89fcd9410630efe1ef59079891324b3c15583 100644 --- a/src/engine.c +++ b/src/engine.c @@ -676,7 +676,7 @@ void engine_step ( struct engine *e ) { /* Check if all the kick1 threads have executed. */ for ( k = 0 ; k < e->sched.nr_tasks ; k++ ) if ( e->sched.tasks[k].type == task_type_kick1 && - e->sched.tasks[k].tic == 0 ) + e->sched.tasks[k].toc == 0 ) error( "Not all kick1 tasks completed." ); // for(k=0; k<10; ++k) diff --git a/src/queue.c b/src/queue.c index 8d5a7808c53c3ccfef82269040bad1f86da38568..9e59b40942e30c9a8382c8399cd3235857e9d7d8 100644 --- a/src/queue.c +++ b/src/queue.c @@ -87,16 +87,6 @@ void queue_insert ( struct queue *q , struct task *t ) { } else break; - /* for ( int k = q->count - 1 ; k > 0 && q->tasks[ q->tid[k-1] ].weight < q->tasks[ q->tid[k] ].weight ; k-- ) { - int temp = q->tid[k]; - q->tid[k] = q->tid[k-1]; - q->tid[k-1] = temp; - } */ - - /* Verify queue consistency. */ - /* for ( int k = 1 ; k < q->count ; k++ ) - if ( q->tasks[ q->tid[(k-1)/2] ].weight < q->tasks[ q->tid[k] ].weight ) - error( "Queue not heaped." ); */ /* Unlock the queue. */ if ( lock_unlock( &q->lock ) != 0 ) @@ -130,8 +120,8 @@ void queue_init ( struct queue *q , struct task *tasks ) { error( "Failed to init queue lock." ); } - - + + /** * @brief Get a task free of dependencies and conflicts. * @@ -142,10 +132,9 @@ void queue_init ( struct queue *q , struct task *tasks ) { struct task *queue_gettask ( struct queue *q , int qid , int blocking ) { - int k, temp, qcount, *qtid, type; + int k, temp, qcount, *qtid; lock_type *qlock = &q->lock; struct task *qtasks, *res = NULL; - struct cell *ci, *cj; /* If there are no tasks, leave immediately. */ if ( q->count == 0 ) @@ -168,35 +157,15 @@ struct task *queue_gettask ( struct queue *q , int qid , int blocking ) { /* Put a finger on the task. */ res = &qtasks[ qtid[k] ]; - ci = res->ci; - cj = res->cj; - type = res->type; /* Is this task blocked? */ if ( res->wait ) - continue; + error( "Enqueued waiting task." ); - /* Try to lock ci. */ - if ( type == task_type_self || - type == task_type_sort || - (type == task_type_sub && cj == NULL) ) { - if ( cell_locktree( ci ) != 0 ) - continue; - } - else if ( type == task_type_pair || (type == task_type_sub && cj != NULL) ) { - if ( ci->hold || cj->hold || ci->wait || cj->wait ) - continue; - if ( cell_locktree( ci ) != 0 ) - continue; - if ( cell_locktree( cj ) != 0 ) { - cell_unlocktree( ci ); - continue; - } - } + /* Try to lock the task and exit if successful. */ + if ( task_lock( res ) ) + break; - /* If we made it this far, we're safe. */ - break; - } /* loop over the task IDs. */ /* Did we get a task? */ @@ -205,11 +174,6 @@ struct task *queue_gettask ( struct queue *q , int qid , int blocking ) { /* Another one bites the dust. */ qcount = q->count -= 1; - /* Own the cells involved. */ - /* ci->super->owner = qid; - if ( cj != NULL ) - cj->super->owner = qid; */ - /* Swap this task with the last task and re-heap. */ if ( k < qcount ) { qtid[ k ] = qtid[ qcount ]; @@ -233,18 +197,6 @@ struct task *queue_gettask ( struct queue *q , int qid , int blocking ) { break; } } - /* qtid[ k ] = qtid[ qcount ]; - while ( k < qcount-1 && qtasks[ qtid[k+1] ].weight > qtasks[ qtid[k] ].weight ) { - temp = qtid[k]; - qtid[k] = qtid[k+1]; - qtid[k+1] = temp; - k += 1; - } */ - - /* Verify queue consistency. */ - /* for ( k = 1 ; k < q->count ; k++ ) - if ( q->tasks[ q->tid[(k-1)/2] ].weight < q->tasks[ q->tid[k] ].weight ) - error( "Queue not heaped." ); */ } else diff --git a/src/runner.c b/src/runner.c index b16bc895e97a0c7486260272b081ed603133f663..ccd5210999ef6c16c2b1f5f80faee901cd61d1fe 100644 --- a/src/runner.c +++ b/src/runner.c @@ -776,7 +776,7 @@ void *runner_main ( void *data ) { struct runner *r = (struct runner *)data; struct engine *e = r->e; struct scheduler *sched = &e->sched; - struct task *t; + struct task *t = NULL; struct cell *ci, *cj; /* Main loop. */ @@ -788,15 +788,20 @@ void *runner_main ( void *data ) { /* Loop while there are tasks... */ while ( 1 ) { - /* Get a task, how and from where depends on the policy. */ - TIMER_TIC - t = scheduler_gettask( sched , r->qid ); - TIMER_TOC(timer_gettask); + /* If there's no old task, try to get a new one. */ + if ( t == NULL ) { - /* Did I get anything? */ - if ( t == NULL ) - break; + /* Get the task. */ + TIMER_TIC + t = scheduler_gettask( sched , r->qid ); + TIMER_TOC(timer_gettask); + + /* Did I get anything? */ + if ( t == NULL ) + break; + } + /* Get the cells. */ ci = t->ci; cj = t->cj; @@ -855,8 +860,8 @@ void *runner_main ( void *data ) { error( "Unknown task type." ); } - /* We're done with this task. */ - scheduler_done( sched , t ); + /* We're done with this task, see if we get a next one. */ + t = scheduler_done( sched , t ); } /* main loop. */ diff --git a/src/scheduler.c b/src/scheduler.c index b47c0da7d52c3aa03f1bc1a14b8ed05f898748f0..569aad0fe839b53735781c610ba9870baa75a518 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -587,16 +587,16 @@ void scheduler_reweight ( struct scheduler *s ) { t->weight += __builtin_popcount( t->flags ) * t->ci->count * ( sizeof(int)*8 - __builtin_clz( t->ci->count ) ); break; case task_type_self: - t->weight += 2 * t->ci->count * t->ci->count; + t->weight += 10 * t->ci->count * t->ci->count; break; case task_type_pair: - t->weight += 2 * sid_scale[ t->flags ] * t->ci->count * t->cj->count; + t->weight += 10 * t->ci->count * t->cj->count * sid_scale[ t->flags ]; break; case task_type_sub: if ( t->cj != NULL ) - t->weight += 2 * sid_scale[ t->flags ] * t->ci->count * t->cj->count; + t->weight += 10 * t->ci->count * t->cj->count * sid_scale[ t->flags ]; else - t->weight += 2 * t->ci->count * t->ci->count; + t->weight += 10 * t->ci->count * t->ci->count; break; case task_type_ghost: if ( t->ci == t->ci->super ) @@ -669,8 +669,13 @@ void scheduler_enqueue ( struct scheduler *s , struct task *t ) { return; /* If this is an implicit task, just pretend it's done. */ - if ( t->implicit ) - scheduler_done( s , t ); + if ( t->implicit ) { + for ( int j = 0 ; j < t->nr_unlock_tasks ; j++ ) { + struct task *t2 = t->unlock_tasks[j]; + if ( atomic_dec( &t2->wait ) == 1 && !t2->skip ) + scheduler_enqueue( s , t2 ); + } + } /* Otherwise, look for a suitable queue. */ else { @@ -697,9 +702,6 @@ void scheduler_enqueue ( struct scheduler *s , struct task *t ) { /* If no previous owner, find the shortest queue. */ if ( qid < 0 ) qid = rand() % s->nr_queues; - /* for ( qid = 0 , int k = 1 ; k < s->nr_queues ; k++ ) - if ( s->queues[k].count < s->queues[qid].count ) - qid = k; */ /* Increase the waiting counter. */ atomic_inc( &s->waiting ); @@ -719,7 +721,7 @@ void scheduler_enqueue ( struct scheduler *s , struct task *t ) { * @param t The finished #task. */ -void scheduler_done ( struct scheduler *s , struct task *t ) { +void scheduler_done_old ( struct scheduler *s , struct task *t ) { int k, res; struct task *t2; @@ -761,6 +763,63 @@ void scheduler_done ( struct scheduler *s , struct task *t ) { } +/** + * @brief Take care of a tasks dependencies. + * + * @param s The #scheduler. + * @param t The finished #task. + * + * @return A pointer to the next task, if a suitable one has + * been identified. + */ + +struct task *scheduler_done ( struct scheduler *s , struct task *t ) { + + int k, res, oid = t->ci->super->owner; + struct task *t2, *next = NULL; + + /* Release whatever locks this task held. */ + if ( !t->implicit ) + task_unlock( t ); + + /* Loop through the dependencies and add them to a queue if + they are ready. */ + for ( k = 0 ; k < t->nr_unlock_tasks ; k++ ) { + t2 = t->unlock_tasks[k]; + if ( ( res = atomic_dec( &t2->wait ) ) < 1 ) + error( "Negative wait!" ); + if ( res == 1 && !t2->skip ) { + if ( !t2->implicit && + t2->ci->super->owner == oid && + ( next == NULL || t2->weight > next->weight ) && + task_lock( t2 ) ) { + if ( next != NULL ) { + task_unlock( next ); + scheduler_enqueue( s , next ); + } + next = t2; + } + else + scheduler_enqueue( s , t2 ); + } + } + + /* Task definitely done. */ + if ( !t->implicit ) { + t->toc = getticks(); + pthread_mutex_lock( &s->sleep_mutex ); + if ( next == NULL ) + atomic_dec( &s->waiting ); + pthread_cond_broadcast( &s->sleep_cond ); + pthread_mutex_unlock( &s->sleep_mutex ); + } + + /* Return the next best task. */ + return next; + + } + + /** * @brief Get a task, preferably from the given queue. * diff --git a/src/scheduler.h b/src/scheduler.h index ee77aeafc00a96f492352eb2bbaeb381970d3828..7fcca102d4ef4ba9881cc96d129a229d24f03410 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -78,5 +78,5 @@ struct task *scheduler_addtask ( struct scheduler *s , int type , int subtype , void scheduler_splittasks ( struct scheduler *s ); void scheduler_map_mkghosts ( struct cell *c , void *data ); void scheduler_map_mkkick1 ( struct cell *c , void *data ); -void scheduler_done ( struct scheduler *s , struct task *t ); +struct task *scheduler_done ( struct scheduler *s , struct task *t ); diff --git a/src/task.c b/src/task.c index 1f27bc08401e609208ff85cef4c0afba2ce32ae8..549dbd2ee241547699bfe759007752f8afd1c5df 100644 --- a/src/task.c +++ b/src/task.c @@ -35,6 +35,7 @@ #include "cycle.h" #include "atomic.h" #include "lock.h" +#include "cell.h" #include "task.h" #include "error.h" @@ -42,6 +43,68 @@ const char *taskID_names[task_type_count] = { "none" , "sort" , "self" , "pair" , "sub" , "ghost" , "kick1" , "kick2" }; +/** + * @brief Unlock the cell held by this task. + * + * @param t The #task. + */ + +void task_unlock ( struct task *t ) { + + /* Act based on task type. */ + switch ( t->type ) { + case task_type_self: + case task_type_sort: + cell_unlocktree( t->ci ); + break; + case task_type_pair: + case task_type_sub: + cell_unlocktree( t->ci ); + if ( t->cj != NULL ) + cell_unlocktree( t->cj ); + break; + } + + } + + +/** + * @brief Try to lock the cells associated with this task. + * + * @param t the #task. + */ + +int task_lock ( struct task *t ) { + + int type = t->type; + struct cell *ci = t->ci, *cj = t->cj; + + /* Unary lock? */ + if ( type == task_type_self || + type == task_type_sort || + (type == task_type_sub && cj == NULL) ) { + if ( cell_locktree( ci ) != 0 ) + return 0; + } + + /* Otherwise, binary lock. */ + else if ( type == task_type_pair || (type == task_type_sub && cj != NULL) ) { + if ( ci->hold || cj->hold || ci->wait || cj->wait ) + return 0; + if ( cell_locktree( ci ) != 0 ) + return 0; + if ( cell_locktree( cj ) != 0 ) { + cell_unlocktree( ci ); + return 0; + } + } + + /* If we made it this far, we've got a lock. */ + return 1; + + } + + /** * @brief Remove all unlocks to tasks that are of the given type. * diff --git a/src/task.h b/src/task.h index fe22fcd3e87de602d56402cdafac9bada7ad65d1..41a5d16b2b687a2abaf74d0dc85dd72325c670b1 100644 --- a/src/task.h +++ b/src/task.h @@ -72,3 +72,5 @@ void task_rmunlock( struct task *ta , struct task *tb ); void task_rmunlock_blind( struct task *ta , struct task *tb ); void task_cleanunlock ( struct task *t , int type ); void task_addunlock( struct task *ta , struct task *tb ); +void task_unlock ( struct task *t ); +int task_lock ( struct task *t );