Timers

Provides the PrecisionTimer class, the Timeout guard, the TimerPrecisions enumeration, and the timer benchmark.

class ataraxis_time.timers.PrecisionTimer(precision=TimerPrecisions.MICROSECOND)

Bases: object

Provides 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.

Instances are safe to share between threads. The timer reference point is updated with atomic operations, so concurrent lap() calls return adjacent intervals that together partition the elapsed time.

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).

_timer

The nanobind-generated C-extension timer class.

_laps

A list of recorded lap times.

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) – Determines whether the timer may use sleep instead of busy-wait to suspend execution. Sleep is only used when the timer precision is ‘ms’ or ‘s’.

  • 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, at the cost of tail latency. When another Python thread is CPU-bound, reacquiring the GIL can extend a delay by up to one thread-switch interval. Use block=True when latency determinism matters more than parallelism.

Return type:

None

property elapsed: int

Returns the time elapsed since class instantiation or the most recent reset() or lap() method call, whichever happened last.

format_elapsed(*, max_fields=2)

Returns the current elapsed time as a human-readable string.

Converts the elapsed time to nanoseconds internally, then breaks it into descending unit components (d, h, m, s, ms, us, ns). Auto-selects the largest non-zero unit as the starting point and includes up to max_fields unit segments.

Parameters:

max_fields (int, default: 2) – The maximum number of unit segments to include in the output. Defaults to 2, e.g. “2 h 30 m” instead of “2 h 30 m 15 s 200 ms”.

Return type:

str

Returns:

A human-readable string representation of the elapsed time, e.g. “2 h 30 m”, “1.5 ms”, “500 ns”.

Raises:

ValueError – If max_fields is less than 1.

lap()

Records the current elapsed time as a lap, resets the timer, and returns the lap duration.

Return type:

int

Returns:

The elapsed time captured as the lap duration, in the timer’s current precision units.

property laps: tuple[int, ...]

Returns all recorded lap times as a tuple.

poll(interval, *, allow_sleep=True, block=False)

Yields an iteration count after each delay cycle.

The caller should break out of the loop when done, because the generator never terminates on its own.

Parameters:
  • interval (int) – The delay interval in the timer’s current precision units.

  • allow_sleep (bool, default: True) – Determines whether the timer may use sleep instead of busy-wait to suspend execution. Sleep is only used when the timer precision is ‘ms’ or ‘s’.

  • block (bool, default: False) – Determines whether to hold or release the GIL during the delay.

Yields:

The iteration count (starting from 1) after each delay cycle.

property precision: str

Returns the units currently used by the instance as a string (‘ns’, ‘us’, ‘ms’, or ‘s’).

reset()

Resets the timer and clears all recorded laps.

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.timers.Timeout(duration, precision=TimerPrecisions.MICROSECOND)

Bases: object

Provides a timeout guard built on PrecisionTimer.

Parameters:
  • duration (int) – The timeout duration in the timer’s precision units. Must be greater than 0.

  • precision (str | TimerPrecisions, default: <TimerPrecisions.MICROSECOND: 'us'>) – The precision to use for timeout tracking. Use one of the supported values defined in the TimerPrecisions enumeration.

_timer

The internal PrecisionTimer instance.

_duration

The timeout duration.

Raises:

ValueError – If the duration is not greater than 0, or if the precision is not a valid option.

property elapsed: int

Returns elapsed time since creation or last reset.

property expired: bool

Returns True if the timeout duration has elapsed.

kick()

Resets the timeout timer (for activity-based timeouts).

Return type:

None

property remaining: int

Returns the remaining time before expiry (0 if expired).

reset(duration=None)

Resets the timeout, optionally with a new duration.

Parameters:

duration (int | None, default: None) – An optional new duration for the timeout. If None, the current duration is preserved. Must be greater than 0 if provided.

Raises:

ValueError – If the provided duration is not greater than 0.

Return type:

None

class ataraxis_time.timers.TimerPrecisions(*values)

Bases: StrEnum

Defines the timer precision modes supported by PrecisionTimer instances.

MICROSECOND = 'us'

Microsecond precision.

MILLISECOND = 'ms'

Millisecond precision.

