Example #1
0
/*
 * 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;
}
Example #2
0
/*
 * 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.
         */
    }
}
Example #3
0
/*
 * 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;
}