|
10 | 10 |
|
11 | 11 | #include <algorithm> |
12 | 12 | #include <chrono> |
13 | | -#include <cstdio> |
14 | 13 | #include <mutex> |
15 | | -#include <ratio> |
16 | 14 | #include <thread> |
17 | 15 |
|
18 | 16 | namespace ycsbc { |
19 | 17 |
|
20 | 18 | namespace utils { |
21 | 19 |
|
22 | | -// Token bucket rate limiter for single client |
| 20 | +// Token bucket rate limiter for single client. |
| 21 | +// Units: rate_ = tokens/sec, bucket_ = tokens. |
23 | 22 | class RateLimiter { |
24 | 23 | public: |
25 | | - RateLimiter(int64_t r, int64_t b) : r_(r * TOKEN_PRECISION), b_(b * TOKEN_PRECISION), tokens_(0), last_(Clock::now()) {} |
| 24 | + RateLimiter(int64_t r, int64_t b) |
| 25 | + : rate_(static_cast<double>(r)), |
| 26 | + bucket_(static_cast<double>(b)), |
| 27 | + tokens_(0.0), |
| 28 | + last_(Clock::now()) {} |
26 | 29 |
|
27 | 30 | inline void Consume(int64_t n) { |
28 | 31 | std::unique_lock<std::mutex> lock(mutex_); |
29 | 32 |
|
30 | | - if (r_ <= 0) { |
| 33 | + if (rate_ <= 0.0) { |
31 | 34 | return; |
32 | 35 | } |
33 | 36 |
|
34 | | - // refill tokens |
35 | | - auto now = Clock::now(); |
36 | | - auto diff = std::chrono::duration_cast<Duration>(now - last_); |
37 | | - tokens_ = std::min(b_, tokens_ + diff.count() * r_ / 1000000000); |
38 | | - last_ = now; |
| 37 | + RefillLocked(); |
39 | 38 |
|
40 | 39 | // check tokens |
41 | | - tokens_ -= n * TOKEN_PRECISION; |
| 40 | + tokens_ -= static_cast<double>(n); |
42 | 41 |
|
43 | 42 | // sleep |
44 | | - if (tokens_ < 0) { |
| 43 | + if (tokens_ < 0.0) { |
45 | 44 | lock.unlock(); |
46 | | - int64_t wait_time = -tokens_ * 1000000000 / r_; |
47 | | - std::this_thread::sleep_for(std::chrono::nanoseconds(wait_time)); |
| 45 | + const auto wait_time = Duration(-tokens_ / rate_); |
| 46 | + std::this_thread::sleep_for(wait_time); |
48 | 47 | } |
49 | 48 | } |
50 | 49 |
|
51 | 50 | inline void SetRate(int64_t r) { |
52 | 51 | std::lock_guard<std::mutex> lock(mutex_); |
53 | 52 |
|
54 | | - // refill tokens |
55 | | - auto now = Clock::now(); |
56 | | - auto diff = std::chrono::duration_cast<Duration>(now - last_); |
57 | | - tokens_ = std::min(b_, tokens_ + diff.count() * r_ * TOKEN_PRECISION / 1000000000); |
58 | | - last_ = now; |
59 | | - |
60 | | - // set rate |
61 | | - r_ = r * TOKEN_PRECISION; |
| 53 | + RefillLocked(); |
| 54 | + rate_ = static_cast<double>(r); |
62 | 55 | } |
63 | 56 |
|
64 | 57 | private: |
65 | 58 | using Clock = std::chrono::steady_clock; |
66 | | - using Duration = std::chrono::nanoseconds; |
67 | | - static constexpr int64_t TOKEN_PRECISION = 10000; |
| 59 | + using Duration = std::chrono::duration<double>; |
| 60 | + |
| 61 | + inline void RefillLocked() { |
| 62 | + auto now = Clock::now(); |
| 63 | + Duration diff = now - last_; |
| 64 | + tokens_ = std::min(bucket_, tokens_ + diff.count() * rate_); |
| 65 | + last_ = now; |
| 66 | + } |
68 | 67 |
|
69 | 68 | std::mutex mutex_; |
70 | | - int64_t r_; |
71 | | - int64_t b_; |
72 | | - int64_t tokens_; |
| 69 | + double rate_; |
| 70 | + double bucket_; |
| 71 | + double tokens_; |
73 | 72 | Clock::time_point last_; |
74 | 73 | }; |
75 | 74 |
|
|
0 commit comments