Skip to content

Commit

Permalink
[fiber] Don't inline context methods (+40% performance improvement!) …
Browse files Browse the repository at this point in the history
…+ fix unit tests
  • Loading branch information
henrikssn committed Sep 11, 2020
1 parent 9385b49 commit 9b07eff
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 125 deletions.
11 changes: 5 additions & 6 deletions examples/generic/fiber/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@ using namespace Board;
using namespace std::chrono_literals;

constexpr uint32_t cycles = 1000'000;
volatile uint32_t counter = 0;
volatile uint32_t f1counter = 0, f2counter = 0;

void f1() {
while (++counter < cycles) {
while (++f1counter < cycles / 2) {
modm::yield();
}
}

void f2() {
while (++counter < cycles) {
asm volatile ("");
while (++f2counter < cycles / 2) {
modm::yield();
}
}
Expand All @@ -36,8 +35,8 @@ modm::fiber::Stack<2048> stack1, stack2;
modm::Fiber fiber1(stack1, &f1), fiber2(stack2, &f2);


// Blue pill (M3 64MHz): Executed 1000000 in 1520841us (657530.94 yields per second)
// Feather M0 (M0+ 48MHz): Executed 1000000 in 2190108us (456598.50 yields per second)
// Blue pill (M3 72MHz): Executed 1000000 in 1098591us (910256.88 yields per second)
// Feather M0 (M0+ 48MHz): Executed 1000000 in 1944692us (514220.25 yields per second)
int main( int argc, char * argv[])
{
Board::initialize();
Expand Down
7 changes: 1 addition & 6 deletions src/modm/processing/fiber/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,17 @@ struct modm_context {
extern modm_context main_context;

/* Prepares the stack to look like the "from" arg after modm_jumpcontext as run. */
inline modm_context
modm_context
modm_makecontext(modm_stack_t* stack, std::size_t stack_size, void (*fn)(void), void (*end)(void));

/* Switches control from the main context to the user context. */
modm_always_inline
void
modm_startcontext(const modm_context &to);

/* Jumps from the "from" user context to the "to" user context. */
modm_always_inline
void
modm_jumpcontext(modm_context* from, const modm_context &to);

/* Switches control back to the main context from the user context. */
modm_always_inline
void
modm_endcontext();

#include "context_impl.hpp"
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
// ----------------------------------------------------------------------------

#include "context.hpp"
#include <modm/platform/device.hpp>
#include <modm/debug/logger.hpp>

Expand Down Expand Up @@ -39,14 +40,9 @@ modm_makecontext(modm_stack_t* stack, std::size_t stack_size,
void
modm_startcontext(const modm_context &to)
{
modm_stack_t** main_sp = &main_context.sp;
modm_stack_t* to_sp = to.sp;
asm volatile
(
"adr r3, 1f" "\n\t" // Load address of instruction after "pop {pc}" into r3
"add r3, r3, #1" "\n\t" // Stay in thumb mode
"push {r3}" "\n\t" // Save instruction address to stack (to be used for PC later)

"push {lr}" "\n\t"
"push {r4-r11, lr}" "\n\t"

"mov r3, sp" "\n\t"
Expand All @@ -57,48 +53,34 @@ modm_startcontext(const modm_context &to)
"orr r3, r2" "\n\t"
"msr control, r3" "\n\t"

"pop {r4-r11}" "\n\t"
"pop {r3}" "\n\t"
"mov lr, r3" "\n\t"

"pop {r4-r11, lr}" "\n\t" // LR points to the "done" function
"pop {pc}" "\n\t" // Perform the jump
".align 2" "\n\t"
"1:" "\n\t"

/*outputs*/: [main_sp] "+r" (main_sp), [to_sp] "+r" (to_sp)
/*inputs*/:
/*clobbers*/: "r2", "r3", "memory"
/*outputs*/:
/*inputs*/: [main_sp] "r" (&main_context.sp), [to_sp] "r" (to.sp)
/*clobbers*/: "r2", "r3", "memory"
);
}

void
modm_jumpcontext(modm_context* from, const modm_context &to)
{
register modm_stack_t** from_sp asm("r0") = &from->sp;
register modm_stack_t* to_sp asm("r1") = to.sp;
asm volatile
(
"adr r3, 1f" "\n\t" // Load address of instruction after "pop {pc}" into r3
"add r3, r3, #1" "\n\t" // Stay in thumb mode
"push {r3}" "\n\t" // Save instruction address to stack (to be used for PC later)

"push {lr}" "\n\t" // Later used as PC
"push {r4-r11, lr}" "\n\t"

"mov r3, sp" "\n\t"
"str r3, [%[from_sp]]" "\n\t" // Store the SP in "from"
"mov sp, %[to_sp]" "\n\t" // Restore SP from "to"

"pop {r4-r11}" "\n\t"
"pop {r3}" "\n\t"
"mov lr, r3" "\n\t"

"pop {pc}" "\n\t" // Perform the jump
".align 2" "\n\t"
"1:" "\n\t"
"pop {r4-r11, lr}" "\n\t"
"pop {pc}" "\n\t" // Perform the jump

/*outputs*/: [from_sp] "+r" (from_sp), [to_sp] "+r" (to_sp)
/*inputs*/:
/*clobbers*/: "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "memory"
/*outputs*/:
/*inputs*/: [from_sp] "r" (&from->sp), [to_sp] "r" (to.sp)
/*clobbers*/: "r3", "memory"
);
}

Expand All @@ -112,11 +94,8 @@ modm_endcontext()
"bic r1, r1, r2" "\n\t"
"msr control, r1" "\n\t"

"pop {r4-r11}" "\n\t"
"pop {r3}" "\n\t"
"mov lr, r3" "\n\t"

"pop {pc}" "\n\t" // Perform the jump
"pop {r4-r11, lr}" "\n\t"
"pop {pc}" "\n\t" // Perform the jump

/*outputs*/:
/*inputs*/:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
*/
// ----------------------------------------------------------------------------


#include "context.hpp"
#include <modm/platform/device.hpp>
#include <modm/debug/logger.hpp>

Expand Down Expand Up @@ -37,13 +39,8 @@ modm_makecontext(modm_stack_t* stack, std::size_t stack_size,

void
modm_startcontext(const modm_context &to) {
modm_stack_t** main_sp = &main_context.sp;
modm_stack_t* to_sp = to.sp;
asm volatile(
"adr r3, 1f\n\t" // Load address of instruction after "pop {pc}" into r3
"add r3, r3, #1\n\t" // Stay in thumb mode
"push {r3}\n\t" // Save instruction address to stack (to be used for PC later)

"push {lr}\n\t"
"push {r4-r7, lr}\n\t"
"mov r4, r8\n\t"
"mov r5, r9\n\t"
Expand All @@ -67,24 +64,17 @@ modm_startcontext(const modm_context &to) {
"pop {r4-r7}\n\t"
"pop {r3}\n\t"
"mov lr, r3\n\t"

"pop {pc}\n\t" // Perform the jump
".align 2\n\t"
"1:\n\t"
/*outputs*/: [main_sp] "+r" (main_sp), [to_sp] "+r" (to_sp)
/*inputs*/:
/*clobbers*/: "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "memory");

/*outputs*/:
/*inputs*/: [main_sp] "r" (&main_context.sp), [to_sp] "r" (to.sp)
/*clobbers*/: "r2", "r3", "memory");
}

void
modm_jumpcontext(modm_context* from, const modm_context &to) {
register modm_stack_t** from_sp asm("r0") = &from->sp;
register modm_stack_t* to_sp asm("r1") = to.sp;
asm volatile(
"adr r3, 1f\n\t" // Load address of instruction after "pop {pc}" into r3
"add r3, r3, #1\n\t" // Stay in thumb mode
"push {r3}\n\t" // Save instruction address to stack (to be used for PC later)

"push {lr}\n\t"
"push {r4-r7, lr}\n\t"
"mov r4, r8\n\t"
"mov r5, r9\n\t"
Expand All @@ -104,13 +94,11 @@ modm_jumpcontext(modm_context* from, const modm_context &to) {
"pop {r4-r7}\n\t"
"pop {r3}\n\t"
"mov lr, r3\n\t"

"pop {pc}\n\t" // Perform the jump
".align 2\n\t"
"1:\n\t"
/*outputs*/: [from_sp] "+r" (from_sp), [to_sp] "+r" (to_sp)
/*inputs*/:
/*clobbers*/: "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "memory");

/*outputs*/:
/*inputs*/: [from_sp] "r" (&from->sp), [to_sp] "r" (to.sp)
/*clobbers*/: "r3", "memory");
}

void
Expand All @@ -129,9 +117,9 @@ modm_endcontext() {
"pop {r4-r7}\n\t"
"pop {r3}\n\t"
"mov lr, r3\n\t"

"pop {pc}\n\t" // Perform the jump

/*outputs*/:
/*inputs*/:
/*clobbers*/: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "memory");
/*clobbers*/: "memory");
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
// ----------------------------------------------------------------------------

#include "context.hpp"

modm_context
modm_makecontext(modm_stack_t* stack, std::size_t stack_size,
Expand All @@ -20,7 +21,6 @@ modm_makecontext(modm_stack_t* stack, std::size_t stack_size,
return {sp, stack_size};
}

modm_always_inline
void
modm_startcontext(const modm_context &to) {
modm_jumpcontext(&main_context, to);
Expand Down Expand Up @@ -60,7 +60,6 @@ modm_jumpcontext(modm_context* from, const modm_context &to) {
/*clobbers*/: "rax", "memory");
}

modm_always_inline
void
modm_endcontext() {
modm_context dummy;
Expand Down
8 changes: 7 additions & 1 deletion src/modm/processing/fiber/fiber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

namespace modm {

namespace fiber {

Scheduler scheduler;

} // namespace fiber

void Fiber::dumpStack() {
#ifdef MODM_BOARD_HAS_LOGGER
MODM_LOG_DEBUG
Expand All @@ -34,7 +40,7 @@ void Fiber::dumpStack() {
void Waitable::wait() {
using ::modm::fiber::scheduler;
pushWaiter(scheduler.removeCurrent());
yield();
scheduler.currentFiber()->jump(*scheduler.lastFiber()->next());
}

void Waitable::signal() {
Expand Down
5 changes: 2 additions & 3 deletions src/modm/processing/fiber/fiber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ class Fiber

protected:
inline void
jump(Fiber& other)
{ modm_jumpcontext(&ctx_, other.ctx_); }
jump(Fiber& other);

private:
Fiber() = default;
Expand Down Expand Up @@ -314,7 +313,7 @@ class Scheduler
Fiber* current_fiber_ = nullptr;
};

static Scheduler scheduler;
extern Scheduler scheduler;

} // namespace fiber

Expand Down
57 changes: 29 additions & 28 deletions src/modm/processing/fiber/fiber_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,9 @@ void yield()
{
using fiber::scheduler;
Fiber* current = scheduler.currentFiber();
Fiber* next;
if (scheduler.last_fiber_->next() != current) {
next = scheduler.lastFiber()->next();
} else {
next = current->next();
scheduler.last_fiber_ = current;
}
if (current == next) return;
scheduler.current_fiber_ = next;
Fiber* next = current->next();
if (next == current) return;
scheduler.last_fiber_ = current;
current->jump(*next);
}

Expand All @@ -46,16 +40,23 @@ Fiber::Fiber(fiber::Stack<size>& stack, void(*f)())
fiber::scheduler.registerFiber(this);
}

void
Fiber::jump(Fiber& other)
{
fiber::scheduler.current_fiber_ = &other;
modm_jumpcontext(&ctx_, other.ctx_);
}

void Fiber::done() {
using fiber::scheduler;
Fiber* current = scheduler.currentFiber();
Fiber* next = current->next();
scheduler.removeCurrent();
if (scheduler.empty()) {
// #ifdef MODM_BOARD_HAS_LOGGER
// MODM_LOG_DEBUG << "Fiber::done: Returning control to main thread." << modm::endl;
// #endif
scheduler.current_fiber_ = nullptr;
modm_endcontext();
}
yield();
current->jump(*next);
}

template<class Data_t>
Expand All @@ -80,7 +81,7 @@ Data_t Channel<Data_t>::recv() {
wait();
}
// Now we are in full or ready state.
modm_assert(!empty(), "Channel::poll.empty", "Channel should not be empty");
modm_assert(!empty(), "Channel::recv.empty", "Channel should not be empty");
Data_t result;
if (--size_) {
result = std::move(buffer_[size_]);
Expand All @@ -97,27 +98,27 @@ void Scheduler::registerFiber(Fiber* fiber) {
if (last_fiber_ == nullptr) {
fiber->next(fiber);
last_fiber_ = fiber;
current_fiber_ = fiber;
return;
}
runLast(fiber);
}

void Scheduler::start() {
if (last_fiber_ == nullptr) return;
// #ifdef MODM_BOARD_HAS_LOGGER
// MODM_LOG_DEBUG << "Starting scheduler with fibers [ current = " << currentFiber()
// << ", last = " << lastFiber()
// << " ] " << modm::endl;
// for (Fiber* fiber = currentFiber();; fiber = fiber->next()) {
// MODM_LOG_DEBUG
// << "(" << modm::hex << fiber
// << ") { sp: " << fiber->ctx_.sp
// << ", next: " << fiber->next() << " }"
// << modm::endl;
// if (fiber->next() == currentFiber()) break;
// }
// #endif
current_fiber_ = last_fiber_->next();
#ifdef MODM_BOARD_HAS_LOGGER
MODM_LOG_DEBUG << "Starting scheduler with fibers [ current = " << currentFiber()
<< ", last = " << lastFiber()
<< " ] " << modm::endl;
for (Fiber* fiber = currentFiber();; fiber = fiber->next()) {
MODM_LOG_DEBUG
<< "(" << modm::hex << fiber
<< ") { sp: " << fiber->ctx_.sp
<< ", next: " << fiber->next() << " }"
<< modm::endl;
if (fiber->next() == currentFiber()) break;
}
#endif
modm_startcontext(currentFiber()->ctx_);
}

Expand Down
Loading

0 comments on commit 9b07eff

Please sign in to comment.