NANOSECOND = 'ns'

Nanosecond precision.

SECOND = 's'

Second precision.

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.

Usage

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

Utilities

Provides general-purpose utility functions for working with date and time data.

class ataraxis_time.utilities.TimeUnits(*values)

Bases: StrEnum

Defines the time units supported by the time conversion functions of this module.

DAY = 'd'

Day time unit.

HOUR = 'h'

Hour time unit.

MICROSECOND = 'us'

Microsecond time unit.

MILLISECOND = 'ms'

Millisecond time unit.

MINUTE = 'm'

Minute time unit.

NANOSECOND = 'ns'

Nanosecond time unit.

SECOND = 's'

Second time unit.

class ataraxis_time.utilities.TimestampFormats(*values)

Bases: StrEnum

Defines the timestamp formats supported by the timestamp functions of this module.

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.

class ataraxis_time.utilities.TimestampPrecisions(*values)

Bases: StrEnum

Defines the precision levels supported by timestamp functions.

DAY = 'day'

Day precision (YYYY-MM-DD).

HOUR = 'hour'

Hour precision (YYYY-MM-DD-HH).

MICROSECOND = 'microsecond'

Microsecond precision (YYYY-MM-DD-HH-MM-SS-ssssss). This is the default and preserves full detail.

MINUTE = 'minute'

Minute precision (YYYY-MM-DD-HH-MM).

MONTH = 'month'

Month precision (YYYY-MM).

SECOND = 'second'

Second precision (YYYY-MM-DD-HH-MM-SS).

YEAR = 'year'

Year precision (YYYY).

ataraxis_time.utilities.convert_time(time, from_units, to_units, *, as_float=False)

Converts the input time value from the original units to the requested units.

Notes

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.

Raises:

ValueError – If ‘from_units’ or ‘to_units’ argument is not set to a valid time-option.

ataraxis_time.utilities.convert_timestamp(timestamp, time_separator='-', output_format=TimestampFormats.STRING, precision=TimestampPrecisions.MICROSECOND)

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. String inputs may have 1 to 7 delimited parts. Missing fields are filled with defaults (month=1, day=1, hour=0, minute=0, second=0, microsecond=0).

Parameters:
  • timestamp (str | int | NDArray[uint8]) – The timestamp value to convert.

  • time_separator (str, default: '-') – The separator to use for delimiting the date-and-time string. This must be a non-empty string for every output format, and it 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”.

  • precision (str | TimestampPrecisions, default: <TimestampPrecisions.MICROSECOND: 'microsecond'>) – The precision level for the output timestamp. Use one of the valid options defined in the TimestampPrecisions enumeration. For STRING format, this controls how many date/time components are included. For INTEGER and BYTES formats, sub-precision fields are zeroed out.

Return type:

