experiment.cpp 8.33 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include <iostream>
#include <thread>
#include <vector>

#include <sched.h>

#include <embb/mtapi/mtapi.h>

#include "defines.h"

#define UNUSED(x) ((void)(x))

#define DOMAIN_ID 1
#define NODE_ID 1
#define ACTION_ID 2

auto gcd(long long a, long long b) -> long long
{
    return b == 0 ? a : gcd(b, a % b);
}

auto lcm(long long a, long long b) -> long long
{
    return (a * b) / gcd(a, b);
}

auto calculate_hyperperiod() -> long long 
{
    long long hyperperiod = taskset[0].period;
    for(int i = 1; i < taskset_length; i++) {
        hyperperiod = lcm(hyperperiod, taskset[i].period); 
    }
    return hyperperiod;
}

struct timestamps {
Tobias Langer committed
37 38
    cpp_time_base start;
    cpp_time_base end;
39 40 41
    int core_id = 0;
};

Tobias Langer committed
42 43 44 45
/**
 * Make place to store timestamps of any running task instance.
 */

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
base_clock::time_point start;
base_clock::time_point end;
std::vector<timestamps> benchmark[taskset_length];

void benchmark_out()
{
    using namespace std::chrono;

    std::cout << "benchmark results ";
    std::cout << "start " << base_clock::to_time_t(start) << " ";
    std::cout << "end " << base_clock::to_time_t(end) << std::endl;

    for(int i = 0; i < taskset_length; i++) {
        std::cout << "task " << i << " ";
        std::cout << "wcet " << taskset[i].wcet << " ";
        std::cout << "period " << taskset[i].period << " ";
        std::cout << "deadline " << taskset[i].deadline << " ";
        std::cout << "executions " << taskset[i].count << std::endl;

        for(int j = 0; j < benchmark[i].size(); j++) {
66 67
            auto task_start = benchmark[i][j].start.count();
            auto task_end = benchmark[i][j].end.count();
68
            auto core_id = benchmark[i][j].core_id;
Tobias Langer committed
69
            std::cout << "instance " << j << " ";
70
            std::cout << "start " << task_start << " ";
Tobias Langer committed
71
            std::cout << "end " << task_end << " ";
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
            std::cout << "core_id " << core_id << std::endl;
        }
    }
}

/******
 * Task Declarations
 ******/

/* Workaround helper function, the base node action is not initialized if the 
 * node gets attributes set.
 */
static void ActionFunction(
        const void* args,
        mtapi_size_t /*args_size*/,
        void* /*result_buffer*/,
        mtapi_size_t /*result_buffer_size*/,
        const void* /*node_local_data*/,
        mtapi_size_t /*node_local_data_size*/,
        mtapi_task_context_t * context) {
    embb::mtapi::TaskContext task_context(context);
    embb::base::Function<void, embb::mtapi::TaskContext &> * func =
        reinterpret_cast<embb::base::Function<void, embb::mtapi::TaskContext &>*>(
                const_cast<void*>(args));
    (*func)(task_context);
    embb::base::Allocation::Delete(func);
}

static void IdleTask(const void* args, mtapi_size_t, void*, mtapi_size_t, 
                     const void*, mtapi_size_t, mtapi_task_context_t*)
{
    using namespace std::chrono;
    /* Get access to parameter data. */
    auto data = static_cast<const std::pair<int,int>*>(args);

    auto task_id = data->first;
    auto task_num = data->second;
    auto start_time = base_clock::now();

    /* idle until task completion. */
    auto idle_time = cpp_time_base(taskset[task_id].wcet);
    std::this_thread::sleep_for(idle_time);

    /* Store our benchmarking data. */
    auto end_time = base_clock::now();
    int core_id = sched_getcpu();

Tobias Langer committed
119 120
    benchmark[task_id][task_num - 1].start = duration_cast<cpp_time_base>(start_time - start);
    benchmark[task_id][task_num - 1].end = duration_cast<cpp_time_base>(end_time - start);
121 122 123 124 125 126 127
    benchmark[task_id][task_num - 1].core_id = core_id;
}

/****
 * Main loop of task starter core.
 ****/

128 129 130
/* Make place to store the arguments for any running task instance. */
std::vector<std::pair<int,int>> task_arguments[taskset_length];

131 132 133 134 135 136 137 138 139
static void TaskStarter()
{
    /* Initialize task starter */
    auto& node = embb::mtapi::Node::GetInstance();
    /* Storage for any task which is started. */
    std::vector<embb::mtapi::Task> running;

    auto hyperperiod = calculate_hyperperiod();

140 141
    /* Initialize deadlines for every task */
    embb::mtapi::ExecutionPolicy deadline_policy[taskset_length];
142 143
    for(int i = 0; i < taskset_length; i++) {
        node.CreateAction(ACTION_ID + i + 1, IdleTask);
144
        deadline_policy[i] = embb::mtapi::ExecutionPolicy(embb_time_base(taskset[i].deadline));
145 146
    }

147 148 149 150 151 152
    using namespace std::chrono;
    start = base_clock::now();

    std::cerr << "Starting TaskStarter thread at: ";
    std::cerr << base_clock::to_time_t(start) << std::endl;

Tobias Langer committed
153 154
    auto cur_time = duration_cast<cpp_time_base>(base_clock::now() - start);

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    while(cur_time.count() < hyperperiod) {
        auto min = cpp_time_base::max();

        /* Check for every task if it has to be executed. */
        for(int i = 0; i < taskset_length; i++) {
            auto task_period = cpp_time_base(taskset[i].period);
            /* Execute always then when a next period has started, meaning when
             * cur_time / task_period is bigger than for the last check. The
             * first task_period is to let the tasks start at time 0 and not at
             * time task_period.
             */
            auto count = (cur_time + task_period) / task_period;

            /* Check how long to sleep next, either min(cur_time % period), or 
             * period if cur_time % period == 0
             */
            auto remaining = cur_time % task_period;
            remaining = remaining.count() == 0 ? task_period : remaining;
            if(remaining < min) {
                min = remaining;
            }

            if(count > taskset[i].count) {
                /* Store parameters for execution.
                 * The count may change during the execution, therefore we have
                 * to make sure that all possible running tasks can access their
                 * parameters. 
                 */
183
                task_arguments[i][count - 1] = std::make_pair(i,count);
184 185
                auto job = node.GetJob(ACTION_ID + i + 1, DOMAIN_ID);

186 187 188 189 190 191
                /* Detached TaskAttribute so we don't have to wait for task completion. */
                embb::mtapi::TaskAttributes detached_attribute;
                // detached_attribute.SetDetached(true);
                detached_attribute.SetPolicy(deadline_policy[i]);

                int tmp; auto t = node.Start(job, &task_arguments[i][count - 1], &tmp, detached_attribute);
192 193 194 195 196 197 198

                /* Store task to wait for it. */
                running.push_back(t);
                taskset[i].count = count;
            }
        }

199 200 201 202 203 204 205 206 207 208 209
        auto wait_start = base_clock::now();
        while(min.count() > 0 && running.size() > 0) {
            auto status = running.front().Wait(min.count()); // TODO factor for anything bigger than milliseconds
            if(status == MTAPI_SUCCESS) {
                running.erase(running.begin());
            }
            auto remain = duration_cast<cpp_time_base>(wait_start - base_clock::now());
            if(remain.count() > 0) {
                min -= remain;
            }
        }
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
        std::this_thread::sleep_for(min);
        cur_time = duration_cast<cpp_time_base>(base_clock::now() - start);
    }

    /* Wait for all started tasks to be completed. */
    for(auto& task : running) {
        task.Wait(MTAPI_INFINITE);
    }

    end = base_clock::now();
    std::cerr << "Finishing TaskStarter thread at: ";
    std::cerr << base_clock::to_time_t(end) << std::endl;
}

int main(int argc, char* argv[]) 
{
    UNUSED(argc);
    UNUSED(argv);

    /* Initialize node and set global edf as scheduling method. */
    embb::mtapi::NodeAttributes attr;
    attr.SetSchedulerMode(GLOBAL_EDF);
    embb::mtapi::Node::Initialize(DOMAIN_ID, NODE_ID, attr);
    auto& node = embb::mtapi::Node::GetInstance();

235 236 237 238
    /*
     * Initialize storage for benchmarking data. By preallocating storage for
     * every instance of every task, we don't need any synchornization.
     */
239 240 241 242
    auto hyperperiod = calculate_hyperperiod();
    for(int i = 0; i < taskset_length; i++) {
        auto job_count = hyperperiod / taskset[i].period;
        benchmark[i] = std::vector<timestamps>(job_count);
Tobias Langer committed
243
        task_arguments[i] = std::vector<std::pair<int,int>>(job_count);
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    }

    /* Workaround, the base node action is not initialized if the node gets
     * attributes set.
     */
    node.CreateAction(ACTION_ID, ActionFunction);

    /* Start task loop */
    TaskStarter();

    /* Print experiment results. */
    benchmark_out();

    return 0; 
}