experiment.cpp 8.27 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
#include <iostream>
#include <thread>
#include <vector>

#include <sched.h>

#include <embb/mtapi/mtapi.h>

#include "defines.h"

11 12 13
#include "timing_header.h"
#include "fun.h"

14 15 16 17 18 19
#define UNUSED(x) ((void)(x))

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

20 21 22 23 24 25 26 27 28 29 30 31
auto loop_count(int duration) -> long long
{
    if(duration < sizeof(timetable) / sizeof(int)) {
        return timetable[duration];
    }

    float m = ((float)timetable[94] - (float)timetable[4]) / (90.0f);
    float t = ((float)timetable[9] - m * 9);

    return m * (float) duration + t;
}

32 33 34 35 36 37 38
auto active_wait(int duration) -> void
{
    for(int i = 0; i < loop_count(duration); i++) {
        donotoptimize();
    }
}

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
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
59 60
    cpp_time_base start;
    cpp_time_base end;
61 62 63
    int core_id = 0;
};

Tobias Langer committed
64 65 66 67
/**
 * Make place to store timestamps of any running task instance.
 */

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
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++) {
88 89
            auto task_start = benchmark[i][j].start.count();
            auto task_end = benchmark[i][j].end.count();
90
            auto core_id = benchmark[i][j].core_id;
Tobias Langer committed
91
            std::cout << "instance " << j << " ";
92
            std::cout << "start " << task_start << " ";
Tobias Langer committed
93
            std::cout << "end " << task_end << " ";
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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
            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. */
134 135
    auto idle_time = taskset[task_id].wcet;

136
    active_wait(idle_time);
137 138 139 140 141

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

Tobias Langer committed
142 143
    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);
144 145 146 147 148 149 150
    benchmark[task_id][task_num - 1].core_id = core_id;
}

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

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

154 155 156 157 158 159 160 161 162
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();

163 164
    /* Initialize deadlines for every task */
    embb::mtapi::ExecutionPolicy deadline_policy[taskset_length];
165 166
    for(int i = 0; i < taskset_length; i++) {
        node.CreateAction(ACTION_ID + i + 1, IdleTask);
167
        deadline_policy[i] = embb::mtapi::ExecutionPolicy(embb_time_base(taskset[i].deadline));
168 169
    }

170 171 172 173 174 175
    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
176 177
    auto cur_time = duration_cast<cpp_time_base>(base_clock::now() - start);

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
    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. 
                 */
206
                task_arguments[i][count - 1] = std::make_pair(i,count);
207 208
                auto job = node.GetJob(ACTION_ID + i + 1, DOMAIN_ID);

209 210
                /* Detached TaskAttribute so we don't have to wait for task completion. */
                embb::mtapi::TaskAttributes detached_attribute;
211
                detached_attribute.SetDetached(true);
212 213 214
                detached_attribute.SetPolicy(deadline_policy[i]);

                int tmp; auto t = node.Start(job, &task_arguments[i][count - 1], &tmp, detached_attribute);
215 216 217 218 219 220 221

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

222
        active_wait(min.count());
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        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();

247 248 249 250
    /*
     * Initialize storage for benchmarking data. By preallocating storage for
     * every instance of every task, we don't need any synchornization.
     */
251 252 253 254
    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
255
        task_arguments[i] = std::vector<std::pair<int,int>>(job_count);
256 257
    }

Tobias Langer committed
258 259
    /* Workaround, the base node action is not initialized if node attributes 
     * are set explicitly.
260 261 262 263 264 265 266 267 268 269 270
     */
    node.CreateAction(ACTION_ID, ActionFunction);

    /* Start task loop */
    TaskStarter();

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

    return 0; 
}