str | int | NDArray[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 ‘time_separator’ argument is an empty string, if the ‘output_format’ argument is not set to a valid format option, or if the ‘precision’ argument is not a valid TimestampPrecisions value. This error is also raised when the ‘timestamp’ string carries the wrong number of delimited parts, non-numeric components, or a component outside its calendar range. It is also raised when the resolved timestamp falls outside the range this module represents.

ataraxis_time.utilities.from_timedelta(timedelta_value, to_units, *, as_float=False)

Converts a datetime.timedelta to a numeric value in the requested units.

Parameters:
  • timedelta_value (timedelta) – The timedelta to convert.

  • to_units (str | TimeUnits) – The units for the output value. Use one of the valid options defined in the TimeUnits enumeration.

  • as_float (bool, default: False) – Determines whether to return the result as a Python float (if True) or a NumPy 64-bit floating scalar (if False).

Return type:

float | float64

Returns:

The numeric value of the timedelta in the requested units.

Raises:

ValueError – If to_units is not a valid time unit.

ataraxis_time.utilities.get_timestamp(output_format=TimestampFormats.STRING, time_separator='-', precision=TimestampPrecisions.MICROSECOND)

Gets the current Coordinated Universal Time (UTC) timestamp (date and time) and returns it using the requested output format.

Notes

The byte-array 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”.

  • precision (str | TimestampPrecisions, default: <TimestampPrecisions.MICROSECOND: 'microsecond'>) – The precision level for the output timestamp. Use one of the valid options defined in the TimestampPrecisions enumeration. For STRING format, this controls how many date/time components are included. For INTEGER and BYTES formats, sub-precision fields are zeroed out.

Return type:

str | int | NDArray[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, if the ‘precision’ argument is not a valid TimestampPrecisions value, or if the ‘time_separator’ argument is an empty string.

ataraxis_time.utilities.interval_to_rate(interval, from_units=TimeUnits.MICROSECOND, *, as_float=False)

Converts a time interval to a frequency in Hz.

Parameters:
  • interval (float | integer[Any] | floating[Any]) – The time interval to convert. Must be a finite value greater than 0.

  • from_units (str | TimeUnits, default: <TimeUnits.MICROSECOND: 'us'>) – The time units of the input interval. Use one of the valid options defined in the TimeUnits enumeration.

  • as_float (bool, default: False) – Determines whether to return the result as a Python float (if True) or a NumPy 64-bit floating scalar (if False).

Return type:

float | float64

Returns:

The frequency in Hz corresponding to the given time interval.

Raises:

ValueError – If the interval is not a finite value greater than 0, if it underflows to zero seconds during conversion, or if from_units is not a valid time unit.

ataraxis_time.utilities.parse_timestamp(date_string, format_string, *, output_format=TimestampFormats.INTEGER, time_separator='-', precision=TimestampPrecisions.MICROSECOND)

Parses an arbitrary datetime string and returns it as a timestamp in the requested format.

Notes

The input datetime string is parsed using datetime.strptime() with the provided format string. The parsed datetime is assumed to be in UTC timezone. The output respects the same format and precision options as get_timestamp() and convert_timestamp().

Parameters:
  • date_string (str) – The datetime string to parse.

  • format_string (str) – The strptime-compatible format string used to parse the date_string.

  • output_format (str | TimestampFormats, default: <TimestampFormats.INTEGER: 'int'>) – 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”.

  • precision (str | TimestampPrecisions, default: <TimestampPrecisions.MICROSECOND: 'microsecond'>) – The precision level for the output timestamp. Use one of the valid options defined in the TimestampPrecisions enumeration.

Return type:

str | int | NDArray[uint8]

Returns:

The parsed timestamp in the requested output format.

Raises:
  • TypeError – If the ‘time_separator’ argument is not a string.

  • ValueError – If the date_string cannot be parsed with the provided format_string, if the output_format or the precision is invalid, or if the ‘time_separator’ argument is an empty string. This error is also raised when the resolved timestamp falls outside the range this module represents.

ataraxis_time.utilities.rate_to_interval(rate, to_units=TimeUnits.MICROSECOND, *, as_float=False)

Converts a frequency in Hz to a time interval in the requested units.

Parameters:
  • rate (float | integer[Any] | floating[Any]) – The frequency in Hz to convert. Must be a finite value greater than 0.

  • to_units (str | TimeUnits, default: <TimeUnits.MICROSECOND: 'us'>) – The time units for the output interval. Use one of the valid options defined in the TimeUnits enumeration.

  • as_float (bool, default: False) – Determines whether to return the result as a Python float (if True) or a NumPy 64-bit floating scalar (if False).

Return type:

float | float64

Returns:

The time interval corresponding to the given frequency, in the requested units.

Raises:

ValueError – If the rate is not a finite value greater than 0, or if to_units is not a valid time unit.

ataraxis_time.utilities.to_timedelta(time, from_units)

Converts a time value in the given units to a datetime.timedelta.

Parameters:
  • time (float | integer[Any] | floating[Any]) – The time value to convert.

  • from_units (str | TimeUnits) – The units of the input time value. Use one of the valid options defined in the TimeUnits enumeration.

Return type:

timedelta

Returns:

The given time value expressed as a duration.

Raises:

ValueError – If from_units is not a valid time unit.

C++ Timer Extension

Defines and implements the CPrecisionTimer class as a nanobind-bound C++ extension module.

Uses the monotonic system clock provided by the ‘chrono’ library, which allows the timer to resolve sub-microsecond intervals on sufficiently fast CPUs. This module works on Windows, macOS, and Linux.

Note

This module is bound to Python using nanobind and is designed to be further wrapped with a pure-Python PrecisionTimer class.

Functions

NB_MODULE(precision_timer_ext, m)

Binds the CPrecisionTimer class to the Python API via nanobind.

The module is available as ‘precision_timer_ext’ and must be bound to a Python package via CMake configuration.

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 class uses the ‘chrono’ library’s steady_clock, which advances monotonically and is therefore immune to the wall-clock adjustments that network time synchronization applies. However, all method calls have a certain overhead associated with them and the busier the OS is, the longer the overhead.

Note

Instances are safe to share between threads. The reference point and the precision are held in atomics, so a thread calling Delay() with the GIL released observes a consistent configuration while another thread reconfigures the instance.

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).

inline void Reset()

Resets the timer.

inline int64_t Elapsed() const

Returns the time elapsed since the last Reset() or Lap() method call, or since class instantiation.

Returns:

The elapsed time, using the current class precision units.

inline int64_t Lap()

Re-bases the timer and returns the time that elapsed since the previous reference point.

Installs the new reference point and reads the one it replaces in a single atomic exchange, so the returned interval begins exactly where the preceding interval ended. Consecutive calls therefore partition the elapsed time, and concurrent calls from separate threads each receive a distinct slice of it.

Returns:

The duration of the completed lap, 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).

