Skip to content

Commit

Permalink
Fixed Bug && Optimize: Timer
Browse files Browse the repository at this point in the history
  • Loading branch information
8sileus committed Mar 19, 2024
1 parent b634fee commit 7341090
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 46 deletions.
2 changes: 1 addition & 1 deletion zedio/common/static_math.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ template <typename T>
requires std::is_integral_v<T>
static inline consteval auto static_pow(T x, T y) -> T {
T result = 1;
while (--y) {
while (y--) {
result *= x;
}
return result;
Expand Down
4 changes: 2 additions & 2 deletions zedio/runtime/builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ class Builder {

[[nodiscard]]
auto driver() -> DriverBuilder<R> {
return *static_cast<DriverBuilder<R> *>(this);
return DriverBuilder<R>{*this};
}

[[nodiscard]]
auto scheduler() -> SchedulerBuilder<R> {
return *static_cast<SchedulerBuilder<R> *>(this);
return SchedulerBuilder<R>{*this};
}

protected:
Expand Down
6 changes: 3 additions & 3 deletions zedio/runtime/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ namespace zedio::runtime::detail {

/// Timer
// Max wheel level
static inline constexpr std::size_t MAX_LEVEL = 6;
static inline constexpr std::size_t MAX_LEVEL{6uz};
// size of slot per wheel
static inline constexpr std::size_t SLOT_SIZE{64};
static inline constexpr std::size_t SLOT_SIZE{64uz};

/// Queue
static inline constexpr std::size_t LOCAL_QUEUE_CAPACITY{256};
static inline constexpr std::size_t LOCAL_QUEUE_CAPACITY{256uz};

struct Config {
// size of io_uring_queue entries
Expand Down
138 changes: 110 additions & 28 deletions zedio/runtime/timer/timer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

#include "zedio/common/error.hpp"
#include "zedio/common/static_math.hpp"
#include "zedio/runtime/timer/util.hpp"
#include "zedio/runtime/timer/wheel.hpp"

// C++
#include <variant>

namespace zedio::runtime::detail {

class Timer;
Expand Down Expand Up @@ -33,73 +37,151 @@ class Timer {
if (expiration_time >= start_ + std::chrono::milliseconds{MAX_MS}) [[unlikely]] {
return std::unexpected{make_zedio_error(Error::TooLongTime)};
}
auto interval
std::size_t interval
= std::chrono::duration_cast<std::chrono::milliseconds>(expiration_time - start_)
.count();

auto [entry, result] = Entry::make(expiration_time, handle);
wheel.add_entry(std::move(entry), interval);

std::visit(
[this, interval, &entry]<typename T>(T &wheel) {
if constexpr (std::is_same_v<T, std::monostate>) {
auto wheel = std::make_unique<Wheel<0>>();
this->level_up_and_add_entry(std::move(wheel), std::move(entry), interval);
} else {
this->level_up_and_add_entry(std::move(wheel), std::move(entry), interval);
}
},
root_wheel_);
num_entries_ += 1;
return result;
}

void remove_entry(std::shared_ptr<Entry> &&entry) {
auto interval = std::chrono::duration_cast<std::chrono::milliseconds>(
entry->expiration_time_ - start_)
.count();
wheel.remove_entry(std::move(entry), interval);
assert(root_wheel_.index() != 0 && num_entries_ != 0);
std::visit(
[this, interval, &entry]<typename T>(T &wheel) {
if constexpr (std::is_same_v<T, std::monostate>) {
std::unreachable();
LOG_ERROR("no entries");
} else {
wheel->remove_entry(std::move(entry), interval);
}
},
root_wheel_);
num_entries_ -= 1;
if (num_entries_ == 0) {
root_wheel_ = std::monostate{};
}
// TODO consider level_downs
}

[[nodiscard]]
auto next_expiration_time() -> std::optional<uint64_t> {
if (wheel.empty()) {
return std::nullopt;
}
return wheel.next_expiration_time()
- std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_)
.count();
return std::visit(
[this]<typename T>(T &ptr) -> std::optional<uint64_t> {
if constexpr (std::is_same_v<T, std::monostate>) {
return std::nullopt;
} else {
return ptr->next_expiration_time()
- std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_)
.count();
}
},
root_wheel_);
}

[[nodiscard]]
auto handle_expired_entries(runtime::detail::LocalQueue &local_queue,
runtime::detail::GlobalQueue &global_queue) {
runtime::detail::GlobalQueue &global_queue) -> std::size_t {
last_handle_time_ = std::chrono::steady_clock::now();

auto interval = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start_)
.count();
if (num_entries_ == 0 || root_wheel_.index() == 0) {
return 0uz;
}

auto interval
= std::chrono::duration_cast<std::chrono::milliseconds>(last_handle_time_ - start_)
.count();

assert(interval >= 0);

std::size_t count{0};

wheel.handle_expired_entries(local_queue,
global_queue,
count,
static_cast<std::size_t>(interval));
rotate_if_needed(interval);
std::visit(
[this, &count, &local_queue, &global_queue, interval]<typename T>(T &wheel) {
if constexpr (std::is_same_v<T, std::monostate>) {
std::unreachable();
} else {
wheel->handle_expired_entries(local_queue,
global_queue,
count,
static_cast<std::size_t>(interval));
num_entries_ -= count;
change_state(wheel, interval);
}
},
root_wheel_);
return count;
}

private:
void rotate_if_needed(std::size_t interval) {
auto n = interval / MS_PER_SLOT;
template <typename T>
void change_state(T &wheel, std::size_t interval) {
if (wheel->empty()) {
assert(num_entries_ == 0);
LOG_DEBUG("empty wheel {}", num_entries_);
root_wheel_ = std::monostate{};
return;
}
// TODO consider level_down
auto n = interval / wheel->ms_per_slot();
if (n > 0) {
wheel.rotate(n);
wheel->rotate(n);
}
start_ += std::chrono::milliseconds{wheel->ms_per_slot() * n};
}

template <class T>
void level_up_and_add_entry(T &&wheel, std::shared_ptr<Entry> &&entry, std::size_t interval) {
if constexpr (std::is_same_v<T, std::unique_ptr<Wheel<MAX_LEVEL + 1uz>>>) {
assert(false);
std::unreachable();
} else {
if (interval >= wheel->max_ms()) {
level_up_and_add_entry(wheel->level_up(std::move(wheel)),
std::move(entry),
interval);
} else {
wheel->add_entry(std::move(entry), interval);
root_wheel_ = std::move(wheel);
}
}
start_ += std::chrono::milliseconds{MS_PER_SLOT * n};
}

private:
//~ 12 days
static constexpr std::size_t MS_PER_SLOT{util::static_pow(64uz, MAX_LEVEL - 1)};
//~ 2 years
static constexpr std::size_t MAX_MS{util::static_pow(64uz, MAX_LEVEL)};
//~ 139 years
static constexpr std::size_t MAX_MS{util::static_pow(SLOT_SIZE, MAX_LEVEL + 1)};

private:
std::chrono::steady_clock::time_point start_{std::chrono::steady_clock::now()};
std::chrono::steady_clock::time_point last_handle_time_{std::chrono::steady_clock::now()};
Wheel<MS_PER_SLOT> wheel;
std::size_t num_entries_{0};
VariantWheelBuilder<MAX_LEVEL + 1uz>::Type root_wheel_{};

// std::variant<std::monostate,
// std::unique_ptr<Wheel<0>>,
// std::unique_ptr<Wheel<1>>,
// std::unique_ptr<Wheel<2>>,
// std::unique_ptr<Wheel<3>>,
// std::unique_ptr<Wheel<4>>,
// std::unique_ptr<Wheel<5>>,
// std::unique_ptr<Wheel<6>>>
// root_wheel_{};
};

} // namespace zedio::runtime::detail
23 changes: 23 additions & 0 deletions zedio/runtime/timer/util.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "zedio/runtime/timer/wheel.hpp"
// C
#include <cinttypes>
// C++
#include <memory>
#include <utility>
#include <variant>

namespace zedio::runtime::detail {

template <std::size_t... N>
static inline constexpr auto variant_wheel_helper_impl(std::index_sequence<N...>) {
return std::variant<std::monostate, std::unique_ptr<Wheel<N>>...>{};
}

template <std::size_t N>
struct VariantWheelBuilder {
using Type = decltype(variant_wheel_helper_impl(std::make_index_sequence<N>{}));
};

} // namespace zedio::runtime::detail
Loading

0 comments on commit 7341090

Please sign in to comment.