Skip to content

Instantly share code, notes, and snippets.

@SteveBronder
Created January 23, 2023 06:38
Show Gist options
  • Save SteveBronder/aa3eaa3ba3ef15efdbeea497bc8aa727 to your computer and use it in GitHub Desktop.
Save SteveBronder/aa3eaa3ba3ef15efdbeea497bc8aa727 to your computer and use it in GitHub Desktop.
#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