Executes 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 may use sleep instead of busy-wait. Sleep is used only when the instance precision is ‘ms’ or ‘s’, regardless of the requested delay duration. It reduces CPU load at the expense of 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.

Throws:

std::overflow_error – If converting the duration to nanoseconds would exceed the range of a 64-bit signed integer at the instance’s current precision.

inline void SetPrecision(const std::string &precision)

Changes the timer precision used by the instance to the requested units.

Parameters:

precision – The new precision to use. Supported values are: ‘ns’ (nanoseconds), ‘us’ (microseconds), ‘ms’ (milliseconds), and ‘s’ (seconds).

Throws:

std::invalid_argument – If the precision does not exactly match one of the four supported options.

inline std::string get_precision() const

Returns the current precision of the timer (‘ns’, ‘us’, ‘ms’, or ‘s’).

inline std::string Repr() const

Returns a string representation of the CPrecisionTimer instance.

~CPrecisionTimer() = default

Destroys the CPrecisionTimer class.

Private Types

enum class kPrecisionUnits : uint8_t

Defines the timer precision options as a compact value, so that conversions dispatch on an integer rather than re-parsing a precision string on every call.

Values:

enumerator kNanosecond

The timer reports and accepts values in nanoseconds.

enumerator kMicrosecond

The timer reports and accepts values in microseconds.

enumerator kMillisecond

The timer reports and accepts values in milliseconds.

enumerator kSecond

The timer reports and accepts values in seconds.

Private Functions

inline int64_t ConvertToPrecision(const int64_t nanoseconds) const

Converts the input value from nanoseconds to the chosen precision units.

Parameters:

nanoseconds – The value in nanoseconds to be converted to the desired precision.

Returns:

The converted time-value, truncated to the whole number via integer division toward zero.

Private Members

std::atomic<steady_clock::time_point> _start_time

Stores the reference point that elapsed intervals are measured from.

std::atomic<kPrecisionUnits> _precision_units = kPrecisionUnits::kMicrosecond

Stores the resolved form of the selected precision option, used to select the conversion divisor.

std::atomic<int64_t> _precision_nanoseconds = kNanosecondsPerMicrosecond

Stores the number of nanoseconds in one unit of the selected precision, used as the conversion multiplier for delay durations.

Private Static Attributes

static int64_t kNanosecondsPerMicrosecond = 1'000

Stores the number of nanoseconds contained in a single microsecond.

static int64_t kNanosecondsPerMillisecond = 1'000'000

Stores the number of nanoseconds contained in a single millisecond.

static int64_t kNanosecondsPerSecond = 1'000'000'000

Stores the number of nanoseconds contained in a single second.

namespace literals

Provides the ability to work with Python literal string-options.

namespace chrono

Provides the binding for various clock-related operations.