void thorium_cfs_scheduler_print(struct thorium_scheduler *self) { struct thorium_cfs_scheduler *concrete_self; struct core_red_black_tree_iterator iterator; uint64_t virtual_runtime; struct thorium_actor *actor; int i; struct core_timer timer; core_timer_init(&timer); concrete_self = self->concrete_self; core_red_black_tree_iterator_init(&iterator, &concrete_self->tree); printf("[cfs_scheduler] %" PRIu64 " ns, timeline contains %d actors\n", core_timer_get_nanoseconds(&timer), core_red_black_tree_size(&concrete_self->tree)); i = 0; while (core_red_black_tree_iterator_get_next_key_and_value(&iterator, &virtual_runtime, &actor)) { printf("[%d] virtual_runtime= %" PRIu64 " actor= %s/%d\n", i, virtual_runtime, thorium_actor_script_name(actor), thorium_actor_name(actor)); ++i; } core_red_black_tree_iterator_destroy(&iterator); core_timer_destroy(&timer); }
void core_timer_stop(struct core_timer *timer) { timer->stop = core_timer_get_nanoseconds(timer); timer->stopped = 1; #ifdef CORE_TIMER_DEBUG fprintf(stderr, "TIMER start %" PRIu64 " stop %" PRIu64 "\n", stop, timer->start); #endif }
float thorium_worker_get_scheduling_epoch_load(struct thorium_worker *worker) { uint64_t end_time; uint64_t period; end_time = core_timer_get_nanoseconds(&worker->timer); period = end_time - worker->scheduling_epoch_start_in_nanoseconds; if (period == 0) { return 0; } return (0.0 + worker->scheduling_epoch_used_nanoseconds) / period; }
void thorium_transport_print_event(struct thorium_transport *self, int type, struct thorium_message *message) { char *description; int count; int source_rank; int destination_rank; uint64_t time; description = EVENT_STRING_SEND; if (type == EVENT_TYPE_RECEIVE) description = EVENT_STRING_RECEIVE; count = thorium_message_count(message); source_rank = thorium_message_source_node(message); destination_rank = thorium_message_destination_node(message); time = core_timer_get_nanoseconds(&self->timer); time -= self->start_time; printf("thorium_transport print_event time_nanoseconds= %" PRIu64 " type= %s source= %d destination= %d count= %d\n", time, description, source_rank, destination_rank, count); }
void thorium_worker_stop(struct thorium_worker *worker) { #ifdef THORIUM_WORKER_DEBUG thorium_worker_display(worker); printf("stopping worker!\n"); #endif /* * FLAG_DEAD is changed and will be read * by the running thread. * * Only one thread is changing this value, so no thread are needed. */ core_bitmap_set_bit_uint32_t(&worker->flags, FLAG_DEAD); /* Make the change visible to other threads too */ core_memory_fence(); /* Wake the worker **after** killing it. * So basically, there is a case where the worker is killed * while sleeping. But since threads are cool, the worker will * wake up, and die for real this time. */ if (worker->waiting_is_enabled) { /* * Wake up if necessary because the worker might be * waiting for something... */ thorium_worker_signal(worker); } core_thread_join(&worker->thread); worker->loop_end_in_nanoseconds = core_timer_get_nanoseconds(&worker->timer); }
void thorium_transport_init(struct thorium_transport *self, struct thorium_node *node, int *argc, char ***argv, struct core_memory_pool *inbound_message_memory_pool, struct core_memory_pool *outbound_message_memory_pool) { int actual_argc; char **actual_argv; self->active_request_count = 0; actual_argc = *argc; actual_argv = *argv; self->flags = 0; core_bitmap_clear_bit_uint32_t(&self->flags, FLAG_PROFILE); core_bitmap_clear_bit_uint32_t(&self->flags, FLAG_PRINT_TRANSPORT_EVENTS); self->transport_interface = NULL; self->concrete_transport = NULL; /* printf("DEBUG Initiating transport\n"); */ /* Select the transport layer */ /* * Assign functions */ thorium_transport_select_implementation(self, actual_argc, actual_argv); self->node = node; self->rank = -1; self->size = -1; if (self->transport_interface != NULL) { self->concrete_transport = core_memory_allocate(self->transport_interface->size, MEMORY_TRANSPORT); self->transport_interface->init(self, argc, argv); } CORE_DEBUGGER_ASSERT(self->rank >= 0); CORE_DEBUGGER_ASSERT(self->size >= 1); CORE_DEBUGGER_ASSERT(self->node != NULL); self->inbound_message_memory_pool = inbound_message_memory_pool; self->outbound_message_memory_pool = outbound_message_memory_pool; thorium_transport_profiler_init(&self->transport_profiler); if (core_command_has_argument(actual_argc, actual_argv, "-enable-transport-profiler")) { printf("Enable transport profiler\n"); core_bitmap_set_bit_uint32_t(&self->flags, FLAG_PROFILE); } if (self->rank == 0) { printf("thorium_transport: type %s\n", self->transport_interface->name); } if (core_command_has_argument(actual_argc, actual_argv, "-print-transport-events")) { core_bitmap_set_bit_uint32_t(&self->flags, FLAG_PRINT_TRANSPORT_EVENTS); } core_timer_init(&self->timer); self->start_time = core_timer_get_nanoseconds(&self->timer); }
void thorium_message_multiplexer_init(struct thorium_message_multiplexer *self, struct thorium_node *node, struct thorium_multiplexer_policy *policy) { int size; int i; /* int bytes; */ int position; struct thorium_multiplexed_buffer *multiplexed_buffer; int argc; char **argv; thorium_decision_maker_init(&self->decision_maker); self->policy = policy; self->original_message_count = 0; self->real_message_count = 0; CORE_BITMAP_CLEAR_FLAGS(self->flags); CORE_BITMAP_CLEAR_FLAG(self->flags, FLAG_DISABLED); #ifdef THORIUM_MULTIPLEXER_TRACK_BUFFERS_WITH_CONTENT core_set_init(&self->buffers_with_content, sizeof(int)); #endif core_timer_init(&self->timer); self->buffer_size_in_bytes = thorium_multiplexer_policy_size_threshold(self->policy); #ifdef CONFIG_MULTIPLEXER_USE_DECISION_MAKER self->timeout_in_nanoseconds = thorium_decision_maker_get_best_timeout(&self->decision_maker, THORIUM_TIMEOUT_NO_VALUE); #else self->timeout_in_nanoseconds = self->policy->threshold_time_in_nanoseconds; #endif CORE_DEBUGGER_ASSERT(self->timeout_in_nanoseconds >= 0); self->node = node; core_vector_init(&self->buffers, sizeof(struct thorium_multiplexed_buffer)); size = thorium_node_nodes(self->node); core_vector_resize(&self->buffers, size); /* bytes = size * self->buffer_size_in_bytes; */ #ifdef DEBUG_MULTIPLEXER thorium_printf("DEBUG_MULTIPLEXER size %d bytes %d\n", size, bytes); #endif position = 0; for (i = 0; i < size; ++i) { multiplexed_buffer = core_vector_at(&self->buffers, i); CORE_DEBUGGER_ASSERT(multiplexed_buffer != NULL); /* * Initially, these multiplexed buffers have a NULL buffer. * It is only allocated when needed because each worker is an exporter * of small messages for a subset of all the destination nodes. */ thorium_multiplexed_buffer_init(multiplexed_buffer, self->buffer_size_in_bytes, self->timeout_in_nanoseconds); position += self->buffer_size_in_bytes; #ifdef DEBUG_MULTIPLEXER1 thorium_printf("DEBUG_MULTIPLEXER thorium_message_multiplexer_init index %d buffer %p\n", i, buffer); #endif #ifdef DEBUG_MULTIPLEXER thorium_printf("DEBUG_MULTIPLEXER thorium_message_multiplexer_init (after) index %d buffer %p\n", i, core_vector_at(&self->buffers, i)); #endif } if (thorium_multiplexer_policy_is_disabled(self->policy)) { CORE_BITMAP_SET_FLAG(self->flags, FLAG_DISABLED); } if (thorium_node_nodes(self->node) < thorium_multiplexer_policy_minimum_node_count(self->policy)) { CORE_BITMAP_SET_FLAG(self->flags, FLAG_DISABLED); } self->worker = NULL; argc = node->argc; argv = node->argv; /* * Aside from the policy, the end user can also disable the multiplexer code path */ if (core_command_has_argument(argc, argv, OPTION_DISABLE_MULTIPLEXER)) { CORE_BITMAP_SET_FLAG(self->flags, FLAG_DISABLED); } self->last_send_event_count = 0; self->last_time = core_timer_get_nanoseconds(&self->timer); self->last_update_time = time(NULL); self->degree_of_aggregation_limit = self->policy->degree_of_aggregation_limit; thorium_router_init(&self->router, self->node->nodes, TOPOLOGY_POLYTOPE); if (thorium_node_must_print_data(self->node)) { thorium_router_print(&self->router); } }
/* * Returns 1 if the message was multiplexed. * * This is O(1) in regard to the number of thorium nodes. */ int thorium_message_multiplexer_multiplex(struct thorium_message_multiplexer *self, struct thorium_message *message) { /* * If buffer is full, use thorium_node_send_with_transport * * get count * * if count is below or equal to the threshold * multiplex the message. * return 1 * * return 0 */ int count; int current_size; int maximum_size; int action; struct core_memory_pool *pool; void *new_buffer; int new_count; void *buffer; int destination_node; int destination_actor; int new_size; int required_size; struct thorium_multiplexed_buffer *real_multiplexed_buffer; uint64_t time; int next_node_in_route; int source_node; int current_node; #ifdef DEBUG_MULTIPLEXER thorium_printf("multiplex\n"); thorium_message_print(message); #endif if (CORE_BITMAP_GET_FLAG(self->flags, FLAG_DISABLED)) { return 0; } action = thorium_message_action(message); CORE_DEBUGGER_ASSERT(action != ACTION_INVALID); #ifdef THORIUM_MULTIPLEXER_USE_ACTIONS_TO_SKIP /* * Don't multiplex already-multiplexed messages. */ if (thorium_multiplexer_policy_is_action_to_skip(self->policy, action)) { return 0; } #endif #ifdef CONFIG_MULTIPLEXER_USE_DECISION_MAKER thorium_message_multiplexer_update_timeout(self); #endif ++self->original_message_count; count = thorium_message_count(message); destination_node = thorium_message_destination_node(message); source_node = message->routing_source; current_node = self->node->name; next_node_in_route = thorium_router_get_next_rank_in_route(&self->router, source_node, current_node, destination_node); /* thorium_message_print(message); thorium_printf("router: source_node %d current_node %d next_node_in_route %d" " destination_node %d\n", source_node, current_node, next_node_in_route, destination_node); */ #ifdef CONFIG_USE_TOPOLOGY_AWARE_AGGREGATION /* * The next node in the route for this message is * next_node_in_route. */ destination_node = next_node_in_route; #endif CORE_DEBUGGER_ASSERT(source_node >= 0); real_multiplexed_buffer = core_vector_at(&self->buffers, destination_node); CORE_DEBUGGER_ASSERT(real_multiplexed_buffer != NULL); required_size = thorium_multiplexed_buffer_required_size(real_multiplexed_buffer, count); buffer = thorium_message_buffer(message); destination_actor = thorium_message_destination(message); #ifdef DEBUG_MULTIPLEXER thorium_printf("DEBUG multiplex count %d required_size %d action %x\n", count, required_size, action); #endif /* * Don't multiplex non-actor messages. */ if (destination_actor == THORIUM_ACTOR_NOBODY) { return 0; } #ifdef CORE_DEBUGGER_ASSERT if (real_multiplexed_buffer == NULL) { thorium_printf("Error action %d destination_node %d destination_actor %d\n", action, destination_node, destination_actor); } #endif current_size = thorium_multiplexed_buffer_current_size(real_multiplexed_buffer); maximum_size = thorium_multiplexed_buffer_maximum_size(real_multiplexed_buffer); /* * Don't multiplex large messages. */ if (required_size > maximum_size) { #ifdef DEBUG_MULTIPLEXER thorium_printf("too large required_size %d maximum_size %d\n", required_size, maximum_size); #endif return 0; } /* thorium_printf("MULTIPLEX_MESSAGE\n"); */ new_size = current_size + required_size; /* * Flush now if there is no space left for the <required_size> bytes */ if (new_size > maximum_size) { #ifdef DEBUG_MULTIPLEXER thorium_printf("thorium_message_multiplexer: must FLUSH thorium_message_multiplexer_multiplex required_size %d new_size %d maximum_size %d\n", required_size, new_size, maximum_size); #endif thorium_message_multiplexer_flush(self, destination_node, FORCE_YES_SIZE); current_size = thorium_multiplexed_buffer_current_size(real_multiplexed_buffer); CORE_DEBUGGER_ASSERT(current_size == 0); } time = core_timer_get_nanoseconds(&self->timer); /* * If the buffer is empty before adding the data, it means that it is not * in the list of buffers with content and it must be added. */ if (current_size == 0) { thorium_multiplexed_buffer_set_time(real_multiplexed_buffer, time); #ifdef THORIUM_MULTIPLEXER_TRACK_BUFFERS_WITH_CONTENT core_set_add(&self->buffers_with_content, &destination_node); #endif /* * Add it to the timeline. */ #ifdef THORIUM_MULTIPLEXER_USE_TREE core_red_black_tree_add_key_and_value(&self->timeline, &time, &destination_node); #elif defined(THORIUM_MULTIPLEXER_USE_HEAP) core_binary_heap_insert(&self->timeline, &time, &destination_node); #elif defined(THORIUM_MULTIPLEXER_USE_QUEUE) core_queue_enqueue(&self->timeline, &destination_node); #endif } /* * The allocation of buffer is lazy. * The current worker is an exporter of small message for the destination * "destination_node". */ if (thorium_multiplexed_buffer_buffer(real_multiplexed_buffer) == NULL) { pool = thorium_worker_get_outbound_message_memory_pool(self->worker); new_count = self->buffer_size_in_bytes + THORIUM_MESSAGE_METADATA_SIZE; new_buffer = core_memory_pool_allocate(pool, new_count); thorium_multiplexed_buffer_set_buffer(real_multiplexed_buffer, new_buffer); } /* thorium_printf("DEBUG worker_latency %d ns\n", thorium_worker_latency(self->worker)); */ thorium_multiplexed_buffer_append(real_multiplexed_buffer, count, buffer, time); /* * Try to flush. This only flushes something if the buffer is full. */ if (thorium_message_multiplexer_buffer_is_ready(self, real_multiplexed_buffer)) { /* * Try to flush here too. This is required in order to satisfy the * technical requirement of a DOA limit. * * Obviously, don't flush if there is some outbound traffic congestion. * Otherwise, there will be too many messages on the network. */ if (!thorium_worker_has_outbound_traffic_congestion(self->worker)) { thorium_message_multiplexer_flush(self, destination_node, FORCE_YES_SIZE); } } /* * Verify invariant. */ CORE_DEBUGGER_ASSERT(thorium_multiplexed_buffer_current_size(real_multiplexed_buffer)<= maximum_size); /* * Inject the buffer into the worker too. */ return 1; }
int thorium_message_multiplexer_buffer_is_ready(struct thorium_message_multiplexer *self, struct thorium_multiplexed_buffer *multiplexed_buffer) { uint64_t buffer_time; int timeout; #ifdef CHECK_PREDICTED_TRAFFIC_REDUCTION double acceptable_traffic_reduction; double traffic_reduction; #endif int message_count; uint64_t time; int duration; /* * Flush only the buffers with a elapsed time that is greater or equal to the * timeout. */ time = core_timer_get_nanoseconds(&self->timer); buffer_time = thorium_multiplexed_buffer_time(multiplexed_buffer); message_count = multiplexed_buffer->message_count_; /* * Get the current time. This current time will be compared * with the virtual time of each item in the timeline. */ duration = time - buffer_time; timeout = self->timeout_in_nanoseconds; #ifdef CHECK_PREDICTED_TRAFFIC_REDUCTION traffic_reduction = thorium_multiplexed_buffer_get_traffic_reduction(multiplexed_buffer); #endif /* * Flush if the timeout is 0. This means that no aggregation will * take place anyway. */ if (timeout == 0) { return 1; } /* * Flush if DOA (degree of aggregation) is large enough. */ if (message_count >= self->degree_of_aggregation_limit) { return 1; } /* * Flush if the age of the buffer is large enough. */ if (duration >= timeout) { return 1; } #ifdef CHECK_PREDICTED_TRAFFIC_REDUCTION if (traffic_reduction >= acceptable_traffic_reduction) { return 1; } #endif /* * Delay the flush function call a bit longer. */ return 0; }
void thorium_message_multiplexer_update_timeout(struct thorium_message_multiplexer *self) { time_t now; uint64_t elapsed; int period; uint64_t throughput; int timeout; uint64_t now_nanoseconds; uint64_t event_count; int new_timeout; #ifdef MULTIPLEXER_IS_VERBOSE int print = 0; if (thorium_node_must_print_data(self->worker->node) && self->worker->node->name == 0 && self->worker->name == 1) print = 1; #endif period = 1; now = time(NULL); elapsed = now - self->last_update_time; if ((int)elapsed < period) return; timeout = self->timeout_in_nanoseconds; CORE_DEBUGGER_ASSERT(timeout >= 0); now_nanoseconds = core_timer_get_nanoseconds(&self->timer); elapsed = now_nanoseconds - self->last_time; event_count = thorium_worker_get_event_counter(self->worker, THORIUM_EVENT_ACTOR_SEND); throughput = event_count; throughput -= self->last_send_event_count; throughput *= (1000 * 1000 * 1000); throughput /= elapsed; #ifdef MULTIPLEXER_IS_VERBOSE if (print) thorium_printf("event_count %" PRIu64 " last %" PRIu64 " elapsed %" PRIu64 " = %" PRIu64 " MPS\n", event_count, self->last_send_event_count, elapsed, throughput); #endif thorium_decision_maker_add_data_point(&self->decision_maker, timeout, (int)throughput); #ifdef MULTIPLEXER_IS_VERBOSE if (print) thorium_decision_maker_print(&self->decision_maker, timeout); #endif new_timeout = thorium_decision_maker_get_best_timeout(&self->decision_maker, self->timeout_in_nanoseconds); if (new_timeout != THORIUM_TIMEOUT_NO_VALUE) { #ifdef MULTIPLEXER_IS_VERBOSE if (print) thorium_printf("TIMEOUT -> old value %d new value %d\n", self->timeout_in_nanoseconds, new_timeout); #endif self->timeout_in_nanoseconds = new_timeout; } self->last_update_time = now; self->last_time = now_nanoseconds; self->last_send_event_count = event_count; }
void core_timer_start(struct core_timer *timer) { timer->start = core_timer_get_nanoseconds(timer); timer->started = 1; }
void thorium_worker_init(struct thorium_worker *worker, int name, struct thorium_node *node) { int capacity; int ephemeral_memory_block_size; int injected_buffer_ring_size; int argc; char **argv; worker->tick_count = 0; thorium_load_profiler_init(&worker->profiler); argc = thorium_node_argc(node); argv = thorium_node_argv(node); #ifdef THORIUM_WORKER_DEBUG_INJECTION worker->counter_allocated_outbound_buffers = 0; worker->counter_freed_outbound_buffers_from_self = 0; worker->counter_freed_outbound_buffers_from_other_workers = 0; worker->counter_injected_outbound_buffers_other_local_workers= 0; worker->counter_injected_inbound_buffers_from_thorium_core = 0; #endif core_map_init(&worker->actor_received_messages, sizeof(int), sizeof(int)); worker->waiting_is_enabled = 0; worker->waiting_start_time = 0; core_timer_init(&worker->timer); capacity = THORIUM_WORKER_RING_CAPACITY; /*worker->work_queue = work_queue;*/ worker->node = node; worker->name = name; core_bitmap_clear_bit_uint32_t(&worker->flags, FLAG_DEAD); worker->last_warning = 0; worker->last_wake_up_count = 0; /*worker->work_queue = &worker->works;*/ /* There are two options: * 1. enable atomic operations for change visibility * 2. Use volatile head and tail. */ core_fast_ring_init(&worker->actors_to_schedule, capacity, sizeof(struct thorium_actor *)); #ifdef THORIUM_NODE_INJECT_CLEAN_WORKER_BUFFERS injected_buffer_ring_size = capacity; core_fast_ring_init(&worker->injected_clean_outbound_buffers, injected_buffer_ring_size, sizeof(void *)); core_fast_ring_init(&worker->clean_message_ring_for_triage, injected_buffer_ring_size, sizeof(struct thorium_message)); core_fast_queue_init(&worker->clean_message_queue_for_triage, sizeof(struct thorium_message)); #endif thorium_scheduler_init(&worker->scheduler, thorium_node_name(worker->node), worker->name); core_map_init(&worker->actors, sizeof(int), sizeof(int)); core_map_iterator_init(&worker->actor_iterator, &worker->actors); core_fast_ring_init(&worker->outbound_message_queue, capacity, sizeof(struct thorium_message)); core_fast_queue_init(&worker->outbound_message_queue_buffer, sizeof(struct thorium_message)); core_bitmap_clear_bit_uint32_t(&worker->flags, FLAG_DEBUG); core_bitmap_clear_bit_uint32_t(&worker->flags, FLAG_BUSY); core_bitmap_clear_bit_uint32_t(&node->flags, FLAG_ENABLE_ACTOR_LOAD_PROFILER); worker->flags = 0; core_bitmap_clear_bit_uint32_t(&worker->flags, FLAG_DEBUG_ACTORS); if (core_command_has_argument(argc, argv, DEBUG_WORKER_OPTION)) { #if 0 printf("DEBUG has option %s\n", DEBUG_WORKER_OPTION); #endif if (thorium_node_name(worker->node) == 0 && thorium_worker_name(worker) == 0) { #if 0 printf("DEBUG setting bit FLAG_DEBUG_ACTORS because %s\n", DEBUG_WORKER_OPTION); #endif core_bitmap_set_bit_uint32_t(&worker->flags, FLAG_DEBUG_ACTORS); } } worker->epoch_used_nanoseconds = 0; worker->loop_used_nanoseconds = 0; worker->scheduling_epoch_used_nanoseconds = 0; worker->started_in_thread = 0; /* 2 MiB is the default size for Linux huge pages. * \see https://wiki.debian.org/Hugepages * \see http://lwn.net/Articles/376606/ */ /* * 8 MiB */ ephemeral_memory_block_size = 8388608; /*ephemeral_memory_block_size = 16777216;*/ core_memory_pool_init(&worker->ephemeral_memory, ephemeral_memory_block_size, MEMORY_POOL_NAME_WORKER_EPHEMERAL); core_memory_pool_disable_tracking(&worker->ephemeral_memory); core_memory_pool_enable_ephemeral_mode(&worker->ephemeral_memory); #ifdef THORIUM_WORKER_ENABLE_LOCK core_lock_init(&worker->lock); #endif core_set_init(&worker->evicted_actors, sizeof(int)); core_memory_pool_init(&worker->outbound_message_memory_pool, CORE_MEMORY_POOL_MESSAGE_BUFFER_BLOCK_SIZE, MEMORY_POOL_NAME_WORKER_OUTBOUND); /* * Disable the pool so that it uses allocate and free * directly. */ #ifdef CORE_MEMORY_POOL_DISABLE_MESSAGE_BUFFER_POOL core_memory_pool_disable(&worker->outbound_message_memory_pool); #endif /* * Transport message buffers are fancy objects. */ core_memory_pool_enable_normalization(&worker->outbound_message_memory_pool); core_memory_pool_enable_alignment(&worker->outbound_message_memory_pool); worker->ticks_without_production = 0; thorium_priority_assigner_init(&worker->assigner, thorium_worker_name(worker)); /* * This variables should be set in * thorium_worker_start, but when running on 1 process with 1 thread, * thorium_worker_start is never called... */ worker->last_report = time(NULL); worker->epoch_start_in_nanoseconds = core_timer_get_nanoseconds(&worker->timer); worker->loop_start_in_nanoseconds = worker->epoch_start_in_nanoseconds; worker->loop_end_in_nanoseconds = worker->loop_start_in_nanoseconds; worker->scheduling_epoch_start_in_nanoseconds = worker->epoch_start_in_nanoseconds; /* * Avoid valgrind warnings. */ worker->epoch_load = 0; }
void thorium_worker_check_production(struct thorium_worker *worker, int value, int name) { uint64_t time; uint64_t elapsed; struct thorium_actor *other_actor; int mailbox_size; int status; uint64_t threshold; /* * If no actor is scheduled to run, things are getting out of hand * and this is bad for business. * * So, here, an actor is poked for inactivity */ if (!value) { ++worker->ticks_without_production; } else { worker->ticks_without_production = 0; } /* * If too many cycles were spent doing nothing, * check the fast ring since there could be issue in the * cache coherency of the CPU, even with the memory fences. * * This should not happen theoretically. * */ if (worker->ticks_without_production >= THORIUM_WORKER_UNPRODUCTIVE_TICK_LIMIT) { if (core_map_iterator_get_next_key_and_value(&worker->actor_iterator, &name, NULL)) { other_actor = thorium_node_get_actor_from_name(worker->node, name); mailbox_size = 0; if (other_actor != NULL) { mailbox_size = thorium_actor_get_mailbox_size(other_actor); } if (mailbox_size > 0) { thorium_scheduler_enqueue(&worker->scheduler, other_actor); status = STATUS_QUEUED; core_map_update_value(&worker->actors, &name, &status); } } else { /* Rewind the iterator. */ core_map_iterator_destroy(&worker->actor_iterator); core_map_iterator_init(&worker->actor_iterator, &worker->actors); /*worker->ticks_without_production = 0;*/ } /* * If there is still nothing, tell the operating system that the thread * needs to sleep. * * The operating system is: * - Linux on Cray XE6, * - Linux on Cray XC30, * - IBM Compute Node Kernel (CNK) on IBM Blue Gene/Q), */ if (worker->waiting_is_enabled) { /* This is a first warning */ if (worker->waiting_start_time == 0) { worker->waiting_start_time = core_timer_get_nanoseconds(&worker->timer); } else { time = core_timer_get_nanoseconds(&worker->timer); elapsed = time - worker->waiting_start_time; threshold = THORIUM_WORKER_UNPRODUCTIVE_MICROSECONDS_FOR_WAIT; /* Convert microseconds to nanoseconds */ threshold *= 1000; /* Verify the elapsed time. * There are 1000 nanoseconds in 1 microsecond. */ if (elapsed >= threshold) { /* * Here, the worker will wait until it receives a signal. * Such a signal will mean that something is ready to be consumed. */ /* Reset the time */ worker->waiting_start_time = 0; #ifdef THORIUM_WORKER_DEBUG_WAIT_SIGNAL printf("DEBUG worker/%d will wait, elapsed %d\n", worker->name, (int)elapsed); #endif /* */ thorium_worker_wait(worker); } } } } }
void thorium_worker_run(struct thorium_worker *worker) { struct thorium_actor *actor; struct thorium_message other_message; #ifdef THORIUM_NODE_INJECT_CLEAN_WORKER_BUFFERS void *buffer; #endif #ifdef THORIUM_NODE_ENABLE_INSTRUMENTATION time_t current_time; int elapsed; int period; uint64_t current_nanoseconds; uint64_t elapsed_nanoseconds; #endif #ifdef THORIUM_WORKER_DEBUG int tag; int destination; struct thorium_message *message; #endif #ifdef THORIUM_WORKER_ENABLE_LOCK thorium_worker_lock(worker); #endif #ifdef THORIUM_NODE_ENABLE_INSTRUMENTATION period = THORIUM_NODE_LOAD_PERIOD; current_time = time(NULL); elapsed = current_time - worker->last_report; if (elapsed >= period) { current_nanoseconds = core_timer_get_nanoseconds(&worker->timer); #ifdef THORIUM_WORKER_DEBUG_LOAD printf("DEBUG Updating load report\n"); #endif elapsed_nanoseconds = current_nanoseconds - worker->epoch_start_in_nanoseconds; if (elapsed_nanoseconds > 0) { worker->epoch_load = (0.0 + worker->epoch_used_nanoseconds) / elapsed_nanoseconds; worker->epoch_used_nanoseconds = 0; worker->last_wake_up_count = core_thread_get_wake_up_count(&worker->thread); /* \see http://stackoverflow.com/questions/9657993/negative-zero-in-c */ if (worker->epoch_load == 0) { worker->epoch_load = 0; } worker->epoch_start_in_nanoseconds = current_nanoseconds; worker->last_report = current_time; } #ifdef THORIUM_WORKER_PRINT_SCHEDULING_QUEUE /* if (thorium_node_name(worker->node) == 0 && worker->name == 0) { */ thorium_scheduler_print(&worker->scheduler, thorium_node_name(worker->node), worker->name); /* } */ #endif if (core_bitmap_get_bit_uint32_t(&worker->flags, FLAG_DEBUG_ACTORS)) { thorium_worker_print_actors(worker, NULL); } } #endif #ifdef THORIUM_WORKER_DEBUG if (core_bitmap_get_bit_uint32_t(&worker->flags, FLAG_DEBUG)) { printf("DEBUG worker/%d thorium_worker_run\n", thorium_worker_name(worker)); } #endif /* check for messages in inbound FIFO */ if (thorium_worker_dequeue_actor(worker, &actor)) { #ifdef THORIUM_WORKER_DEBUG message = biosal_work_message(&work); tag = thorium_message_action(message); destination = thorium_message_destination(message); if (tag == ACTION_ASK_TO_STOP) { printf("DEBUG pulled ACTION_ASK_TO_STOP for %d\n", destination); } #endif /* * Update the priority of the actor * before starting the timer because this is part of the * runtime system (RTS). */ #ifdef THORIUM_UPDATE_SCHEDULING_PRIORITIES thorium_priority_assigner_update(&worker->scheduler, actor); #endif #ifdef THORIUM_NODE_ENABLE_INSTRUMENTATION core_timer_start(&worker->timer); #endif core_bitmap_set_bit_uint32_t(&worker->flags, FLAG_BUSY); /* * Dispatch message to a worker */ thorium_worker_work(worker, actor); core_bitmap_clear_bit_uint32_t(&worker->flags, FLAG_BUSY); #ifdef THORIUM_NODE_ENABLE_INSTRUMENTATION core_timer_stop(&worker->timer); elapsed_nanoseconds = core_timer_get_elapsed_nanoseconds(&worker->timer); if (elapsed_nanoseconds >= THORIUM_GRANULARITY_WARNING_THRESHOLD) { } worker->epoch_used_nanoseconds += elapsed_nanoseconds; worker->loop_used_nanoseconds += elapsed_nanoseconds; worker->scheduling_epoch_used_nanoseconds += elapsed_nanoseconds; worker->last_elapsed_nanoseconds = elapsed_nanoseconds; #endif } /* queue buffered message */ if (core_fast_queue_dequeue(&worker->outbound_message_queue_buffer, &other_message)) { if (!core_fast_ring_push_from_producer(&worker->outbound_message_queue, &other_message)) { #ifdef SHOW_FULL_RING_WARNINGS printf("thorium_worker: Warning: ring is full => outbound_message_queue\n"); #endif core_fast_queue_enqueue(&worker->outbound_message_queue_buffer, &other_message); } } #ifdef THORIUM_NODE_INJECT_CLEAN_WORKER_BUFFERS /* * Free outbound buffers, if any */ if (thorium_worker_fetch_clean_outbound_buffer(worker, &buffer)) { core_memory_pool_free(&worker->outbound_message_memory_pool, buffer); #ifdef THORIUM_WORKER_DEBUG_INJECTION ++worker->counter_freed_outbound_buffers_from_other_workers; #endif } #endif /* * Transfer messages for triage */ if (core_fast_queue_dequeue(&worker->clean_message_queue_for_triage, &other_message)) { CORE_DEBUGGER_ASSERT(thorium_message_buffer(&other_message) != NULL); thorium_worker_enqueue_message_for_triage(worker, &other_message); } #ifdef THORIUM_WORKER_ENABLE_LOCK thorium_worker_unlock(worker); #endif }
void thorium_worker_reset_scheduling_epoch(struct thorium_worker *worker) { worker->scheduling_epoch_start_in_nanoseconds = core_timer_get_nanoseconds(&worker->timer); worker->scheduling_epoch_used_nanoseconds = 0; }