Shrapnel needs to keep track of time to manage scheduling of sleeps and timeouts. Because Shrapnel is intended to support thousands of coroutines, and each coroutine may be making many timeout calls per second, Shrapnel needs to use a timing facility that is relatively high performance. It also needs one that is monotonic, so it does not need to deal with system clock changes.
The clocks subpackage is intended to provide a variety of different time facilities. Currently it only supports using the x86 TSC timer. This is a timer built in to the CPU, and thus is very fast.
Support for TSC time is implemented in the coro.clocks.tsc_time module.
TSC time library.
This module implements a “Time” object that is based on the TSC value of the x86 processor. This is a monotonically increasing value that is somewhat dependable (whereas system time may change). It is also very high resolution and very efficient to retrieve.
This library is designed to be as high-performance as possible. If you use it correctly, you can ensure your code maintains that high level of performance.
There are 4 objects in this module all deriving from the base Time object. They are:
Each of these objects are relatively the same. The main difference is when adding or comparing the objects, they behave as their type describes.
The base Time object defines methods for converting the TSC value to another type. They are as_posix_sec, as_posix_usec, and as_posix_fsec.
There are a three classes of functions in the module for creating these objects. The now_* functions compute the current time. The mktime_* functions convert a Python “time tuple” to a Time object. The *_from_* functions take a raw value (TSC, Posix, etc.) and create a Time object.
The module also provides methods for converting raw values from one type to another. These are the *_to_* functions, and generally should not be needed if your code uses Time objects throughout.
When the module is first imported, it captures the current wall-clock time and TSC value. This information is used for doing conversions between TSC and Posix time. It is important to call the update_time_relation function whenever the wall-clock is changed. Also, it is a good idea to call it periodically to retain accuracy. This is necessary because the library uses the ticks_per_sec value for conversions. This value is obtained from the machdep.tsc_freq sysctl, and may be slightly off (my system is about 0.002% off which is about 10 minutes per year). Long-term computations based on the ticks_per_sec value should not be trusted to be very accurate.
The conversion functions use roughly imprecise, but faster integer arithmetic. The reason it is inaccurate is because it uses the ticks_per_usec value which is less accurate than ticks_per_sec. To compute the inaccuracy, you can use the formula:
(1000000 / ticks_per_sec)
This is a rough estimate. On my 3.5 GHz system, this is about 0.027% or about 2.3 hours per year. Slower systems have less accuracy (about 0.09% for a 1 GHz machine or about 8 hours per year).
To be more accurate, we would either need to use numbers larger than 64 bits (bignums, Python Longs, 80-bit C doubles, etc.), but it would slow the conversions down a little (for C doubles it was about 30% slower on my system).
TSC values that are significantly far from the current time should not be trusted to be very accurate.
The C functions in this module are available for direct access from other C extension modules. For C modules, simply include “tsc_time.h” and call the initialization function once. For Pyrex modules, include “tsc_time_include.pyx”. See the respective files for more detail.
TSC values may be negative (to indicate a time before the computer booted). In general, this library uses signed data types to avoid signed/unsigned multiplication/division. A particular exception is the ticks_per_sec value because it is currently defined as a 32-bit number, and we need to support machines with CPU’s faster than 2GHz.
On most POSIX systems, time_t is a signed 32-bit integer (on some it is a signed 64-bit integer, whose negative value extends past the beginning of the universe). In theory, a signed 32-bit value can handle negative values from 1901 to 1970 and 1970 to 2038 positive. Some foolish systems have attempted to define time_t as an unsigned value to extend the overflow point to 2106, but this is rare.
The rate of the TSC value may change on systems with thermal and power throttling. (though rumor has it some processors adjust the TSC rate when auto-throttling to ensure it runs at a constant speed). This invalidates assumptions made in this library, so do not use those features.
On SMP kernels, FreeBSD will synchronize the TSC value on all CPU’s at boot time, and the assumption is made that they will remain roughly in sync. Rumor has it that some motherboards will attempt to keep the TSC value in sync on all processors over time. AMD CPU’s are rumored to be especially vulnerable to this.
This is detailed low-level information about the rdtsc instruction that is used to obtain the TSC value.
rdtsc - ReaD TimeStamp Counter
The cycle counter in the Pentium series of processors is incremented once for every clock cycle. It starts out as 0 on system boot. It is a 64-bit number, and thus will wrap over in 292 years on a 2 gigahertz processor. It should keep counting unless the system goes into deep sleep mode.
FYI, the control registers on the Pentium can be configured to restrict RDTSC to privileged level 0.
The RDTSC instruction is generally not synchronized. Thus, with out of order execution, it is possible for it to run ahead of other instructions that came before it. This is mainly only important if you are trying to do exact profiling of instruction cycles.
Most x86 systems have other hardware timers. They all have different frequencies, accuracies, performance characteristics, etc. The following is a list of alternate counters that we may want to investigate:
Some interesting papers:
Variables: |
|
---|
Bases: coro.clocks.tsc_time.Time
Time in POSIX seconds.
Convert a raw POSIX floating-point seconds value to a Posix object.
Convert a raw POSIX seconds value to a Posix object.
Convert a raw POSIX microseconds value to a Posix object.
Convert a raw TSC value to a Posix object.
Bases: coro.clocks.tsc_time.Time
Time in TSC ticks.
Convert a raw POSIX floating-point seconds value to a TSC object.
Convert a raw POSIX seconds value to a TSC object.
Convert a raw POSIX microseconds value to a TSC object.
Convert a raw TSC value to a TSC object.
Bases: object
Base time object.
Time object support the following operations:
Comparison. Comparison is done using the native time object type. For example, “Posix” objects compare time in POSIX seconds. Thus, if comparing two Posix objects that have slightly different TSC values, but due to the loss of precision have the same POSIX value, they will compare as equal.
Comparison between different types is OK (Posix compared to TSC). You can also compare a value with Python numeric literals (int, long, float, etc.).
Hashing. Hashing is based on the object type.
Addition and subtraction. This only works between two types of the exact same type (Posix and Posix for example), or with a Python numeric literal (int, long, float, etc.).
int/long/float. Calling the int, long, or float functions on the object will return an int, long, or float value of the object’s type.
Ivariables : |
|
---|
Return the time as POSIX seconds (a floating-point number).
Return : | Returns a float as POSIX seconds. |
---|
Return the time as POSIX seconds (an integer).
Return : | Returns an integer as POSIX seconds. |
---|
Return the time as POSIX microseconds (a long).
Return : | Returns a long as POSIX microseconds. |
---|
Return the time as a string.
This returns the time as a classic C-style 24-character time string in the local timezone in the format ‘Sun Jun 20 23:21:05 1993’. This does not include a trailing newline like C does.
Return : | Returns a string of the local time. |
---|
Return a Python time-tuple in UTC.
Return : | Returns a time.struct_time time-tuple in UTC. |
---|
Return a Python time-tuple in the local timezone.
Return : | Returns a time.struct_time time-tuple in the local timezone. |
---|
Convert time to a string in the local timezone.
Parameters : |
|
---|---|
Return : | Returns a string in the local timezone. |
Convert time to a string in UTC.
Parameters : |
|
---|---|
Return : | Returns a string in UTC. |
Bases: coro.clocks.tsc_time.Time
Time in POSIX seconds as a floating-point number.
Convert a raw POSIX floating-point seconds value to an fPosix object.
Convert a raw POSIX seconds value to an fPosix object.
Convert a raw POSIX microseconds value to an fPosix object.
Convert a raw TSC value to an fPosix object.
Convert POSIX seconds (a floating-point number) to ticks.
Parameters : |
|
---|---|
Return : | Returns the time in TSC ticks. |
Get the current time from the kernel in microseconds.
Avoid using this unless absolutely necessary due to performance reasons.
Return : | Returns the current time in microseconds. |
---|
Convert a Python time-tuple to an fPosix object.
Convert a Python time-tuple to a Posix object.
Convert a Python time-tuple to a uPosix object.
Convert a Python time-tuple to a TSC object.
Return an fPosix object of the current time.
Return a Posix object of the current time.
Return a uPosix object of the current time.
Get the current time as raw POSIX floating-point seconds.
Get the current time as raw POSIX seconds.
Get the current time as raw POSIX microseconds.
Get the current time as raw ticks.
Return a TSC object of the current time.
Return the current TSC value.
Convert POSIX seconds to ticks.
Parameters : |
|
---|---|
Return : | Returns the time in TSC ticks. |
Emulate setting the system time to the given timestamp.
This alters the library to behave as-if the current time is the given time. Note that this is different than changing the clock on the system. Changing the clock on the system does not affect TSC values, but this function does affect TSC values to behave as-if time has elapsed in the real world.
Parameters : |
|
---|
Emulate changing the system time by the given number of seconds.
See set_time for more detail.
Parameters : |
|
---|
Convert ticks to POSIX seconds (a floating-point number).
Parameters : |
|
---|---|
Return : | Returns the time in POSIX microseconds. |
Convert ticks to POSIX seconds (an integer).
Parameters : |
|
---|---|
Return : | Returns the time in POSIX microseconds. |
Convert ticks to POSIX microseconds.
Parameters : |
|
---|---|
Return : | Returns the time in POSIX microseconds. |
Convert ticks to POSIX microseconds.
This is “safe” in that if the value is zero, then it returns zero.
Parameters : |
|
---|---|
Return : | Returns the time in POSIX microseconds. |
Bases: coro.clocks.tsc_time.Time
Time in POSIX microseconds.
Convert a raw POSIX floating-point seconds value to a uPosix object.
Convert a raw POSIX seconds value to a uPosix object.
Convert a raw POSIX microseconds value to a uPosix object.
Convert a raw TSC value to a uPosix object.
Update the relative time stamps.
You should call this whenever you think the clock has been changed. It should also be called periodically due to inaccuracies in the ticks_per_sec value.
Convert POSIX microseconds to ticks.
Parameters : |
|
---|---|
Return : | Returns the time in TSC ticks. |
Convert POSIX microseconds to ticks.
This is “safe” in that if the value is zero, then it returns zero.
Parameters : |
|
---|---|
Return : | Returns the time in TSC ticks. |