/* select a worker to pull from */ struct thorium_worker *thorium_worker_pool_select_worker_for_message(struct thorium_worker_pool *pool) { int index; int i; int score; struct thorium_worker *worker; int attempts; struct thorium_worker *best_worker; int best_score; #ifdef THORIUM_WORKER_POOL_USE_COUNT_CACHE int best_index; best_index = -1; #endif best_score = 0; best_worker = NULL; i = 0; attempts = THORIUM_WORKER_POOL_MESSAGE_SCHEDULING_WINDOW; /* select thet worker with the most messages in the window. */ while (i < attempts) { index = pool->worker_for_message; pool->worker_for_message = thorium_worker_pool_next_worker(pool, index); worker = thorium_worker_pool_get_worker(pool, index); #ifdef THORIUM_WORKER_POOL_USE_COUNT_CACHE score = thorium_worker_pool_get_cached_value(pool, index); #else score = -1; #endif /* Update the cache. * This is expensive because it will touch the cache line. * Only the worker is increasing the number of messages, and * only the worker pool is decreasing it. * As long as the cached value is greater than 0, then there is * definitely something to pull without the need * to break the CPU cache line * */ /* always update cache because otherwise there will be * starvation */ if (1 || score == 0) { score = thorium_worker_get_message_production_score(worker); #ifdef THORIUM_WORKER_POOL_USE_COUNT_CACHE thorium_worker_pool_set_cached_value(pool, index, score); #endif } if (best_worker == NULL || score > best_score) { best_worker = worker; best_score = score; #ifdef THORIUM_WORKER_POOL_USE_COUNT_CACHE best_index = index; #endif } ++i; } #ifdef THORIUM_WORKER_POOL_USE_COUNT_CACHE /* Update the cached value for the winning worker to have an * accurate value for this worker. */ core_vector_set_int(&pool->message_count_cache, best_index, best_score - 1); #endif return best_worker; }
int thorium_balancer_select_worker_least_busy( struct thorium_balancer *self, int *worker_score) { int to_check; int score; int best_score; struct thorium_worker *worker; struct thorium_worker *best_worker; int selected_worker; #if 0 int last_worker_score; #endif #ifdef THORIUM_WORKER_DEBUG int tag; int destination; struct thorium_message *message; #endif best_worker = NULL; best_score = 99; to_check = THORIUM_SCHEDULER_WORK_SCHEDULING_WINDOW; while (to_check--) { /* * get the worker to test for this iteration. */ worker = thorium_worker_pool_get_worker(self->pool, self->worker_for_work); score = thorium_worker_get_epoch_load(worker); #ifdef THORIUM_WORKER_POOL_DEBUG_ISSUE_334 if (score >= THORIUM_WORKER_WARNING_THRESHOLD && (self->last_scheduling_warning == 0 || score >= self->last_scheduling_warning + THORIUM_WORKER_WARNING_THRESHOLD_STRIDE)) { printf("Warning: node %d worker %d has a scheduling score of %d\n", thorium_node_name(thorium_worker_pool_get_node(self->pool)), self->worker_for_work, score); self->last_scheduling_warning = score; } #endif /* if the worker is not busy and it has no work to do, * select it right away... */ if (score == 0) { best_worker = worker; best_score = 0; break; } /* Otherwise, test the worker */ if (best_worker == NULL || score < best_score) { best_worker = worker; best_score = score; } /* * assign the next worker */ self->worker_for_work = thorium_worker_pool_next_worker(self->pool, self->worker_for_work); } #ifdef THORIUM_WORKER_POOL_DEBUG message = biosal_work_message(work); tag = thorium_message_action(message); destination = thorium_message_destination(message); if (tag == ACTION_ASK_TO_STOP) { printf("DEBUG dispatching ACTION_ASK_TO_STOP for actor %d to worker %d\n", destination, *start); } #endif selected_worker = self->worker_for_work; /* * assign the next worker */ self->worker_for_work = thorium_worker_pool_next_worker(self->pool, self->worker_for_work); *worker_score = best_score; /* This is a best effort algorithm */ return selected_worker; }