Skip to content
Snippets Groups Projects
Commit 6564d6e8 authored by Niccolò Betto's avatar Niccolò Betto
Browse files

[TaskScheduler] Pre-allocate the agenda's backing vector

Avoids runtime re-allocations by reserving the
maximum capacity at construction time.
parent feffaf44
Branches
No related tags found
No related merge requests found
...@@ -31,6 +31,7 @@ using namespace miosix; ...@@ -31,6 +31,7 @@ using namespace miosix;
namespace Boardcore namespace Boardcore
{ {
namespace Constants namespace Constants
{ {
static constexpr unsigned int TICKS_PER_MS = static constexpr unsigned int TICKS_PER_MS =
...@@ -39,8 +40,16 @@ static constexpr unsigned int MS_PER_TICK = ...@@ -39,8 +40,16 @@ static constexpr unsigned int MS_PER_TICK =
1000 / miosix::TICK_FREQ; // Number of milliseconds in a tick 1000 / miosix::TICK_FREQ; // Number of milliseconds in a tick
} // namespace Constants } // namespace Constants
TaskScheduler::EventQueue TaskScheduler::makeAgenda()
{
std::vector<Event> agendaStorage{};
agendaStorage.reserve(MAX_TASKS);
return EventQueue{std::greater<Event>{}, std::move(agendaStorage)};
}
TaskScheduler::TaskScheduler(miosix::Priority priority) TaskScheduler::TaskScheduler(miosix::Priority priority)
: ActiveObject(STACK_MIN_FOR_SKYWARD, priority), tasks() : ActiveObject(STACK_MIN_FOR_SKYWARD, priority), tasks(),
agenda(makeAgenda())
{ {
// Preallocate the vector to avoid dynamic allocation later on // Preallocate the vector to avoid dynamic allocation later on
tasks.reserve(MAX_TASKS); tasks.reserve(MAX_TASKS);
...@@ -65,17 +74,21 @@ size_t TaskScheduler::addTask(function_t function, uint32_t period, ...@@ -65,17 +74,21 @@ size_t TaskScheduler::addTask(function_t function, uint32_t period,
return 0; return 0;
} }
// Insert a new task with the given parameters
tasks.emplace_back(function, period, policy);
size_t id = tasks.size() - 1;
if (policy == Policy::ONE_SHOT) if (policy == Policy::ONE_SHOT)
{ {
startTick += period; startTick += period;
} }
// Add the task first event in the agenda, performs in-place construction // Insert a new task with the given parameters
tasks.emplace_back(function, period, policy, startTick);
size_t id = tasks.size() - 1;
// Only add the task to the agenda if the scheduler is running
// Otherwise, the agenda will be populated when the scheduler is started
if (isRunning())
{
agenda.emplace(id, startTick); agenda.emplace(id, startTick);
}
condvar.broadcast(); // Signals the run thread condvar.broadcast(); // Signals the run thread
mutex.unlock(); mutex.unlock();
...@@ -131,8 +144,8 @@ bool TaskScheduler::start() ...@@ -131,8 +144,8 @@ bool TaskScheduler::start()
return false; return false;
} }
// Normalize the tasks start time if they precede the current tick // Populate the agenda with the tasks we have so far
normalizeTasks(); populateAgenda();
return ActiveObject::start(); return ActiveObject::start();
} }
...@@ -163,27 +176,24 @@ vector<TaskStatsResult> TaskScheduler::getTaskStats() ...@@ -163,27 +176,24 @@ vector<TaskStatsResult> TaskScheduler::getTaskStats()
return result; return result;
} }
void TaskScheduler::normalizeTasks() void TaskScheduler::populateAgenda()
{ {
int64_t currentTick = getTick(); int64_t currentTick = getTick();
EventQueue newAgenda; for (size_t id = 1; id < tasks.size(); id++)
while (!agenda.empty())
{ {
Event event = agenda.top(); Task& task = tasks[id];
agenda.pop();
if (event.nextTick < currentTick) int64_t nextTick = task.startTick;
// Normalize the tasks start time if they precede the current tick
if (nextTick < currentTick)
{ {
Task& task = tasks[event.taskId]; nextTick +=
event.nextTick += ((currentTick - nextTick) / task.period + 1) * task.period;
((currentTick - event.nextTick) / task.period + 1) *
task.period;
} }
newAgenda.push(event); agenda.emplace(id, nextTick);
} }
agenda = std::move(newAgenda);
} }
void TaskScheduler::run() void TaskScheduler::run()
...@@ -304,16 +314,17 @@ void TaskScheduler::enqueue(Event event, int64_t startTick) ...@@ -304,16 +314,17 @@ void TaskScheduler::enqueue(Event event, int64_t startTick)
} }
TaskScheduler::Task::Task() TaskScheduler::Task::Task()
: function(nullptr), period(0), enabled(false), policy(Policy::SKIP), : function(nullptr), period(0), startTick(0), enabled(false),
lastCall(-1), activationStats(), periodStats(), workloadStats(), policy(Policy::SKIP), lastCall(-1), activationStats(), periodStats(),
missedEvents(0), failedEvents(0) workloadStats(), missedEvents(0), failedEvents(0)
{ {
} }
TaskScheduler::Task::Task(function_t function, uint32_t period, Policy policy) TaskScheduler::Task::Task(function_t function, uint32_t period, Policy policy,
: function(function), period(period), enabled(true), policy(policy), int64_t startTick)
lastCall(-1), activationStats(), periodStats(), workloadStats(), : function(function), period(period), startTick(startTick), enabled(true),
missedEvents(0), failedEvents(0) policy(policy), lastCall(-1), activationStats(), periodStats(),
workloadStats(), missedEvents(0), failedEvents(0)
{ {
} }
......
...@@ -87,7 +87,7 @@ public: ...@@ -87,7 +87,7 @@ public:
* respected and the task will run consecutively for some time (See issue * respected and the task will run consecutively for some time (See issue
* #91). * #91).
*/ */
enum class Policy enum class Policy : uint8_t
{ {
ONE_SHOT, ///< Run the task one single timer. ONE_SHOT, ///< Run the task one single timer.
SKIP, // Skips lost executions and stays aligned with the original SKIP, // Skips lost executions and stays aligned with the original
...@@ -133,16 +133,12 @@ public: ...@@ -133,16 +133,12 @@ public:
std::vector<TaskStatsResult> getTaskStats(); std::vector<TaskStatsResult> getTaskStats();
private: private:
/**
* @brief Check the start time of the tasks in the agenda and moves them in
* the future respecting the period in respect to the original start time.
*/
void normalizeTasks();
struct Task struct Task
{ {
function_t function; function_t function;
uint32_t period; // [ms] uint32_t period; // [ms]
int64_t startTick; ///< First activation time, useful for synchronizing
///< tasks.
bool enabled; ///< Whether the task should be executed. bool enabled; ///< Whether the task should be executed.
Policy policy; Policy policy;
int64_t lastCall; ///< Last activation tick for statistics computation. int64_t lastCall; ///< Last activation tick for statistics computation.
...@@ -163,8 +159,10 @@ private: ...@@ -163,8 +159,10 @@ private:
* @param function The std::function to be called * @param function The std::function to be called
* @param period The Period in [ms] * @param period The Period in [ms]
* @param policy The task policy in case of a miss * @param policy The task policy in case of a miss
* @param startTick The first activation time
*/ */
explicit Task(function_t function, uint32_t period, Policy policy); explicit Task(function_t function, uint32_t period, Policy policy,
int64_t startTick);
// Delete copy constructor and copy assignment operator to avoid copying // Delete copy constructor and copy assignment operator to avoid copying
// and force moving // and force moving
...@@ -209,6 +207,21 @@ private: ...@@ -209,6 +207,21 @@ private:
using EventQueue = using EventQueue =
std::priority_queue<Event, std::vector<Event>, std::greater<Event>>; std::priority_queue<Event, std::vector<Event>, std::greater<Event>>;
/**
* @brief Instantiates a new EventQueue backed by a vector with a
* capacity of `MAX_TASKS` to avoid reallocations when inserting new events.
*/
static EventQueue makeAgenda();
/**
* @brief Populates the agenda prior to starting the scheduler. Checks the
* start time of the tasks in the agenda and moves them in the future
* respecting the period in respect to the original start time.
* @note This function must be called before starting the scheduler or the
* agenda will be empty.
*/
void populateAgenda();
void run() override; void run() override;
/** /**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment