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
========
Add time.monotonic(), time.hires() and time.try_monotonic() functions to Python
3.3.
Add time.monotonic() and time.hires() functions to Python 3.3.
Rationale
@ -24,33 +23,9 @@ Use cases:
* 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()
* Implement a timeout: use monotonic clock, or fallback to the clock with
the highest resolution. time.try_monotonic()
* Benchmark and profiling: time.hires(), with a manual fallback to time.time()
* Benchmark, profiling, timeout: time.hires()
* 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
=========
@ -60,7 +35,7 @@ time.time()
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
forward, and is not monotonic.
forward. It is not monotonic.
It is available on all platforms and cannot fail.
@ -96,12 +71,12 @@ Pseudo-code [#pseudo]_: ::
time.monotonic()
----------------
Monotonic clock advancing at a monotonic rate relative to real time. It
cannot go backward. Its rate may be adjusted by NTP. The reference point of the
returned value is undefined so only the difference of consecutive calls is
valid.
Clock advancing at a monotonic rate relative to real time. It cannot go
backward. Its rate may be adjusted by NTP. The reference point of the returned
value is undefined so only the difference of consecutive calls is 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.
Pseudo-code [#pseudo]_: ::
@ -157,53 +132,21 @@ Pseudo-code [#pseudo]_: ::
time.hires()
------------
High-resolution clock. It has an unspecified starting point and may be
adjusted. The clock starting point changes when the system is resumed after
being suspended.
High-resolution clock: use a monotonic clock if available, or fallback to the
system time.
It is available on all platforms and cannot fail.
Pseudo-code [#pseudo]_: ::
if os.name == 'nt':
def hires():
return _time.QueryPerformanceCounter()
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:
def hires():
if hires.use_monotonic:
try:
return time.monotonic()
except (AttributeError, OSError):
try_monotonic.use_monotonic = False
if try_monotonic.use_hires:
try:
return time.hires()
except (AttributeError, OSError):
try_monotonic.use_hires = False
except OSError:
hires.use_monotonic = False
return time.time()
try_monotonic.use_monotonic = True
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.
hires.use_monotonic = hasattr(time, 'monotonic')
Clocks
@ -212,54 +155,75 @@ Clocks
Monotonic
---------
* mach_absolute_time(): Mac OS X provides a monotonic clock:
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().
mach_absolute_time
^^^^^^^^^^^^^^^^^^
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
available, CLOCK_MONOTONIC_RAW should be used instead of CLOCK_MONOTONIC to
avoid the NTP adjustement. CLOCK_MONOTONIC stops while the machine is
suspended.
avoid the NTP adjustement.
Resolution:
CLOCK_MONOTONIC stops while the machine is suspended.
* mach_absolute_time(): 1 nanosecond
* CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW: be read using clock_getres().
1 nanosecond on Linux for example.
* GetTickCount(), GetTickCount64(): 1 millisecond
clock_gettime() fails if the system does not support the specified clock,
whereas the standard C library supports it. For example, CLOCK_MONOTONIC_RAW
requires a kernel version 2.6.28 or later.
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
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::
clock_gettime() requires to link the program with the realtime ("rt") library.
.. note::
GetTickCount64() was added to Windows Vista and Windows Server 2008.
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.
The clock resolution is 1 millisecond.
System time
-----------
System time on Windows
^^^^^^^^^^^^^^^^^^^^^^
Windows: GetSystemTimeAsFileTime
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The system time can be read using GetSystemTimeAsFileTime(), ftime() and
time().
@ -276,12 +240,12 @@ The system time can be set using SetSystemTime().
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.
Resolution:
* clock_settime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux
* clock_gettime(): clock_getres(CLOCK_REALTIME), 1 nanosecond on Linux
* gettimeofday(): 1 microsecond
* ftime(): 1 millisecond
* time(): 1 second
@ -299,7 +263,8 @@ Process
^^^^^^^
* 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():
* Windows: The elapsed wall-clock time since the start of the process
@ -331,28 +296,6 @@ Resolution:
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
--------------------------
@ -369,12 +312,12 @@ QueryUnbiasedInterruptTime() was introduced in Windows 7.
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.
* 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
"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
---------------------
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
attribute value would be None before the first call to time.try_monotonic().
An alternative is to use a function attribute: time.monotonic.is_monotonic. The
attribute value would be None before the first call to time.monotonic().
Working around operating system bugs?