Precision Timer
This module provides the PrecisionTimer class that provides a high-level API for accessing the bound C++ timer’s functionality.
- class ataraxis_time.precision_timer.timer_class.PrecisionTimer(precision=TimerPrecisions.MICROSECOND)
Bases:
objectProvides high-precision interval-timing and delay functionality.
This class functions as an interface wrapper for a C++ class that uses the ‘chrono’ library to interface with the highest available precision clock of the host system.
Notes
The precision of all class methods depends on the precision and frequency of the system’s CPU. It is highly advised to benchmark the class before deploying it in time-critical projects to characterize the overhead associated with using different timer methods.
- _timer
Stores the nanobind-generated C-extension timer class.
- Parameters:
precision (
str|TimerPrecisions, default:<TimerPrecisions.MICROSECOND: 'us'>) – The desired precision of the timer. Use one of the supported values defined in the TimerPrecisions enumeration. Currently, accepted precision values are ‘ns’ (nanoseconds), ‘us’ (microseconds), ‘ms’ (milliseconds), and ‘s’ (seconds).- Raises:
ValueError – If the input precision is not one of the accepted options.
- delay(delay, *, allow_sleep=False, block=False)
Delays program execution for the requested period of time.
- Parameters:
delay (
int) – The integer period of time to wait for. The method assumes the delay is given in the same precision units as used by the instance.allow_sleep (
bool, default:False) – A boolean flag that allows releasing the CPU while suspending execution for durations above 1 millisecond.block (
bool, default:False) – Determines whether to hold (if True) or release (if False) the Global Interpreter Lock (GIL) during the delay. Releasing the GIL allows other Python threads to run in parallel with the delay.
- Return type:
None
- property elapsed: int
Returns the time elapsed since class instantiation or the last reset() method call, whichever happened last.
- property precision: str
Returns the units currently used by the instance as a string (‘ns’, ‘us’, ‘ms’, or ‘s’).
- reset()
Resets the timer.
- Return type:
None
- set_precision(precision)
Changes the precision used by the timer to the input option.
- Parameters:
precision (
str|TimerPrecisions) – The desired precision of the timer. Use one of the supported values defined in the TimerPrecisions enumeration. Currently, accepted precision values are ‘ns’ (nanoseconds), ‘us’ (microseconds), ‘ms’ (milliseconds), and ‘s’ (seconds).- Raises:
ValueError – If the input precision is not one of the accepted options.
- Return type:
None
- class ataraxis_time.precision_timer.timer_class.TimerPrecisions(*values)
Bases:
StrEnumStores the timer precision modes supported by the PrecisionTimer instances.
Use this enumeration when initializing or reconfiguring the precision used by a PrecisionTimer instance.
- MICROSECOND = 'us'
- MILLISECOND = 'ms'
- NANOSECOND = 'ns'
- SECOND = 's'
Timer Benchmark
axt-benchmark
Benchmarks the PrecisionTimer class performance on the local host-system. Use this command to determine the actual precision of the timer for the local Operating System and CPU clock combination.
axt-benchmark [OPTIONS]
Options
- -ic, --interval-cycles <interval_cycles>
The number of times to repeat the interval benchmark for each of the tested timer precisions.
- Default:
60
- -id, --interval-delay <interval_delay>
The interval duration, in seconds, to use during the interval benchmark for each of the tested timer precisions.
- Default:
1
- -dc, --delay-cycles <delay_cycles>
The number of times to repeat the delay benchmark for each of the tested timer precisions. Expects a space-separated sequence in the order of: ns, us, ms, s.
- Default:
1000, 1000, 1000, 60
- -dd, --delay-durations <delay_durations>
The delay duration, in precision-units, to use during the delay benchmark for the tested precisions. Expects a space-separated sequence in the order of: ns, us, ms, s.
- Default:
500, 5, 2, 1
Helper Functions
This package provides general-purpose helper functions used to work with date and time data.
- class ataraxis_time.time_helpers.TimeUnits(*values)
Bases:
StrEnumStores the time units supported by the convert_time() function.
Use this enumeration to specify the source and destination time units when calling the conversion function.
- DAY = 'd'
- HOUR = 'h'
- MICROSECOND = 'us'
- MILLISECOND = 'ms'
- MINUTE = 'm'
- NANOSECOND = 'ns'
- SECOND = 's'
- class ataraxis_time.time_helpers.TimestampFormats(*values)
Bases:
StrEnumStores the timestamp formats supported by the get_timestamp() and the convert_timestamp() functions.
Use this enumeration when generating or converting timestamps.
- BYTES = 'byte'
A NumPy byte array. The timestamp is stored as a 64-bit unsigned integer serialized using little endian scheme that represents the number of microseconds elapsed since the UTC epoch onset.
- INTEGER = 'int'
The integer that represents the number of microseconds elapsed since the UTC epoch onset.
- STRING = 'str'
A delimited date-and-time string. The timestamp is stored in the ‘YYYY-MM-DD-HH-MM-SS-ssssss’ format and uses UTC timezone.
- ataraxis_time.time_helpers.convert_time(time, from_units, to_units, *, as_float=False)
Converts the input time value from the original units to the requested units.
Notes
By default, this function returns the converted time value as a NumPy 64-bit floating scalar.
The conversion uses 3 decimal places rounding.
- Parameters:
time (
float|integer[Any] |floating[Any]) – The time-value to convert.from_units (
str|TimeUnits) – The units used by the input data. Use one of the valid options defined in the TimeUnits enumeration: ‘ns’ (nanoseconds), ‘us’ (microseconds), ‘ms’ (milliseconds), ‘s’ (seconds), ‘m’ (minutes), ‘h’ (hours), ‘d’ (days).to_units (
str|TimeUnits) – The units to convert the input data to. Uses the same options as the ‘from_units’ argument.as_float (
bool, default:False) – Determines whether to return the converted time as a Python floating object (if True) or a NumPy 64-bit floating scalar type (if False).
- Return type:
float|float64- Returns:
The converted time-value given in the requested time-units. Depending on the ‘as_float’ argument, the value is returned either as a Python float or a NumPy 64-bit floating scalar.
- Raises:
ValueError – If ‘from_units’ or ‘to_units’ argument is not set to a valid time-option.
- ataraxis_time.time_helpers.convert_timestamp(timestamp, time_separator='-', output_format=TimestampFormats.STRING)
Converts a timestamp generated by the get_timestamp() function into a different format.
Notes
This method is primarily designed to decode byte-serialized timestamps produced by the get_timestamp() function into other formats.
- Parameters:
timestamp (
str|int|ndarray[tuple[Any,...],dtype[uint8]]) – The timestamp value to convert.time_separator (
str, default:'-') – The separator to use for delimiting the date-and-time string. This is used when parsing input date-and-time strings and when converting timestamps to the string format.output_format (
str|TimestampFormats, default:<TimestampFormats.STRING: 'str'>) – The format in which to return the timestamp. Use one of the valid options defined in the TimestampFormats enumeration: “str”, “byte”, or “int”.
- Return type:
str|int|ndarray[tuple[Any,...],dtype[uint8]]- Returns:
The timestamp converted to the requested format.
- Raises:
TypeError – If the ‘time_separator’ argument is not a string, or if the ‘timestamp’ argument is not of a valid type.
ValueError – If the ‘output_format’ argument is not set to a valid format option.
- ataraxis_time.time_helpers.get_timestamp(output_format=TimestampFormats.STRING, time_separator='-')
Gets the current Coordinated Universal Time (UTC) timestamp (date and time) and returns it using the requested output format.
Notes
The bytes’-converted timestamp is encoded using the little-endian format.
- Parameters:
output_format (
str|TimestampFormats, default:<TimestampFormats.STRING: 'str'>) – The format in which to return the timestamp. Use one of the valid options defined in the TimestampFormats enumeration: “str”, “byte”, or “int”.time_separator (
str, default:'-') – The separator to use for delimiting the date-and-time string. This is only used if the ‘output_format’ argument is set to “str”.
- Return type:
str|int|ndarray[tuple[Any,...],dtype[uint8]]- Returns:
The current UTC timestamp converted to the requested output format.
- Raises:
TypeError – If the ‘time_separator’ argument is not a string.
ValueError – If the ‘output_format’ argument is not set to a valid format option.
C++ Timer Extension
The C++ extension module that defines and implements the CPrecisionTimer class.
Description:
This module instantiates the CPrecisionTimer class using the fastest system clock available through the ‘chrono’ library, which allows the timer to resolve sub-microsecond timer-intervals on sufficiently fast CPUs. This module works on Windows, macOS, and Linux.
Dependencies:
nanobind/nanobind.h: For nanobind-based binding to Python.
nanobind/stl/string.h: To enable working with python string arguments.
chrono: To work with system-exposed time sources.
thread: To control GIL-locking behavior of noblock methods.
Note
This module is bound to python using (nanobind) project and is designed to be further wrapped with a pure-python PrecisionTimer wrapper instantiated by the init.py of the python module. The binding code is stored in the same file as source code (at the end of this file).
Functions
-
NB_MODULE(precision_timer_ext, m)
The nanobind module that binds (exposes) the CPrecisionTimer class to the Python API.
This nanobind module wraps the CPrecisionTimer class and exposes it to Python via its API.
Note
The module is available as ‘precision_timer_ext’ and has to be properly bound to a python package via CMake configuration. Each method exposed to Python API below uses the names given as the first argument to each ‘def’ method.
-
class CPrecisionTimer
Provides methods for sub-microsecond-precise interval timing and blocking / non-blocking code execution delays.
Note
The performance of this class scales with the OS and the state of the host system. The ‘chrono’ library provides a high_resolution_clock, which is automatically set to the highest resolution clock of the host OS. However, all method calls have a certain overhead associated with them and the busier the OS is, the longer the overhead.
Public Functions
-
inline explicit CPrecisionTimer(const std::string &precision = "us")
Instantiates the CPrecisionTimer class using the requested precision.
- Parameters:
precision – The precision of the timer. This controls the units used by the timer for all inputs and outputs. Supported values are: ‘ns’ (nanoseconds), ‘us’ (microseconds), ‘ms’ (milliseconds), and ‘s’ (seconds).
-
~CPrecisionTimer() = default
Destroys the CPrecisionTimer class.
-
inline void Reset()
Resets the timer.
-
inline int64_t Elapsed() const
Returns the time elapsed since the last reset() method call or class instantiation.
- Returns:
int64_t The elapsed time, using the current class precision units.
-
inline void Delay(const int64_t duration, const bool allow_sleep = false, const bool block = false) const
Blocks (delays) in-place for the specified number of time-units (depends on used precision).
This method is used to execute arbitrary delays while maintaining or releasing the Global Interpreter Lock (GIL). By default, this method uses a busy-wait approach where the thread constantly checks elapsed time until the exit condition is encountered and releases the GIL while waiting.
Warning
If sleeping is allowed, there is an overhead of up to 1 millisecond due to scheduling on the Windows OS.
- Parameters:
duration – The time to block for. Uses the same units as the instance’s precision parameter.
allow_sleep – Determines whether the method should use sleep for delay durations above 1 millisecond. Sleep may be beneficial in some cases as it reduces the CPU load at the expense of an additional overhead compared to the default busy-wait delay approach.
block – Determines whether the method should release the GIL, allowing concurrent execution of other Python threads. If false, the method releases the GIL. If true, the method maintains the GIL, preventing other Python threads from running.
-
inline void SetPrecision(const std::string &precision)
Changes the timer precision used by the instance to the requested units.
This method can be used to dynamically change the precision used by a class instance during runtime.
- Parameters:
precision – The new precision to use. Supported values are: ‘ns’ (nanoseconds), ‘us’ (microseconds), ms’ (milliseconds), and ‘s’ (seconds).’
-
inline std::string GetPrecision() const
Returns the current precision (time-units) of the timer.
- Returns:
std::string The current precision of the timer (‘ns’, ‘us’, ‘ms’, or ‘s’).
Private Functions
-
inline int64_t ConvertToPrecision(const int64_t nanoseconds) const
Converts the input value from nanoseconds to the chosen precision units.
This method is currently used by the Elapsed() method to convert elapsed time from nanoseconds (used by the class) to the desired precision (requested by the user). However, it is a general converter that may be used by other methods in the future.
- Parameters:
nanoseconds – The value in nanoseconds to be converted to the desired precision.
- Returns:
int64_t The converted time-value, rounded to the whole number.
Private Members
-
high_resolution_clock::time_point _start_time
Stores the reference value used to calculate elapsed time.
-
std::string _precision
Stores the string-option that describes the units used for inputs and outputs.
-
nanoseconds _precision_duration = microseconds(1)
Stores the conversion factor that is assigned based on the chosen _precision option. It is used to convert the input duration values (for delay methods) to nanoseconds and the output duration values from nanoseconds to the chosen precision units.
-
inline explicit CPrecisionTimer(const std::string &precision = "us")
-
namespace literals
Provides the ability to work with Python literal string-options.
-
namespace chrono
Provides the binding for various clock-related operations.