Created
January 23, 2023 06:38
-
-
Save SteveBronder/aa3eaa3ba3ef15efdbeea497bc8aa727 to your computer and use it in GitHub Desktop.
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
#include <Eigen/Dense> | |
#include <array> | |
#include <atomic> | |
#include <chrono> | |
#include <cmath> | |
#include <ctime> | |
#include <iomanip> | |
#include <iostream> | |
#include <memory> | |
#include <sstream> | |
#include <thread> | |
#include <vector> | |
#define likely(x) __builtin_expect((x), 1) | |
#define unlikely(x) __builtin_expect((x), 0) | |
#define NOINLINE __attribute__((noinline)) | |
#define ALWAYS_INLINE __attribute__((always_inline)) | |
std::string Time_Point_String( | |
const std::chrono::high_resolution_clock::time_point& timePoint) { | |
time_t timeNow = std::chrono::system_clock::to_time_t(timePoint); | |
tm time = *localtime(&timeNow); | |
std::stringstream timeString; | |
timeString << std::setfill('0') << 1900 + time.tm_year << "-" | |
<< std::setw(2) << time.tm_mon + 1 << "-" << std::setw(2) | |
<< time.tm_mday << " " << std::setw(2) << time.tm_hour << ":" | |
<< std::setw(2) << time.tm_min << ":" << std::setw(2) | |
<< time.tm_sec; | |
return timeString.str(); | |
} | |
#define DEBUG_LIMITER | |
static constexpr int debug_iters = 80; | |
static Eigen::MatrixXd debug_info(debug_iters, 7); | |
/** | |
* @tparam F Function to call in the limiter | |
* @tparam Allowance # of calls permitted | |
* @tparam Burst burst amount allowed | |
* @tparam Num Max number of calls in ratio | |
* @tparam Dem Time in seconds for ratio. The refresh rate is Num / Dem | |
*/ | |
template <typename F, typename Clock, int Burst, typename time_ratio> | |
struct rate_limiter { | |
double allowance_{Burst}; | |
decltype(Clock::then()) last_time_; | |
F f_; | |
/** | |
* Init the limiter | |
* @tparam FF same as F, extra template for perfect forwarding | |
* @param f function to call. | |
*/ | |
template <typename FF> | |
rate_limiter(FF&& f) : f_(std::forward<FF>(f)), last_time_(Clock::then()) {} | |
bool run(int& x, int incr) { | |
auto curr_time = Clock::now(); | |
using seconds_d = std::chrono::duration<double, std::ratio<1, 1>>; | |
auto time_increment = seconds_d(curr_time - last_time_); | |
#ifdef DEBUG_LIMITER | |
debug_info(incr, 3) = time_increment.count(); | |
debug_info(incr, 4) = allowance_; | |
#endif | |
auto token_adj = | |
time_increment.count() * (static_cast<double>(time_ratio::num) / | |
static_cast<double>(time_ratio::den)); | |
#ifdef DEBUG_LIMITER | |
debug_info(incr, 5) = token_adj; | |
#endif | |
allowance_ = std::min(static_cast<double>(Burst), allowance_ + token_adj); | |
last_time_ = curr_time; | |
#ifdef DEBUG_LIMITER | |
debug_info(incr, 6) = allowance_; | |
#endif | |
if (allowance_ > 1) { | |
allowance_--; | |
return f_(x); | |
} | |
return false; | |
} | |
}; | |
static auto base_time = std::chrono::high_resolution_clock::now(); | |
template <typename Incr = std::ratio<1, 1>> | |
struct TestClock { | |
// Don't increment clock | |
static auto then() noexcept { return base_time; } | |
// Increment clock by Incr | |
static auto now() noexcept { | |
base_time += std::chrono::duration<long int, Incr>(1L); | |
return base_time; | |
} | |
}; | |
int main() { | |
using Clockaroo = TestClock<std::ratio<1, 100>>; | |
auto blah = [](auto& x) { | |
x++; | |
return true; | |
}; | |
constexpr int Burst = 10; | |
constexpr int Calls = 10; | |
constexpr int per_second = 1; | |
using time_ratio = std::ratio<Calls, per_second>; | |
rate_limiter<decltype(blah), Clockaroo, Burst, time_ratio> limiter(blah); | |
int tracker = 0; | |
auto start = Clockaroo::then(); | |
for (int i = 0; i < debug_iters; ++i) { | |
limiter.run(tracker, i); | |
auto curr = Clockaroo::then(); | |
double time_res = | |
std::chrono::duration<double, std::ratio<1, 1>>(curr - start) | |
.count(); | |
#ifdef DEBUG_LIMITER | |
debug_info(i, 0) = i; | |
debug_info(i, 2) = tracker; | |
debug_info(i, 1) = time_res; | |
#endif | |
} | |
#ifdef DEBUG_LIMITER | |
Eigen::IOFormat CleanFmt(4, 0, " | ", "\n", "[", "]"); | |
std::cout << " iter time calls t_incr tk_pre t_adj tk_post" | |
<< std::endl; | |
std::cout << debug_info.format(CleanFmt); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment