Skip to content
Snippets Groups Projects
Commit 6d2ea529 authored by Philipp Stenkamp's avatar Philipp Stenkamp
Browse files

Scheduler performance testing

parent fb94d03a
Branches
No related tags found
No related merge requests found
No preview for this file type
// LeonardoMixer.cpp // LeonardoMixer.cpp
// Copyright 2016, 2017 Lukas Friedrichsen, Philipp Stenkamp // Copyright 2016, 2017 Lukas Friedrichsen, Philipp Stenkamp
// License: Modified BSD-License // License: Modified BSD-License
......
...@@ -226,8 +226,8 @@ unsigned long Scheduler::getTaskTimerDiff (task *taskToGet) { ...@@ -226,8 +226,8 @@ unsigned long Scheduler::getTaskTimerDiff (task *taskToGet) {
// Handles the functions excluding send // Handles the functions excluding send
void Scheduler::perform (void) { void Scheduler::perform (void) {
if (taskCounter && *taskCounter) { if (taskCounter && *taskCounter) {
(*taskCounter)->activity();
updateTaskTimer((task *) *taskCounter); updateTaskTimer((task *) *taskCounter);
(*taskCounter)->activity();
taskCounter++; taskCounter++;
} }
else if (nRtTasks) { else if (nRtTasks) {
...@@ -245,6 +245,8 @@ void Scheduler::exterminate (void){ ...@@ -245,6 +245,8 @@ void Scheduler::exterminate (void){
// Scheduler, called cyclical in the loop-function // Scheduler, called cyclical in the loop-function
void Scheduler::schedule (void) { void Scheduler::schedule (void) {
long diff;
long schedTime = micros();
if (rtTasks) { if (rtTasks) {
unsigned long timerDiff = getTaskTimerDiff(rtTasks->listElement); unsigned long timerDiff = getTaskTimerDiff(rtTasks->listElement);
unsigned long cycleTime = getTaskCycleTime((rtTask *) rtTasks->listElement); unsigned long cycleTime = getTaskCycleTime((rtTask *) rtTasks->listElement);
...@@ -258,13 +260,14 @@ void Scheduler::schedule (void) { ...@@ -258,13 +260,14 @@ void Scheduler::schedule (void) {
exterminate(); exterminate();
} }
} }
rtTasks->listElement->activity();
updateTaskTimer(rtTasks->listElement); updateTaskTimer(rtTasks->listElement);
rtTasks->listElement->activity();
rtTasks = sortRtTasks(rtTasks); rtTasks = sortRtTasks(rtTasks);
if (nRtTasks){ if (nRtTasks){
setPriorities(); setPriorities();
taskCounter = nRtTasks; taskCounter = nRtTasks;
} }
diff = micros()-schedTime;
} }
else if (nRtTasks) { else if (nRtTasks) {
perform(); perform();
...@@ -279,4 +282,9 @@ void Scheduler::schedule (void) { ...@@ -279,4 +282,9 @@ void Scheduler::schedule (void) {
} }
// No tasks... Nothing to do! // No tasks... Nothing to do!
} }
if (debugger) {
debugger->print("schedule runtime: ");
debugger->println(diff);
}
} }
.pioenvs
.clang_complete
.gcc-flags.json
.piolibdeps
.pioenvs
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < http://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
# < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
# < http://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
# < http://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choice one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#
#
# Template #1: General project. Test it using existing `platformio.ini`.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# install:
# - pip install -U platformio
#
# script:
# - platformio run
#
# Template #2: The project is intended to by used as a library with examples
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# env:
# - PLATFORMIO_CI_SRC=path/to/test/file.c
# - PLATFORMIO_CI_SRC=examples/file.ino
# - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
# - pip install -U platformio
#
# script:
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
LeonardoMixerIO
Copyright 2016, 2017 Lukas Friedrichsen, Philipp Stenkamp
License: Modified BSD-License
Realtime RC mixer for Arduino-devices
2017-02-10
r timer;
int taskCounter = 1;
int priorityCounter = 1;
Tasks senden = new Task(
void *send (void){
...
}
, 0);
Tasks mischen = new Task(
void *mix (void){
...
}
, 2);
Tasks empfangen = new Task(
void receive (void){
...
}
, 1);
Tasks *tasks = {senden, mischen, empfangen}; //Jedem Task muss eine eindeutige, fortlaufende Priorität gegeben werden!
struct {
boolean empfangen;
int data;
input protocol;
} signal;
signal *inputs = {...};
void scheduler (void){
switch timer:
case >20ms:
resetTimer();
task[0].activity();
setPriorities();
case <20ms:
perform();
}
void resetTimer (void){
timer = 0;
}
void aktualisiereTimer (void){
timer += ticksseitletztemaktualisieren/tickfrequenz
}
void setPriorities (void){
if (alleEmpfangen()){
task[1].priority = 1;
tasks[2].priority = 2;
}
else {
task[1].priority = 2;
tasks[2].priority = 1;
}
taskCounter = 1;
priorityCounter = 1;
}
void perform (void){
if (taskCounter > tasks.length()){
taskCounter = 1;
priorityCounter++;
}
if (priorityCounter > tasks.length(){
priorityCounter = 1;
}
if (tasks[taskCounter].priority == priorityCounter){
tasks[taskCounter].activity();
}
taskCounter++;
}
boolean alleEmpfangen (void){
boolean ready = true;
for (int i = 0; i <= input.length(); i++){
ready = ready & input[i].empfangen;
}
return empfangen;
}
void main (void){
while (true){
aktualisiereTimer();
scheduler();
}
}
This directory is intended for the project specific (private) libraries.
PlatformIO will compile them to static libraries and link to executable file.
The source code of each library should be placed in separate directory, like
"lib/private_lib/[here are source files]".
For example, see how can be organized `Foo` and `Bar` libraries:
|--lib
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |- readme.txt --> THIS FILE
|- platformio.ini
|--src
|- main.c
Then in `src/main.c` you should use:
#include <Foo.h>
#include <Bar.h>
// rest H/C/CPP code
PlatformIO will find your libraries automatically, configure preprocessor's
include paths and build them.
More information about PlatformIO Library Dependency Finder
- http://docs.platformio.org/page/librarymanager/ldf.html
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
[env:leonardo]
platform = atmelavr
board = leonardo
framework = arduino
// PerformanceTest.cpp
// Copyright 2017 Lukas Friedrichsen, Philipp Stenkamp
// License: Modified BSD-License
//
// Test-Application for the Scheduler
//
// 2017-01-06
#include <Arduino.h>
#include "Scheduler.h"
Scheduler *scheduler;
rtTask rt1, rt2, rt3;
rtTask *rtTasks[] = { &rt1, &rt2, &rt3, NULL };
nRtTask nrt1, nrt2;
//nRtTask *nRtTasks[] = { &nrt1, &nrt2, NULL };
nRtTask *nRtTasks[] = { NULL };
long lastTime;
void no_Function (void)
{
return;
}
void nrt1_Function (void)
{
//return;// do nothing
long tmpTime = micros();
Serial.print("Timestamp nrt1: ");
Serial.print(tmpTime);
Serial.print(", Diff: ");
Serial.println(tmpTime - lastTime);
lastTime = micros();
}
void nrt2_Function (void)
{
//return;// do nothing
long tmpTime = micros();
Serial.print("Timestamp nrt2: ");
Serial.print(tmpTime);
Serial.print(", Diff (us): ");
Serial.println(tmpTime - lastTime);
lastTime = micros();
}
void send_Function (void)
{
return;
long tmpTime = micros()-lastTime;
lastTime = micros();
Serial.print("Cycletime send: ");
Serial.print(tmpTime);
Serial.print(", Diff (us): ");
Serial.println(tmpTime - rt1.cycleTime);
}
void setup()
{
Serial.begin(9600);
nrt1.priority = 1;
nrt1.activity = nrt1_Function;
nrt1.timestamp = 0;
nrt1.priority = 2;
nrt2.activity = nrt2_Function;
nrt2.timestamp = 0;
rt1.activity = no_Function;
rt1.timestamp = 0;
rt1.cycleTime = 50000;
rt2.activity = no_Function;
rt2.timestamp = 0;
rt2.cycleTime = 20000;
rt3.activity = no_Function;
rt3.timestamp = 0;
rt3.cycleTime = 100000;
lastTime = micros();
scheduler = new Scheduler(rtTasks, nRtTasks);
scheduler->setDebugger(&Serial);
}
void loop() { scheduler->schedule(); }
// Scheduler.cpp
// Copyright 2016, 2017 Lukas Friedrichsen, Philipp Stenkamp
// License: Modified BSD-License
//
// Implementation of a cooperative multitasking based scheduler for Arduino-devices
//
// 2017-02-10
#include "Scheduler.h"
// Constructor with task-array as input-argument
Scheduler::Scheduler (rtTask **newRtTasks, nRtTask **newNRtTask) {
rtTasks = NULL;
nRtTasks = NULL;
taskCounter = NULL;
debugger = NULL;
listStoragePointer = NULL;
overloadCounter = 0;
nRtTaskCounter = 0;
setRtTasks(newRtTasks);
setNRtTasks(newNRtTask);
}
// Sorts and sets the input-array as the new non-realtime-task-array; the last element of newNRtTasks HAS TO BE A NULLPOINTER to mark the end of the array
void Scheduler::setNRtTasks (nRtTask **newNRtTasks) {
if (newNRtTasks && newNRtTasks[0]) {
nRtTaskCounter = 0;
while (newNRtTasks[nRtTaskCounter]) {
nRtTaskCounter++;
if (nRtTaskCounter >= MAX_TASK_THRESHOLD) {
if (debugger) {
debugger->println("Number of non-realtime-tasks exceeds task threshold or nullpointer at the end of the array missing! Shutting down scheduler!");
}
exterminate();
}
}
nRtTasks = newNRtTasks;
sortTasks((task **) nRtTasks, 0, nRtTaskCounter-1);
taskCounter = nRtTasks;
}
else {
nRtTasks = NULL;
}
}
// Sorts the realtime-task-array and converts it to a linked list; the last element of newRtTask HAS TO BE A NULLPOINTER to mark the end of the array
void Scheduler::setRtTasks (rtTask **newRtTasks) {
if (newRtTasks && newRtTasks[0]) {
listStoragePointer = listStorage;
int counter = 0;
while (newRtTasks[counter]) {
counter++;
if (counter >= MAX_TASK_THRESHOLD) {
if (debugger) {
debugger->println("Number of realtime-tasks exceeds task threshold or nullpointer at the end of the array missing! Shutting down scheduler!");
}
exterminate();
}
}
task **temp = (task **) newRtTasks;
sortTasks(temp, 0, counter-1);
rtTasks = convertToLinkedList(temp);
}
else {
rtTasks = NULL;
}
}
// Sets a serial-port for debugging; error-messages will be printed there
void Scheduler::setDebugger (Serial_ *serial){
debugger = serial;
}
// Sorts the task-array so that the tasks are in order of their cycle-time / priority (if a non-realtime-task-array is given, getTaskCycleTime will return the tasks priority); left is the first, right the last position in the array to be sorted
void Scheduler::sortTasks(task **tasks, int left, int right) {
if (left < right && (right-left+1) > 1 && tasks && *tasks) {
// Quicksort (O(n*log(n)) on average; recursive)
/*int middle = left+rand()%(right-left+1);
unsigned long test = getTaskCycleTime((rtTask *) tasks[middle]);
int l = left;
int r = right;
while (l <= r) {
while (getTaskCycleTime((rtTask *) tasks[l]) < test) {
l++;
}
while (getTaskCycleTime((rtTask *) tasks[r]) > test) {
r--;
}
if (l <= r) {
task *temp = tasks[l];
tasks[l] = tasks[r];
tasks[r] = temp;
l++;
r--;
}
}
sortTasks(tasks, left, r);
sortTasks(tasks, l, right);*/
// Heapsort (allways O(n*log(n)); iterative)
int length, start, heapLength, i, j;
task *reference;
start = left;
heapLength = right-left+1;
length = heapLength/2+1;
while (heapLength > 1) {
if (length > 1) {
reference = tasks[start+(--length-1)];
}
else {
reference = tasks[start+heapLength-1];
tasks[start+heapLength-1] = tasks[start];
heapLength--;
}
i = length;
j = length*2;
while (j <= heapLength && heapLength > 1) {
if (j < heapLength && getTaskCycleTime((rtTask *) tasks[start+j-1]) < getTaskCycleTime((rtTask *) tasks[start+j])) {
j++;
}
if (getTaskCycleTime((rtTask *) reference) < getTaskCycleTime((rtTask *) tasks[start+j-1])) {
tasks[start+i-1] = tasks[start+j-1];
i = j;
j *= 2;
}
else {
j = heapLength + 1; // Terminates the shift-down
}
}
tasks[start+i-1] = reference;
}
}
}
// Places the given element in the linked list based on when it has to be called next; if only one realtime-task exists, the list isn't modified
linkedListElement * Scheduler::sortRtTasks (linkedListElement *tasks) {
linkedListElement *reference = tasks;
linkedListElement *counter = (linkedListElement *) reference->next;
if (counter && (signed long)(getTaskCycleTime((rtTask *) reference->listElement)-getTaskTimerDiff(reference->listElement)) > (signed long)(getTaskCycleTime((rtTask *) counter->listElement)-getTaskTimerDiff(counter->listElement))) {
while (counter->next && (signed long)(getTaskCycleTime((rtTask *) reference->listElement)-getTaskTimerDiff(reference->listElement)) > (signed long)(getTaskCycleTime((rtTask *) counter->listElement)-getTaskTimerDiff(counter->listElement))){
counter = (linkedListElement *) counter->next;
}
linkedListElement *temp = (linkedListElement *) reference->next;
reference->next = counter->next;
counter->next = reference;
return temp;
}
else {
return reference;
}
}
// Converts the given realtime-task-array to a bidirectional linked list element
linkedListElement * Scheduler::convertToLinkedList (task **taskArray) {
linkedListElement *firstElement = NULL;
linkedListElement *listElementPointer = NULL;
while (*taskArray) {
linkedListElement *new_pointer = newLinkedListElement();
new_pointer->listElement = *taskArray;
new_pointer->next = NULL;
if (listElementPointer) {
listElementPointer->next = new_pointer;
}
else {
firstElement = new_pointer;
}
listElementPointer = new_pointer;
taskArray++;
}
return firstElement;
}
// Returns a pointer to a new linked list element from the array listStorage
linkedListElement * Scheduler::newLinkedListElement (void){
if (listStoragePointer){
return listStoragePointer++;
}
else {
if (debugger) {
debugger->println("Can't initialize any more linked list elements! Shutting down scheduler!");
}
exterminate();
}
}
// Sets the timer of the given task to the current time in microseconds
void Scheduler::updateTaskTimer (task *taskToUpdate) {
taskToUpdate->timestamp = micros();
}
// Dynamic prioritiy-allocation to prevent starvation
void Scheduler::setPriorities (void) {
sortTasks((task **) nRtTasks, 0, nRtTaskCounter-1);
taskCounter = nRtTasks;
int counter = 0;
while (taskCounter && *taskCounter){
if (getTaskTimerDiff((task *) *taskCounter) >= STARVATION_THRESHOLD){
nRtTask *temp = *taskCounter;
memmove(nRtTasks+1, nRtTasks, counter*sizeof(void *));
*nRtTasks = temp;
}
counter++;
taskCounter++;
}
}
// Returns the cycle time of the given realtime-task
unsigned long Scheduler::getTaskCycleTime (rtTask *taskToGet) {
return taskToGet->cycleTime;
}
// Returns the priority of the given non-realtime-task
unsigned long Scheduler::getTaskPriority(nRtTask *taskToGet) {
return taskToGet->priority;
}
// Returns the difference between the value of timestamp of the given task and the current time in microseconds
unsigned long Scheduler::getTaskTimerDiff (task *taskToGet) {
return (micros() - taskToGet->timestamp);
}
// Handles the functions excluding send
void Scheduler::perform (void) {
if (taskCounter && *taskCounter) {
updateTaskTimer((task *) *taskCounter);
(*taskCounter)->activity();
taskCounter++;
}
else if (nRtTasks) {
taskCounter = nRtTasks;
}
}
// The system switches to a defined state (do nothing) in case of a error.
void Scheduler::exterminate (void){
if (debugger) {
debugger->println("An error occured! Programm terminated!");
}
while (true);
}
// Scheduler, called cyclical in the loop-function
void Scheduler::schedule (void) {
long diff;
long schedTime = micros();
if (rtTasks) {
unsigned long timerDiff = getTaskTimerDiff(rtTasks->listElement);
unsigned long cycleTime = getTaskCycleTime((rtTask *) rtTasks->listElement);
if (timerDiff >= cycleTime) {
if (((timerDiff-cycleTime)*100/cycleTime) > OVERLOAD_THRESHOLD_PERCENT) {
overloadCounter++;
if (overloadCounter > OVERLOAD_THRESHOLD_TIMES) {
if (debugger) {
debugger->println("Capacity overload! Delay between theoretical and practical cycle-time exceeds threshold! Shutting down scheduler!");
}
exterminate();
}
}
updateTaskTimer(rtTasks->listElement);
rtTasks->listElement->activity();
rtTasks = sortRtTasks(rtTasks);
if (nRtTasks){
setPriorities();
taskCounter = nRtTasks;
}
diff = micros()-schedTime;
if (debugger) {
debugger->println("Scheduler::schedule runtime (us): ");
debugger->println(diff);
}
}
else if (nRtTasks) {
perform();
}
}
else if (nRtTasks) {
perform();
}
else {
if (debugger) {
debugger->println("No tasks... Nothing to do!");
}
// No tasks... Nothing to do!
}
}
// Scheduler.h
// TODO License
// Lukas Friedrichsen, 2016-12-
//
#ifndef Scheduler_h
#define Scheduler_h
#include <Arduino.h>
#include "Scheduler.h"
#define MAX_TASK_THRESHOLD 10
#define OVERLOAD_THRESHOLD_PERCENT 10
#define OVERLOAD_THRESHOLD_TIMES 1000
#define STARVATION_THRESHOLD 100000
struct task
{
void (*activity) (void); // The schedulers only purpose is scheduling the tasks, not to process any data. Therefor the function doesn't accept input-arguments nor returns any value.
unsigned long timestamp;
};
struct rtTask: task
{
unsigned long cycleTime;
};
struct nRtTask: task
{
unsigned long priority;
};
struct linkedListElement
{
task *listElement;
void *next;
};
class Scheduler
{
public:
Scheduler (rtTask **newRtTasks, nRtTask **newNRtTask);
void schedule (void);
void setRtTasks (rtTask **newRtTasks);
void setNRtTasks (nRtTask **newNRtTasks);
void setDebugger (Serial_ *debugger);
private:
linkedListElement *rtTasks, *listStoragePointer, listStorage[MAX_TASK_THRESHOLD];
nRtTask **nRtTasks, **taskCounter;
unsigned int overloadCounter, nRtTaskCounter;
Serial_ *debugger;
void sortTasks (task **tasks, int left, int right);
linkedListElement * sortRtTasks (linkedListElement *tasks);
linkedListElement * convertToLinkedList (task **tasks);
linkedListElement * newLinkedListElement (void);
void updateTaskTimer (task *taskToUpdate);
void setPriorities (void);
unsigned long getTaskCycleTime (rtTask *taskToGet);
unsigned long getTaskPriority (nRtTask *taskToGet);
unsigned long getTaskTimerDiff (task *taskToGet);
void perform (void);
void exterminate (void);
};
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment