-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for RDTSC, apply to token bucket
The new module lib.tsc provides a generic Time Stamp Counter mechanism for measuring time intervals based on different time sources. One such source makes use of the CPU's TSC register via the rdtsc instruction for low-latency timing purposes. The rdtsc method is made the default time source for the token bucket, formerly provided by core.lib, now moved to a separate module lib.token_bucket. The commit also includes documentation for the token bucket, which was previously missing.
- Loading branch information
1 parent
b9da7ca
commit fb29cd0
Showing
5 changed files
with
434 additions
and
78 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
### Token Bucket (lib.token_bucket) | ||
|
||
This module implements a [token | ||
bucket](https://en.wikipedia.org/wiki/Token_bucket) for rate-limiting | ||
of arbitrary events. The bucket is filled with tokens at a constant | ||
rate up to a given maximum called the *burst_size*. Tokens are added | ||
and removed in integer quantities. An event can only take place if at | ||
least one token is available. A burst of back-to-back events is | ||
allowed to happen by consuming all available tokens at a given point | ||
in time. The maximum size of such a burst is determined by the | ||
capacity of the bucket, hence the name *burst_size*. | ||
|
||
The token bucket is updated in a lazy fashion, i.e. only when a | ||
request for tokens cannot be satisfied immediately. | ||
|
||
By default, a token bucket uses the `rdtsc` time source via the | ||
[`tsc`](./README.tsc.md) module to minimise overhead. To override, | ||
the `default_source` parameter of the `tsc` module must be set | ||
to the desired value. | ||
|
||
#### Functions | ||
|
||
— Function **new** *config* | ||
|
||
Creates an instance of a token bucket. The required *config* argument | ||
must be a table with the following keys. | ||
|
||
— Key **rate** | ||
|
||
*Required*. The rate in units of Hz at which tokens are placed in the | ||
bucket as an arbitrary floating point number larger than zero. | ||
|
||
— Key **burst_size** | ||
|
||
*Optional*. The maximum number of tokens that can be stored in the | ||
bucket. The default is **rate** tokens, i.e. the amount of tokens | ||
accumulated over one second rounded up to the next integer. | ||
|
||
#### Methods | ||
|
||
The object returned by the **new** function provides the following | ||
methods. | ||
|
||
— Method **token_bucket:set** [*rate*], [*burst_size*] | ||
|
||
Set the rate and burst size to the values *rate* and *burst_size*, | ||
respectively, and fill the bucket to capacity. If *rate* is `nil`, | ||
the rate remains unchanged. If *burst_size* is `nil`, the burst size | ||
is set to the number of tokens that will be accumulated over one | ||
second with the new rate (like in the **new** function). | ||
|
||
— Method **token_bucket:get** | ||
|
||
Returns the current rate and burst size. | ||
|
||
— Method **token_bucket:can_take** [*n*] | ||
|
||
Returns `true` if at least *n* tokens are available, `false` | ||
otherwise. If *n* is `nil`, the bucket is checked for a single token. | ||
|
||
— Method **token_bucket:take** [*n*] | ||
|
||
If at least *n* tokens are available, they are removed from the bucket | ||
and the method returns `true`. Otherwise, the bucket remains | ||
unchanged and `false` is returned. If *n* is `nil`, the bucket is | ||
checked for a single token. | ||
|
||
— Method **token_bucket:take_burst** | ||
|
||
Takes all available tokens from the bucket and returns that number. |
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,112 @@ | ||
### Time Stamp Counter (lib.tsc) | ||
|
||
A Time Stamp Counter (TSC) is an unsigned 64-bit value which increases | ||
at a fixed frequency. The latter property provides a measure of the | ||
time that has passed between two readings of the counter in units of | ||
clock ticks. | ||
|
||
To convert a value in clock ticks to the corresponding number of | ||
seconds requires a measurement of the actual frequency at which the | ||
TSC runs. This is referred to as calibration. The `tsc` module | ||
provides a uniform interface to TSCs based on different time sources. | ||
|
||
Example usage | ||
```lua | ||
local tsc = require('lib.tsc') | ||
local sleep = require("ffi").C.sleep | ||
local function wait(t) | ||
print("source " .. t:source()) | ||
local s1 = t:stamp() | ||
sleep(1) | ||
local s2 = t:stamp() | ||
print(("clock ticks %s, nanoseconds %s"):format(tostring(s2 - s1), | ||
tostring(t:to_ns(s2 - s1)))) | ||
end | ||
|
||
wait(tsc.new()) | ||
-- Override default | ||
tsc.default_source = 'system' | ||
wait(tsc.new()) | ||
``` | ||
|
||
#### Parameters | ||
|
||
Parameters can be set by | ||
|
||
```lua | ||
local tsc = require('lib.tsc') | ||
tsc.<parameter> = <value> | ||
``` | ||
|
||
— Parameter **default_source** *source* | ||
|
||
The time source used by a new TSC instance if no **source** key is | ||
specified. The default is `rdtsc`. | ||
|
||
#### Functions | ||
|
||
— Function **new** *config* | ||
|
||
Create a new TSC instance. The optional *config* argument is a table | ||
with the following keys. | ||
|
||
— Key **source** | ||
|
||
*Optional*. The name of the timing source to be used with this | ||
instance. The following sources are available. The default is `rdtsc` | ||
(or whatever has been set by the `default_source` parameter). | ||
|
||
* `system` | ||
|
||
This source uses `clock_gettime(2)` with `CLOCK_MONOTONIC` provided | ||
by `ffi.C.get_time_ns()`. The frequency is exactly 1e9 Hz, | ||
i.e. one tick per nanosecond. | ||
|
||
* `rdtsc` | ||
|
||
This source uses the [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter) CPU | ||
register via the `rdtsc` instruction, provided that the platform | ||
supports the `constant_tsc` and `nonstop_tsc` features. If these | ||
features are not present, a warning is printed and the TSC falls | ||
back to the `system` time source. | ||
|
||
The TSC register is consistent for all cores of a CPU. However, | ||
the calling program is responsible for setting the CPU affinity on | ||
multi-socket systems. | ||
|
||
The `system` time source is used for calibration. | ||
|
||
— Function **rdtsc** | ||
|
||
Returns the current value of the CPU's TSC register through the | ||
`rdtsc` instruction as a `uint64_t` object. | ||
|
||
#### Methods | ||
|
||
The object returned by the **new** function provides the following | ||
methods. | ||
|
||
— Method **tsc:source** | ||
|
||
Returns the name of the time source, i.e. `rdtsc` or `system`. | ||
|
||
— Method **tsc:time_fn** | ||
|
||
Returns the function used to generate a time stamp for the configured | ||
time source, which returns the value of the TSC as a `uint64_t` | ||
object. | ||
|
||
— Method **tsc:stamp** | ||
|
||
Returns the current value of the TSC as a `uint64_t`. It is | ||
equivalent to the call `tsc:time_fn()()`. | ||
|
||
— Method **tsc:tps** | ||
|
||
Returns the number of clock ticks per second as a `uint64_t`. | ||
|
||
— Method **tsc:to_ns**, *ticks* | ||
|
||
Returns *ticks* converted from clock ticks to nanoseconds as a | ||
`uint64_t`. This method should be avoided in low-latency code paths | ||
due to conversions from/to Lua numbers. |
Oops, something went wrong.