/* * TODO: * * add support for actor priority values here. * * Values are: * * - THORIUM_PRIORITY_LOW * - THORIUM_PRIORITY_NORMAL * - THORIUM_PRIORITY_HIGH * - THORIUM_PRIORITY_MAX * * The priority (an integer of type 'int') of an actor can be obtained with * * actor->priority * * or with * * thorium_actor_get_priority(actor) */ int thorium_cfs_scheduler_enqueue(struct thorium_scheduler *self, struct thorium_actor *actor) { struct thorium_cfs_scheduler *concrete_self; uint64_t virtual_runtime; int priority; concrete_self = self->concrete_self; virtual_runtime = actor->virtual_runtime; priority = actor->priority; /* * Trick the scheduler. */ if (priority == THORIUM_PRIORITY_MAX) { virtual_runtime = 0; } core_red_black_tree_add_key_and_value(&concrete_self->tree, &virtual_runtime, &actor); return 1; }
/* * This is O(n), that is if all thorium nodes have a buffer to flush. */ void thorium_message_multiplexer_test(struct thorium_message_multiplexer *self) { /* * Check if the multiplexer has waited enough. */ int index; struct thorium_multiplexed_buffer *multiplexed_buffer; int buffer_is_ready; #if defined(THORIUM_MULTIPLEXER_USE_TREE) || defined(THORIUM_MULTIPLEXER_USE_HEAP) uint64_t *lowest_key; int *index_bucket; #endif if (CORE_BITMAP_GET_FLAG(self->flags, FLAG_DISABLED)) { return; } #ifdef THORIUM_MULTIPLEXER_TRACK_BUFFERS_WITH_CONTENT /* * Nothing to do, there is nothing to flush * in the system. */ if (core_set_empty(&self->buffers_with_content)) { return; } #endif #if 1 /* * Don't flush anything if the outbound ring is full, * which means there is congestion. * * This means that the throughput is at its maximum value. In that case, * it is better to generate even larger messages. */ if (thorium_worker_has_outbound_traffic_congestion(self->worker)) { return; } #endif #ifdef DEBUG_MULTIPLEXER_TEST if (size >= DEBUG_MINIMUM_COUNT) thorium_printf("DEBUG multiplexer_test buffers with content: %d\n", size); #endif #ifdef CHECK_PREDICTED_TRAFFIC_REDUCTION /* * 0.95 corresponds to 20 actor messages in 1 network message. * 0.90 corresponds to 10 actor messages in 1 network message. */ acceptable_traffic_reduction = 0.90; #endif /* * Get the destination with the oldest buffer. * If this one has not waited enough, then any other more recent * buffer has not waited enough neither. */ while (1) { #ifdef THORIUM_MULTIPLEXER_USE_TREE lowest_key = core_red_black_tree_get_lowest_key(&self->timeline); /* * The timeline is empty. */ if (lowest_key == NULL) return; #elif defined(THORIUM_MULTIPLEXER_USE_HEAP) lowest_key = NULL; core_binary_heap_get_root(&self->timeline, (void **)&lowest_key, (void **)&index_bucket); /* * The timeline is empty. */ if (lowest_key == NULL) return; #elif defined(THORIUM_MULTIPLEXER_USE_QUEUE) if (!core_queue_dequeue(&self->timeline, &index)) { return; } #endif /* * Get the index. * The index is the destination. */ #ifdef THORIUM_MULTIPLEXER_USE_TREE index_bucket = core_red_black_tree_get(&self->timeline, lowest_key); index = *index_bucket; #endif multiplexed_buffer = core_vector_at(&self->buffers, index); buffer_is_ready = thorium_message_multiplexer_buffer_is_ready(self, multiplexed_buffer); /* * The oldest item is too recent. * Therefore, all the others are too recent too * because the timeline is ordered. */ if (!buffer_is_ready) { #ifdef THORIUM_MULTIPLEXER_USE_QUEUE core_queue_enqueue(&self->timeline, &index); #endif return; } #ifdef CHECK_OUTBOUND_THROUGHPUT /* * Don't flush now since the transport layer has already reached its maximum * throughput. */ if (thorium_worker_has_reached_maximum_outbound_throughput(self->worker) && traffic_reduction < acceptable_traffic_reduction) { #ifdef THORIUM_MULTIPLEXER_USE_QUEUE core_queue_enqueue(&self->timeline, &index); #endif return; } #endif /* * Remove the object from the timeline. */ #ifdef THORIUM_MULTIPLEXER_USE_TREE core_red_black_tree_delete(&self->timeline, lowest_key); #elif defined(THORIUM_MULTIPLEXER_USE_HEAP) core_binary_heap_delete_root(&self->timeline); #endif #ifdef VERIFY_TARGET /* * Verify if the buffer can grow more. */ if (!thorium_multiplexed_buffer_has_reached_target(multiplexed_buffer)) { thorium_multiplexed_buffer_set_time(multiplexed_buffer, time); core_red_black_tree_add_key_and_value(&self->timeline, &time, &index); return; } #endif /* * The item won't have content in the case were _flush() * was called elsewhere with FORCE_YES_SIZE. */ #ifdef THORIUM_MULTIPLEXER_TRACK_BUFFERS_WITH_CONTENT if (core_set_find(&self->buffers_with_content, &index)) { #endif thorium_message_multiplexer_flush(self, index, FORCE_YES_TIME); #ifdef THORIUM_MULTIPLEXER_TRACK_BUFFERS_WITH_CONTENT } #endif /* * Otherwise, keep flushing stuff. * This will eventually end anyway. */ } }
/* * 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; }