-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Felix Gündling <felix.guendling@gmail.com>
- Loading branch information
1 parent
8438534
commit 06f6d7c
Showing
6 changed files
with
217 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#pragma once | ||
|
||
#include <atomic> | ||
#include <functional> | ||
|
||
#include "boost/fiber/algo/work_stealing.hpp" | ||
#include "boost/fiber/buffered_channel.hpp" | ||
#include "boost/fiber/operations.hpp" | ||
|
||
#include "net/web_server/query_router.h" | ||
|
||
#include "motis/scheduler/scheduler_algo.h" | ||
|
||
namespace motis { | ||
|
||
struct runner { | ||
runner(std::size_t const n_threads, std::size_t const buffer_size) | ||
: init_barrier_{n_threads}, schedulers_{n_threads}, ch_{buffer_size} {} | ||
|
||
auto run_fn() { | ||
return [&]() { | ||
/* | ||
boost::fibers::use_scheduling_algorithm< | ||
boost::fibers::algo::work_stealing>(schedulers_.size()); | ||
*/ | ||
boost::fibers::use_scheduling_algorithm<scheduler_algo>( | ||
init_barrier_, schedulers_, ++next_id_); | ||
auto t = net::fiber_exec::task_t{}; | ||
while (ch_.pop(t) != boost::fibers::channel_op_status::closed) { | ||
t(); | ||
} | ||
}; | ||
} | ||
|
||
boost::fibers::detail::thread_barrier init_barrier_; | ||
std::atomic_uint32_t next_id_{0U}; | ||
std::vector<scheduler_algo*> schedulers_; | ||
net::fiber_exec::channel_t ch_; | ||
}; | ||
|
||
} // namespace motis |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#pragma once | ||
|
||
#include <cinttypes> | ||
#include <vector> | ||
|
||
#include "boost/context/detail/prefetch.hpp" | ||
#include "boost/fiber/algo/algorithm.hpp" | ||
#include "boost/fiber/detail/context_spinlock_queue.hpp" | ||
#include "boost/fiber/detail/thread_barrier.hpp" | ||
#include "boost/fiber/properties.hpp" | ||
#include "boost/fiber/scheduler.hpp" | ||
|
||
namespace motis { | ||
|
||
struct fiber_props : public boost::fibers::fiber_properties { | ||
fiber_props(boost::fibers::context*); | ||
|
||
// In order to keep request latency low, finishing already started requests | ||
// has to be prioritized over new requests. Otherwise, the server only starts | ||
// new requests and never finishes anything. | ||
enum class type : std::uint8_t { | ||
kWork, // follow-up work scheduled by work or I/O | ||
kIo // initial work scheduled by I/O (web request / batch query) | ||
} type_{type::kIo}; | ||
}; | ||
|
||
struct scheduler_algo | ||
: public boost::fibers::algo::algorithm_with_properties<fiber_props> { | ||
using ready_queue_t = boost::fibers::scheduler::ready_queue_type; | ||
|
||
scheduler_algo(boost::fibers::detail::thread_barrier&, | ||
std::vector<scheduler_algo*>& schedulers, | ||
std::uint32_t id); | ||
|
||
boost::fibers::context* steal() noexcept; | ||
|
||
virtual void awakened(boost::fibers::context* ctx, | ||
fiber_props& props) noexcept override; | ||
virtual boost::fibers::context* pick_next() noexcept override; | ||
virtual bool has_ready_fibers() const noexcept override; | ||
virtual void suspend_until( | ||
std::chrono::steady_clock::time_point const&) noexcept override; | ||
virtual void notify() noexcept override; | ||
|
||
std::vector<scheduler_algo*>& schedulers_; | ||
bool suspend_{false}; | ||
std::uint32_t id_; | ||
boost::fibers::detail::context_spinlock_queue work_queue_, io_queue_; | ||
std::mutex mtx_{}; | ||
std::condition_variable cnd_{}; | ||
bool flag_{false}; | ||
}; | ||
|
||
} // namespace motis |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#include "motis/scheduler/scheduler_algo.h" | ||
|
||
#include "boost/context/detail/prefetch.hpp" | ||
#include "boost/fiber/context.hpp" | ||
#include "boost/fiber/detail/context_spinlock_queue.hpp" | ||
#include "boost/fiber/properties.hpp" | ||
#include "boost/fiber/scheduler.hpp" | ||
#include "boost/fiber/type.hpp" | ||
|
||
namespace bf = boost::fibers; | ||
|
||
namespace motis { | ||
|
||
fiber_props::fiber_props(bf::context* ctx) : fiber_properties{ctx} {} | ||
|
||
scheduler_algo::scheduler_algo(boost::fibers::detail::thread_barrier& b, | ||
std::vector<scheduler_algo*>& schedulers, | ||
std::uint32_t const id) | ||
: schedulers_{schedulers}, id_{id} { | ||
schedulers_[id] = this; | ||
b.wait(); | ||
} | ||
|
||
void scheduler_algo::awakened(bf::context* ctx, fiber_props& props) noexcept { | ||
if (!ctx->is_context(bf::type::pinned_context)) { | ||
ctx->detach(); | ||
} | ||
auto const orig_type = props.type_; | ||
props.type_ = fiber_props::type::kWork; // Continuations are prioritized. | ||
orig_type == fiber_props::type::kWork ? work_queue_.push(ctx) | ||
: io_queue_.push(ctx); | ||
} | ||
|
||
bf::context* scheduler_algo::pick_next() noexcept { | ||
using boost::context::detail::prefetch_range; | ||
bf::context* victim = nullptr; | ||
if (victim = work_queue_.pop(); victim != nullptr) { | ||
// Highest priority: work continuation. | ||
prefetch_range(victim, sizeof(bf::context)); | ||
if (!victim->is_context(bf::type::pinned_context)) { | ||
bf::context::active()->attach(victim); | ||
} | ||
} else if (victim = io_queue_.pop(); victim != nullptr) { | ||
// Lower priority: I/O from our own queue. | ||
prefetch_range(victim, sizeof(bf::context)); | ||
if (!victim->is_context(bf::type::pinned_context)) { | ||
bf::context::active()->attach(victim); | ||
} | ||
} else { // Fallback: try to steal from another thread. | ||
auto id = 0U; | ||
auto count = std::size_t{0U}; | ||
auto size = schedulers_.size(); | ||
static thread_local std::minstd_rand generator{std::random_device{}()}; | ||
auto distribution = std::uniform_int_distribution<std::uint32_t>{ | ||
0, static_cast<std::uint32_t>(size - 1)}; | ||
|
||
do { | ||
do { | ||
++count; | ||
id = distribution(generator); | ||
} while (id == id_ /* don't steal from own scheduler */); | ||
victim = schedulers_[id]->steal(); | ||
} while (victim == nullptr && count < size); | ||
|
||
if (victim != nullptr) { | ||
prefetch_range(victim, sizeof(bf::context)); | ||
BOOST_ASSERT(!victim->is_context(bf::type::pinned_context)); | ||
bf::context::active()->attach(victim); | ||
} | ||
} | ||
return victim; | ||
} | ||
|
||
bool scheduler_algo::has_ready_fibers() const noexcept { | ||
return !(work_queue_.empty() && io_queue_.empty()); | ||
} | ||
|
||
bf::context* scheduler_algo::steal() noexcept { | ||
auto work = work_queue_.pop(); | ||
if (work != nullptr) { | ||
return work; | ||
} | ||
return io_queue_.pop(); | ||
} | ||
|
||
void scheduler_algo::suspend_until( | ||
std::chrono::steady_clock::time_point const& time_point) noexcept { | ||
if (suspend_) { | ||
if ((std::chrono::steady_clock::time_point::max)() == time_point) { | ||
auto lk = std::unique_lock<std::mutex>{mtx_}; | ||
cnd_.wait(lk, [this]() { return flag_; }); | ||
flag_ = false; | ||
} else { | ||
auto lk = std::unique_lock<std::mutex>{mtx_}; | ||
cnd_.wait_until(lk, time_point, [this]() { return flag_; }); | ||
flag_ = false; | ||
} | ||
} | ||
} | ||
|
||
void scheduler_algo::notify() noexcept { | ||
if (suspend_) { | ||
auto lk = std::unique_lock<std::mutex>{mtx_}; | ||
flag_ = true; | ||
lk.unlock(); | ||
cnd_.notify_all(); | ||
} | ||
} | ||
|
||
} // namespace motis |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters