PEP 418: Add fallback=True argument to time.monotonic()

* time.monotonic() doesn't use QPC anymore
 * Rewrite also time.monotonic() and time.highres() definitions.
This commit is contained in:
Victor Stinner 2012-03-31 00:50:05 +02:00
parent 2e69f4914f
commit 8e9a3c50ca
1 changed files with 53 additions and 36 deletions

View File

@ -13,7 +13,7 @@ Python-Version: 3.3
Abstract
========
Add time.monotonic() and time.highres() functions to Python 3.3.
Add time.monotonic(fallback=True) and time.highres() functions to Python 3.3.
Rationale
@ -24,12 +24,19 @@ 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()
* Benchmark, profiling, timeout: time.highres()
* Event scheduler: time.monotonic()
* Event scheduler: time.monotonic(), or time.monotonic(fallback=False)
Functions
=========
* time.time(): system clock, "wall clock"
* time.highres(): clock with the best accuracy
* time.monotonic(fallback=True): monotonic clock. If no monotonic clock is
available, falls back to system clock by default, or raises an OSError if
*fallback* is False. time.monotonic(fallback=True) cannot go backward.
time.time()
-----------
@ -68,12 +75,12 @@ Pseudo-code [#pseudo]_: ::
return _time.time()
time.monotonic()
----------------
time.monotonic(fallback=True)
-----------------------------
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 that cannot go backward, its rate is as steady as possible. 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
available on GNU/Hurd for example.
@ -85,34 +92,21 @@ Pseudo-code [#pseudo]_: ::
if os.name == 'nt':
# GetTickCount64() requires Windows Vista, Server 2008 or later
if hasattr(time, '_GetTickCount64'):
_get_tick_count = _time.GetTickCount64
def monotonic(fallback=True):
return _time.GetTickCount64()
else:
def _get_tick_count():
def monotonic(fallback=True):
ticks = _time.GetTickCount()
if ticks < _get_tick_count.last:
if ticks < monotonic.last:
# Integer overflow detected
_get_tick_count.delta += 2**32
_get_tick_count.last = ticks
return ticks + _get_tick_count.delta
_get_tick_count.last = 0
_get_tick_count.delta = 0
def monotonic():
if monotonic.use_performance_counter:
try:
return _time.QueryPerformanceCounter()
except OSError:
# QueryPerformanceFrequency() may fail, if the installed
# hardware does not support a high-resolution performance
# counter for example
monotonic.use_performance_counter = False
# Fallback to GetTickCount/GetTickCount64 which has
# a lower resolution
return _get_tick_count()
monotonic.use_performance_counter = True
monotonic.delta += 2**32
monotonic.last = ticks
return ticks + monotonic.delta
monotonic.last = 0
monotonic.delta = 0
elif os.name == 'mac':
def monotonic():
def monotonic(fallback=True):
if monotonic.factor is None:
factor = _time.mach_timebase_info()
monotonic.factor = timebase[0] / timebase[1]
@ -120,31 +114,45 @@ Pseudo-code [#pseudo]_: ::
monotonic.factor = None
elif os.name.startswith('sunos'):
def monotonic():
def monotonic(fallback=True):
if monotonic.use_clock_highres:
try:
time.clock_gettime(time.CLOCK_HIGHRES)
except OSError:
monotonic.use_clock_highres = False
return time.gethrtime()
if monotonic.use_gethrtime:
try:
return time.gethrtime()
except OSError:
if not fallback:
raise
monotonic.use_gethrtime = False
return time.time()
monotonic.use_clock_highres = (hasattr(time, 'clock_gettime')
and hasattr(time, 'CLOCK_HIGHRES'))
monotonic.use_gethrtime = True
elif hasattr(time, "clock_gettime"):
def monotonic():
def monotonic(fallback=True):
while monotonic.clocks:
try:
clk_id = monotonic.clocks[0]
return time.clock_gettime(clk_id)
except OSError:
# CLOCK_MONOTONIC_RAW requires a Linux kernel >= 2.6.28
if len(monotonic.clocks) == 1 and not fallback:
raise
del monotonic.clocks[0]
return time.clock_gettime(time.CLOCK_MONOTONIC)
return time.time()
monotonic.clocks = []
if hasattr(time, 'CLOCK_MONOTONIC_RAW'):
monotonic.clocks.append(time.CLOCK_MONOTONIC_RAW)
if hasattr(time, 'CLOCK_HIGHRES'):
monotonic.clocks.append(time.CLOCK_HIGHRES)
monotonic.clocks.append(time.CLOCK_MONOTONIC)
On Windows, QueryPerformanceCounter() is not used even if it has a better
resolution than GetTickCount(). It is not reliable and has too much issues.
.. note::
@ -157,20 +165,29 @@ Pseudo-code [#pseudo]_: ::
time.highres()
--------------
High-resolution clock: use a monotonic clock if available, or fallback to the
system time.
Clock with the best available resolution.
It is available on all platforms and cannot fail.
Pseudo-code::
def highres():
if monotonic.use_performance_counter:
try:
return _time.QueryPerformanceCounter()
except OSError:
# QueryPerformanceFrequency() may fail, if the installed
# hardware does not support a high-resolution performance
# counter for example
monotonic.use_performance_counter = False
if highres.use_monotonic:
# Monotonic clock is preferred over system clock
try:
return time.monotonic()
except OSError:
highres.use_monotonic = False
return time.time()
highres.use_performance_counter = (os.name == 'nt')
highres.use_monotonic = hasattr(time, 'monotonic')