PEP 418: merge time.try_monotonic() and time.hires() into time.hires()

Cleanup the PEP.
This commit is contained in:
Victor Stinner 2012-03-28 02:18:11 +02:00
parent 280378cb3a
commit 02b9a5bcb3
1 changed files with 80 additions and 137 deletions

View File

@ -13,8 +13,7 @@ Python-Version: 3.3
Abstract Abstract
======== ========
Add time.monotonic(), time.hires() and time.try_monotonic() functions to Python Add time.monotonic() and time.hires() functions to Python 3.3.
3.3.
Rationale Rationale
@ -24,33 +23,9 @@ Use cases:
* Display the current time to a human (e.g. display a calendar or draw a wall * Display the current time to a human (e.g. display a calendar or draw a wall
clock): use system clock. time.time() or datetime.datetime.now() clock): use system clock. time.time() or datetime.datetime.now()
* Implement a timeout: use monotonic clock, or fallback to the clock with * Benchmark, profiling, timeout: time.hires()
the highest resolution. time.try_monotonic()
* Benchmark and profiling: time.hires(), with a manual fallback to time.time()
* Event scheduler: time.monotonic() * Event scheduler: time.monotonic()
time.try_monotonic() tries to use a monotonic clock, but it falls back to a
non-monotonic clock if no monotonic clock is available or if reading a
monotonic clock failed.
The pybench benchmark tool supports the following clocks: time.clock(),
time.time() and systimes.processtime(). By default, it uses time.clock() on
Windows, and time.time() otherwise. It is possible to choose the clock using
the --timer command line option.
Which clock?
* System time: pthread_mutex_timedlock(), pthread_cond_wait() (CLOCK_REALTIME)
* Monotonic: clock_nanosleep() (CLOCK_MONOTONIC)
Timeouts:
* threading.Lock.wait()
* select.select()
* time.sleep()
* subprocess.Popen.communicate()
* etc.
Functions Functions
========= =========
@ -60,7 +35,7 @@ time.time()
The system time is the "wall clock". It can be set manually by the system The system time is the "wall clock". It can be set manually by the system
administrator or automatically by a NTP daemon. It can jump backward and administrator or automatically by a NTP daemon. It can jump backward and
forward, and is not monotonic. forward. It is not monotonic.
It is available on all platforms and cannot fail. It is available on all platforms and cannot fail.
@ -96,12 +71,12 @@ Pseudo-code [#pseudo]_: ::
time.monotonic() time.monotonic()
---------------- ----------------
Monotonic clock advancing at a monotonic rate relative to real time. It Clock advancing at a monotonic rate relative to real time. It cannot go
cannot go backward. Its rate may be adjusted by NTP. The reference point of the backward. Its rate may be adjusted by NTP. The reference point of the returned
returned value is undefined so only the difference of consecutive calls is value is undefined so only the difference of consecutive calls is valid.
valid.
It is not available on all platforms and may raise an OSError.
It is not avaialble on all platforms and raise an OSError on error.
The monotonic clock may stop while the system is suspended. The monotonic clock may stop while the system is suspended.
Pseudo-code [#pseudo]_: :: Pseudo-code [#pseudo]_: ::
@ -157,53 +132,21 @@ Pseudo-code [#pseudo]_: ::
time.hires() time.hires()
------------ ------------
High-resolution clock. It has an unspecified starting point and may be High-resolution clock: use a monotonic clock if available, or fallback to the
adjusted. The clock starting point changes when the system is resumed after system time.
being suspended.
It is available on all platforms and cannot fail.
Pseudo-code [#pseudo]_: :: Pseudo-code [#pseudo]_: ::
if os.name == 'nt':
def hires(): def hires():
return _time.QueryPerformanceCounter() if hires.use_monotonic:
elif hasattr(time, "monotonic"):
hires = time.monotonic
elif hasattr(time, "clock_gettime") and hasattr(time, "CLOCK_REALTIME"):
def hires():
return time.clock_gettime(time.CLOCK_REALTIME)
It is not available on all platforms and may raise an OSError.
time.try_monotonic()
--------------------
This clock advances at a steady rate relative to real time. It may be adjusted.
The reference point of the returned value is undefined so only the difference
of consecutive calls is valid.
Pseudo-code::
def try_monotonic():
if try_monotonic.use_monotonic:
try: try:
return time.monotonic() return time.monotonic()
except (AttributeError, OSError): except OSError:
try_monotonic.use_monotonic = False hires.use_monotonic = False
if try_monotonic.use_hires:
try:
return time.hires()
except (AttributeError, OSError):
try_monotonic.use_hires = False
return time.time() return time.time()
try_monotonic.use_monotonic = True hires.use_monotonic = hasattr(time, 'monotonic')
try_monotonic.use_hires = True
If available, a monotonic clock is used. The function falls back to another
clock if the monotonic clock failed or is not available.
This function cannot fail.
Clocks Clocks
@ -212,54 +155,75 @@ Clocks
Monotonic Monotonic
--------- ---------
* mach_absolute_time(): Mac OS X provides a monotonic clock: mach_absolute_time
mach_absolute_time(). mach_timebase_info() provides a fraction to convert it ^^^^^^^^^^^^^^^^^^
to a number of nanoseconds. According to the documentation,
mach_timebase_info() is always equals to one and does never fail, even if
the function may fail according to its prototype.
* clock_gettime(CLOCK_MONOTONIC): Clock that cannot be set and represents
monotonic time since some unspecified starting point.
* clock_gettime(CLOCK_MONOTONIC_RAW), since Linux 2.6.28; Linux-specific:
Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based time
that is not subject to NTP adjustments.
* Windows: GetTickCount(), GetTickCount64().
CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW clocks cannot be set. Mac OS X provides a monotonic clock: mach_absolute_time(). mach_timebase_info()
provides a fraction to convert the clock value to a number of nanoseconds.
According to the documentation, mach_timebase_info() is always equals to one
and does never fail, even if the function may fail according to its prototype.
mach_absolute_time() has a resolution of 1 nanosecond.
CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW represents monotonic time since some
unspecified starting point. They cannot be set.
CLOCK_MONOTONIC_RAW is specific to Linux. It is similar to CLOCK_MONOTONIC, but
provides access to a raw hardware-based time that is not subject to NTP
adjustments. CLOCK_MONOTONIC_RAW requires Linux 2.6.28 or later.
On Linux, NTP may adjust CLOCK_MONOTONIC rate, but not jump backward. If On Linux, NTP may adjust CLOCK_MONOTONIC rate, but not jump backward. If
available, CLOCK_MONOTONIC_RAW should be used instead of CLOCK_MONOTONIC to available, CLOCK_MONOTONIC_RAW should be used instead of CLOCK_MONOTONIC to
avoid the NTP adjustement. CLOCK_MONOTONIC stops while the machine is avoid the NTP adjustement.
suspended.
Resolution: CLOCK_MONOTONIC stops while the machine is suspended.
* mach_absolute_time(): 1 nanosecond clock_gettime() fails if the system does not support the specified clock,
* CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW: be read using clock_getres().
1 nanosecond on Linux for example.
* GetTickCount(), GetTickCount64(): 1 millisecond
May fail?
* mach_absolute_time() cannot fail. According to the documentation,
mach_timebase_info() does never fail, even if the function has a return
value.
* clock_gettime() can fail if the system does not support the specified clock,
whereas the standard C library supports it. For example, CLOCK_MONOTONIC_RAW whereas the standard C library supports it. For example, CLOCK_MONOTONIC_RAW
requires a kernel version 2.6.28 or later. requires a kernel version 2.6.28 or later.
* GetTickCount() and GetTickCount64() cannot fail
clock_getres() gives the clock resolution. It is 1 nanosecond on Linux.
.. note:: .. note::
clock_gettime() requires to link the program with the realtime ("rt") library. clock_gettime() requires to link the program with the realtime ("rt") library.
.. note:: QueryPerformanceCounter
^^^^^^^^^^^^^^^^^^^^^^^
High-resolution performance counter. It is monotonic.
QueryPerformanceFrequency() gives its frequency.
On Windows XP, QueryPerformanceFrequency() is the processor frequency and
QueryPerformanceCounter() is the TSC of the current processor. Windows XP
had a bug (see `KB896256 <http://support.microsoft.com/?id=896256>`_): on a
multiprocessor computer, QueryPerformanceCounter() returned a different value
for each processor.
QueryPerformanceFrequency() should only be called once: the frequency will not
change while the system is running. It fails if the installed hardware does not
support a high-resolution performance counter.
GetTickCount(), GetTickCount64()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
GetTickCount() and GetTickCount64() are monotonic and cannot fail.
GetTickCount64() was added to Windows Vista and Windows Server 2008. GetTickCount64() was added to Windows Vista and Windows Server 2008.
The clock resolution is 1 millisecond.
System time System time
----------- -----------
System time on Windows Windows: GetSystemTimeAsFileTime
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The system time can be read using GetSystemTimeAsFileTime(), ftime() and The system time can be read using GetSystemTimeAsFileTime(), ftime() and
time(). time().
@ -276,12 +240,12 @@ The system time can be set using SetSystemTime().
System time on UNIX System time on UNIX
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
gettimeofday(), ftime(), time() and clock_settime(CLOCK_REALTIME) return the gettimeofday(), ftime(), time() and clock_gettime(CLOCK_REALTIME) return the
system clock. system clock.
Resolution: Resolution:
* clock_settime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux * clock_gettime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux
* gettimeofday(): 1 microsecond * gettimeofday(): 1 microsecond
* ftime(): 1 millisecond * ftime(): 1 millisecond
* time(): 1 second * time(): 1 second
@ -299,7 +263,8 @@ Process
^^^^^^^ ^^^^^^^
* Windows: GetProcessTimes() * Windows: GetProcessTimes()
* clock_gettime(CLOCK_PROCESS_CPUTIME_ID): High-resolution per-process timer from the CPU. * clock_gettime(CLOCK_PROCESS_CPUTIME_ID): High-resolution per-process timer
from the CPU.
* clock(): * clock():
* Windows: The elapsed wall-clock time since the start of the process * Windows: The elapsed wall-clock time since the start of the process
@ -331,28 +296,6 @@ Resolution:
See also pthread_getcpuclockid(). See also pthread_getcpuclockid().
QueryPerformanceCounter
-----------------------
Windows provides a high-resolution performance counter:
QueryPerformanceCounter(). Its frequency can be retrieved using
QueryPerformanceFrequency().
On Windows XP, QueryPerformanceFrequency() is the processor frequency and
QueryPerformanceCounter() is the TSC of the current processor. Windows XP
had a bug (see `KB896256 <http://support.microsoft.com/?id=896256>`_): on a
multiprocessor computer, QueryPerformanceCounter() returned a different value
for each processor.
QueryPerformanceCounter() is monotonic.
QueryPerformanceFrequency() fails if the installed hardware does not support a
high-resolution performance counter.
QueryPerformanceFrequency() should only be called once: the frequency will not
change while the system is running.
QueryUnbiasedInterruptTime QueryUnbiasedInterruptTime
-------------------------- --------------------------
@ -369,12 +312,12 @@ QueryUnbiasedInterruptTime() was introduced in Windows 7.
Alternatives: API design Alternatives: API design
======================== ========================
One function with a flag: time.try_monotonic(strict=False) One function with a flag: time.monotonic(strict=False)
---------------------------------------------------------- ----------------------------------------------------------
* time.try_monotonic(strict=False) falls back to another clock if no monotonic clock * time.monotonic(strict=False) falls back to another clock if no monotonic clock
is not available or does not work, but it does never fail. is not available or does not work, but it does never fail.
* time.try_monotonic(strict=True) raises OSError if monotonic clock fails or * time.monotonic(strict=True) raises OSError if monotonic clock fails or
NotImplementedError if the system does not provide a monotonic clock NotImplementedError if the system does not provide a monotonic clock
"A keyword argument that gets passed as a constant in the caller is usually "A keyword argument that gets passed as a constant in the caller is usually
@ -387,10 +330,10 @@ should be avoided.
One function, no flag One function, no flag
--------------------- ---------------------
time.try_monotonic() returns (time: float, is_monotonic: bool). time.monotonic() returns (time: float, is_monotonic: bool).
An alternative is to use a function attribute: time.try_monotonic.monotonic. The An alternative is to use a function attribute: time.monotonic.is_monotonic. The
attribute value would be None before the first call to time.try_monotonic(). attribute value would be None before the first call to time.monotonic().
Working around operating system bugs? Working around operating system bugs?