ataraxis-time
A Python library that provides a sub-microsecond-precise thread-safe timer and helper methods to work with date and
time data.
Detailed Description
This library uses the 'chrono' C++ library to access the fastest available system clock and use it to provide interval
timing and delay functionality via a Python binding API. While the performance of the timer heavily depends on the
particular system configuration and utilization, most modern CPUs should be capable of sub-microsecond precision using
this timer. Due to using a C-extension to provide interval and delay timing functionality, the library is thread- and
process-safe and releases the GIL when using the appropriate delay command. Additionally, the library offers a set of
standalone helper functions that can be used to manipulate date and time data.
While the library was written to integrate with other Sun Lab projects, it can be used as a standalone library for
non-lab projects with no additional modification.
Features
- Supports Windows, Linux, and OSx.
- Sub-microsecond precision on modern CPUs (~ 3 GHz+) during delay and interval timing.
- Releases GIL during (non-blocking) delay timing even when using microsecond and nanosecond precision.
- Pure-python API.
- Fast C++ core with direct extension API access via nanobind.
- GPL 3 License.
Table of Contents
Dependencies
For users, all library dependencies are installed automatically for all supported installation methods
(see Installation section). For developers, see the Developers section for
information on installing additional development dependencies.
Installation
Source
Note. Building from source may require additional build components to be available to compile the C++ portion of the
library. It is highly recommended to install from PIP or CONDA instead.
- Download this repository to your local machine using your preferred method, such as git-cloning. Optionally, use one
of the stable releases that include precompiled binary wheels in addition to source code.
cd
to the root directory of the project using your command line interface of choice.- Run
python -m pip install .
to install the project. Alternatively, if using a distribution with precompiled
binaries, use python -m pip install WHEEL_PATH
, replacing 'WHEEL_PATH' with the path to the wheel file. - Optionally, run the timer benchmark using
benchmark-timer
command from your command line interface
(no need to use 'python' directive). You can use benchmark-timer --help
command to see the list of additional
configuration parameters that can be used to customize the benchmark behavior.
PIP
Use the following command to install the library using PIP: pip install ataraxis-time
Conda / Mamba
Note. Due to conda-forge contributing process being more nuanced than pip uploads, conda versions may lag behind
pip and source code distributions.
Use the following command to install the library using Conda or Mamba: conda install ataraxis-time
Usage
Precision Timer
The timer API is intentionally minimalistic to simplify class adoption and usage. It is heavily inspired by the
elapsedMillis library for
Teensy and Arduino microcontrollers.
All timer class functionality is realized through a fast c-extension wrapped into the PrecisionTimer class. Primarily,
the functionality comes through 3 class methods: reset(), elapsed (property) and delay():
Initialization and Configuration
The timer takes the 'precision' to use as the only initialization argument. All instances of the timer class are
thread- and process-safe and do not interfere with each other. The precision of the timer can be adjusted after
initialization if needed, which is more efficient than re-initializing the timer.
from ataraxis_time import PrecisionTimer
# Currently, the timer supports 4 'precisions: 'ns' (nanoseconds), 'us' (microseconds), 'ms' (milliseconds), and
# 's' seconds.'
timer = PrecisionTimer('us')
# However, the precision can be adjusted after initialization if needed:
timer.set_precision('ms') # Switches timer precision to milliseconds
Interval Timing
Interval timing functionality is realized through two methods: reset() and elapsed. This functionality of the class
is identical to using perf_counter_ns() from 'time' library. The main difference from the 'time' library is that the
class uses a slightly different interface (reset / elapsed) and automatically converts the output to the desired
precision.
from ataraxis_time import PrecisionTimer
import time as tm
timer = PrecisionTimer('us')
# Interval timing example
timer.reset() # Resets (re-bases) the timer
tm.sleep(1) # Simulates work (for 1 second)
print(f'Work time: {timer.elapsed} us') # This returns the 'work' duration using the precision units of the timer.
Delay
Delay timing functionality is the primary advantage of this class over the standard 'time' library. At the time of
writing, the 'time' library can provide nanosecond-precise delays via a 'busywait' perf_counter_ns() loop that does not
release the GIL. Alternatively, it can release the GIL via sleep() method, that is, however, only accurate up to
millisecond precision (up to 16ms on some Windows platforms). PrecisionTimer class can delay for time-periods up to
nanosecond precision (on some systems) while releasing or holding the GIL (depends on the method used):
from ataraxis_time import PrecisionTimer
import time as tm
timer = PrecisionTimer('us')
# GIL-releasing microsecond delays.
for i in range(10):
print(f'us delay iteration: {i}')
timer.delay_block(500) # Delays for 500 microseconds, does not release the GIL
# Non-GIL-releasing milliseconds delay (uses sleep for millisecond delays to optimize resource usage).
timer.set_precision('ms') # Switches timer precision to milliseconds
for i in range(10):
print(f'ms delay iteration: {i}')
timer.delay_noblock(500) # Delays for 500 milliseconds, releases the GIL
Date & Time Helper Functions
These are minor helper methods that are not directly part of the timer class showcased above. Since these methods are
not intended for realtime applications, they are implemented using pure python (slow), rather than fast
c-extension method.
Convert Time
This helper method performs time-conversions, rounding to 3 Significant Figures for the chosen precision, and works
with time-scales from nanoseconds to days.
from ataraxis_time.time_helpers import convert_time
# The method can convert single inputs...
initial_time = 12
time_in_seconds = convert_time(time=initial_time, from_units='d', to_units='s') # Returns 1036800.0
# And Python iterables and numpy arrays
initial_time = np.array([12, 12, 12])
# Returns a numpy aray with all values set to 1036800.0 (uses float_64 format)
time_in_seconds = convert_time(time=initial_time, from_units='d', to_units='s')
Get Timestamp
This method is used to get timestamps accurate up to seconds. This is typically helpful when time-stamping file
names and slow events.
from ataraxis_time.time_helpers import get_timestamp
# Obtains the current date and time and uses it to generate a timestamp that can be used in file-names (for example).
dt = get_timestamp(time_separator='-') # Returns 2024-06-18-00-06-25 (yyyy-mm-dd-hh-mm-ss)
API Documentation
See the API documentation for the
detailed description of the methods and classes exposed by components of this library.
The documentation also covers the C++ source code and benchmark-timer command line interface command.
Developers
This section provides installation, dependency, and build-system instructions for the developers that want to
modify the source code of this library. Additionally, it contains instructions for recreating the conda environments
that were used during development from the included .yml files.
Installing the library
- Download this repository to your local machine using your preferred method, such as git-cloning.
cd
to the root directory of the project using your command line interface of choice.- Install development dependencies. You have multiple options of satisfying this requirement:
- Preferred Method: Use conda or pip to install
tox or use an environment that has it installed and
call
tox -e import
to automatically import the os-specific development environment included with the
source code in your local conda distribution. Alternatively, you can use tox -e create
to create the
environment from scratch and automatically install the necessary dependencies using pyproject.toml file. See
environments section for other environment installation methods. - Run
python -m pip install .'[dev]'
command to install development dependencies and the library. For some
systems, you may need to use a slightly modified version of this command: python -m pip install .[dev]
. - As long as you have an environment with
tox installed
and do not intend to run any code outside the predefined project automation pipelines,
tox will automatically install all required dependencies for each task.
Note: When using tox automation, having a local version of the library may interfere with tox tasks that attempt
to build the library using an isolated environment. While the problem is rare, our 'tox' pipelines automatically
install and uninstall the project from its' conda environment. This relies on a static tox configuration and will only
target the project-specific environment, so it is advised to always tox -e import
or tox -e create
the
project environment using 'tox' before running other tox commands.
Additional Dependencies
In addition to installing the required python packages, separately install the following dependencies:
- Doxygen, if you want to generate C++ code documentation.
- An appropriate build tool or Docker, if you intend to build binary wheels via
cibuildwheel (See the link for information on which dependencies to
install).
- Python distributions, one for each version that you intend to support.
Currently, this library supports 3.10, 3.11, and 3.12. The easiest way to get tox to work as intended is to have
separate python distributions, but using pyenv is a good alternative too.
This is needed for the 'test' task to work as intended.
Development Automation
This project comes with a fully configured set of automation pipelines implemented using
tox. Check tox.ini file for details about
available pipelines and their implementation. Alternatively, call tox list
from the root directory of the project
to see the list of available tasks.
Note! All commits to this project have to successfully complete the tox
task before being pushed to GitHub.
To minimize the runtime task for this task, use tox --parallel
.
For more information, you can also see the 'Usage' section of the
ataraxis-automation project documentation.
Environments
All environments used during development are exported as .yml files and as spec.txt files to the envs folder.
The environment snapshots were taken on each of the three explicitly supported OS families: Windows 11, OSx (M1) 14.5
and Linux Ubuntu 22.04 LTS.
Note! Since the OSx environment was built against an M1 (Apple Silicon) platform and may not work on Intel-based
Apple devices.
To install the development environment for your OS:
- Download this repository to your local machine using your preferred method, such as git-cloning.
cd
into the envs folder.- Use one of the installation methods below:
- Preferred Method: Install tox or use another
environment with already installed tox and call
tox -e import
. - Alternative Method: Run
conda env create -f ENVNAME.yml
or mamba env create -f ENVNAME.yml
.
Replace 'ENVNAME.yml' with the name of the environment you want to install (axt_dev_osx for OSx, axt_dev_win
for Windows, and axt_dev_lin for Linux).
Hint: while only the platforms mentioned above were explicitly evaluated, this project is likely to work on any
common OS, but may require additional configurations steps.
Since the release of ataraxis-automation version 2.0.0 you can
also create the development environment from scratch via pyproject.toml dependencies. To do this, use
tox -e create
from project root directory.
Automation Troubleshooting
Many packages used in 'tox' automation pipelines (uv, mypy, ruff) and 'tox' itself are prone to various failures. In
most cases, this is related to their caching behavior. Despite a considerable effort to disable caching behavior known
to be problematic, in some cases it cannot or should not be eliminated. If you run into an unintelligible error with
any of the automation components, deleting the corresponding .cache (.tox, .ruff_cache, .mypy_cache, etc.) manually
or via a cli command is very likely to fix the issue.
Versioning
We use semantic versioning for this project. For the versions available, see the
tags on this repository.
Authors
License
This project is licensed under the GPL3 License: see the LICENSE file for details.
Acknowledgments
- All Sun Lab members for providing the inspiration and comments during the
development of this library.
- My NBB Cohort for answering 'random questions' pertaining to the desired library
functionality.
- click project for providing the low-level command-line-interface functionality
for this project.
- tqdm project for providing an easy-to-use progress bar functionality used in our
benchmark script.
- numpy project for providing low-level functionality for our benchmark script.
- elapsedMillis project for providing the
inspiration for the API and the functionality of the timer class.
- nanobind project for providing a fast and convenient way of binding c++ code to
python projects.
- The creators of all other projects used in our development automation pipelines see pyproject.toml.