static int ws_push_task(struct starpu_task *task) { unsigned sched_ctx_id = task->sched_ctx; struct _starpu_work_stealing_data *ws = (struct _starpu_work_stealing_data*)starpu_sched_ctx_get_policy_data(sched_ctx_id); struct _starpu_deque_jobq *deque_queue; struct _starpu_job *j = _starpu_get_job_associated_to_task(task); int workerid = starpu_worker_get_id(); unsigned worker = 0; struct starpu_worker_collection *workers = starpu_sched_ctx_get_worker_collection(sched_ctx_id); struct starpu_sched_ctx_iterator it; workers->init_iterator(workers, &it); /* !! C'est ballot de tout locker! */ while(workers->has_next(workers, &it)) { worker = workers->get_next(workers, &it); starpu_pthread_mutex_t *sched_mutex; starpu_pthread_cond_t *sched_cond; starpu_worker_get_sched_condition(worker, &sched_mutex, &sched_cond); STARPU_PTHREAD_MUTEX_LOCK(sched_mutex); } /* If the current thread is not a worker but * the main thread (-1), we find the better one to * put task on its queue */ if (workerid == -1) workerid = select_worker(sched_ctx_id); deque_queue = ws->queue_array[workerid]; #ifdef HAVE_AYUDAME_H if (AYU_event) { intptr_t id = workerid; AYU_event(AYU_ADDTASKTOQUEUE, j->job_id, &id); } #endif _starpu_job_list_push_back(&deque_queue->jobq, j); deque_queue->njobs++; starpu_push_task_end(task); while(workers->has_next(workers, &it)) { worker = workers->get_next(workers, &it); starpu_pthread_mutex_t *sched_mutex; starpu_pthread_cond_t *sched_cond; starpu_worker_get_sched_condition(worker, &sched_mutex, &sched_cond); #ifndef STARPU_NON_BLOCKING_DRIVERS STARPU_PTHREAD_COND_SIGNAL(sched_cond); #endif STARPU_PTHREAD_MUTEX_UNLOCK(sched_mutex); } return 0; }
static int push_task_dummy(struct starpu_task *task) { unsigned sched_ctx_id = task->sched_ctx; struct dummy_sched_data *data = (struct dummy_sched_data*)starpu_sched_ctx_get_policy_data(sched_ctx_id); /* NB: In this simplistic strategy, we assume that the context in which we push task has at least one worker*/ /* lock all workers when pushing tasks on a list where all of them would pop for tasks */ starpu_pthread_mutex_lock(&data->policy_mutex); starpu_task_list_push_front(&data->sched_list, task); starpu_push_task_end(task); starpu_pthread_mutex_unlock(&data->policy_mutex); /*if there are no tasks block */ /* wake people waiting for a task */ unsigned worker = 0; struct starpu_worker_collection *workers = starpu_sched_ctx_get_worker_collection(sched_ctx_id); struct starpu_sched_ctx_iterator it; workers->init_iterator(workers, &it); while(workers->has_next(workers, &it)) { worker = workers->get_next(workers, &it); starpu_pthread_mutex_t *sched_mutex; starpu_pthread_cond_t *sched_cond; starpu_worker_get_sched_condition(worker, &sched_mutex, &sched_cond); starpu_pthread_mutex_lock(sched_mutex); starpu_pthread_cond_signal(sched_cond); starpu_pthread_mutex_unlock(sched_mutex); } return 0; }
/** * Return a worker from which a task can be stolen. * Selecting a worker is done in a round-robin fashion, unless * the worker previously selected doesn't own any task, * then we return the first non-empty worker. */ static unsigned select_victim_round_robin(unsigned sched_ctx_id) { struct _starpu_work_stealing_data *ws = (struct _starpu_work_stealing_data*)starpu_sched_ctx_get_policy_data(sched_ctx_id); unsigned worker = ws->last_pop_worker; unsigned nworkers = starpu_sched_ctx_get_nworkers(sched_ctx_id); starpu_pthread_mutex_t *victim_sched_mutex; starpu_pthread_cond_t *victim_sched_cond; /* If the worker's queue is empty, let's try * the next ones */ while (1) { unsigned njobs; starpu_worker_get_sched_condition(worker, &victim_sched_mutex, &victim_sched_cond); /* Here helgrind would shout that this is unprotected, but we * are fine with getting outdated values, this is just an * estimation */ njobs = ws->queue_array[worker]->njobs; if (njobs) break; worker = (worker + 1) % nworkers; if (worker == ws->last_pop_worker) { /* We got back to the first worker, * don't go in infinite loop */ break; } } ws->last_pop_worker = (worker + 1) % nworkers; return worker; }
/* Note: this is not scalable work stealing, use lws instead */ static struct starpu_task *ws_pop_task(unsigned sched_ctx_id) { struct _starpu_work_stealing_data *ws = (struct _starpu_work_stealing_data*)starpu_sched_ctx_get_policy_data(sched_ctx_id); struct starpu_task *task; struct _starpu_deque_jobq *q; int workerid = starpu_worker_get_id(); STARPU_ASSERT(workerid != -1); q = ws->queue_array[workerid]; task = _starpu_deque_pop_task(q, workerid); if (task) { /* there was a local task */ ws->performed_total++; q->nprocessed++; q->njobs--; return task; } starpu_pthread_mutex_t *worker_sched_mutex; starpu_pthread_cond_t *worker_sched_cond; starpu_worker_get_sched_condition(workerid, &worker_sched_mutex, &worker_sched_cond); /* Note: Releasing this mutex before taking the victim mutex, to avoid interlock*/ STARPU_PTHREAD_MUTEX_UNLOCK(worker_sched_mutex); /* we need to steal someone's job */ unsigned victim = select_victim(sched_ctx_id); starpu_pthread_mutex_t *victim_sched_mutex; starpu_pthread_cond_t *victim_sched_cond; starpu_worker_get_sched_condition(victim, &victim_sched_mutex, &victim_sched_cond); STARPU_PTHREAD_MUTEX_LOCK(victim_sched_mutex); struct _starpu_deque_jobq *victimq = ws->queue_array[victim]; task = _starpu_deque_pop_task(victimq, workerid); if (task) { _STARPU_TRACE_WORK_STEALING(q, workerid); ws->performed_total++; /* Beware : we have to increase the number of processed tasks of * the stealer, not the victim ! */ q->nprocessed++; victimq->njobs--; } STARPU_PTHREAD_MUTEX_UNLOCK(victim_sched_mutex); STARPU_PTHREAD_MUTEX_LOCK(worker_sched_mutex); if(!task) { task = _starpu_deque_pop_task(q, workerid); if (task) { /* there was a local task */ ws->performed_total++; q->nprocessed++; q->njobs--; return task; } } return task; }