PEP 418: Add time.perf_counter(), time.process_time(), deprecate time.clock()

Reorder also sections.
This commit is contained in:
Victor Stinner 2012-04-12 13:38:06 +02:00
parent 288203ec0e
commit a724856238
1 changed files with 187 additions and 119 deletions

View File

@ -1,5 +1,5 @@
PEP: 418
Title: Add a monotonic time functions
Title: Add monotonic time, performance counter and process time functions
Version: $Revision$
Last-Modified: $Date$
Author: Jim Jewett <jimjjewett@gmail.com>, Victor Stinner <victor.stinner@gmail.com>
@ -13,7 +13,8 @@ Python-Version: 3.3
Abstract
========
Add time.monotonic() and time.get_clock_info(name) functions to Python 3.3.
Add time.get_clock_info(name), time.monotonic(), time.perf_counter() and
time.process_time() functions to Python 3.3.
Rationale
@ -25,62 +26,74 @@ early or too late when the system clock is set manually or adjusted
automatically by NTP. A monotonic clock should be used instead to not be
affected by system clock updates.
Clocks:
To measure the performance of a function, time.clock() can be used but it
is very different on Windows and on Unix. On Windows, time.clock() includes
time elapsed during sleep, whereas it does not on Unix. time.clock() precision
is very good on Windows, but very bad on Unix. A new time.perf_counter() should
be used instead to always get the most precise performance counter with a
portable behaviour.
* time.time(): system clock
* time.monotonic(): monotonic clock
To measure CPU time, Python does not provide directly a portable function.
time.clock() can be used on Unix, but it has a bad precision.
resource.getrusage() can also be used on Unix, but it requires to get fields of
a structure and compute the sum of time spend in kernel space and user space.
A new time.process_time() function is portable counter, always measure CPU time
(don't include time elapsed during sleep), and has the best available
precision.
Each operating system implements clocks and performance counters differently,
and it is useful to know exactly which function is used and some properties of
the clock like its resolution and its precision. A new time.get_clock_info()
function gives access to all available information of each Python time
function.
New functions:
* time.monotonic(): timeout and scheduling, not affected by system clock
updates
* time.perf_counter(): benchmarking, most precise clock for short period
* time.process_time(): profiling, CPU time of the process
time.clock() is deprecated by this PEP because it is not portable:
time.perf_counter() or time.process_time() should be used instead, depending
on your requirements.
Functions
=========
Python functions
================
To fulfill the use cases, the functions' properties are:
New functions
-------------
* time.time(): system clock, "wall clock".
* time.monotonic(): monotonic clock
* time.get_clock_info(name): get information on the specified time function
time.get_clock_info(name)
^^^^^^^^^^^^^^^^^^^^^^^^^
Get information on the specified clock. Supported clocks:
time.time()
-----------
* "clock": time.clock()
* "monotonic": time.monotonic()
* "perf_counter": time.perf_counter()
* "process_time": time.process_time()
* "time": 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. It is not monotonic.
Return a dictionary with the following keys:
It is available on all platforms and cannot fail.
* Mandatory keys:
Pseudo-code [#pseudo]_::
* "function" (str): name of the underlying operating system function.
Examples: "QueryPerformanceCounter()", "clock_gettime(CLOCK_REALTIME)".
* "resolution" (float): resolution in seconds of the clock
* "is_monotonic" (bool): True if the clock cannot go backward
if os.name == "nt":
def time():
return _time.GetSystemTimeAsFileTime()
else:
def time():
if hasattr(time, "clock_gettime"):
try:
# resolution = 1 nanosecond
return time.clock_gettime(time.CLOCK_REALTIME)
except OSError:
# CLOCK_REALTIME is not supported (unlikely)
pass
if hasattr(_time, "gettimeofday"):
try:
# resolution = 1 microsecond
return _time.gettimeofday()
except OSError:
# gettimeofday() should not fail
pass
if hasattr(_time, "ftime"):
# resolution = 1 millisecond
return _time.ftime()
else:
# resolution = 1 second
return _time.time()
* Optional keys:
* "precision" (float): precision in seconds of the clock
* "is_adjusted" (bool): True if the clock can be adjusted (e.g. by a NTP
daemon)
time.monotonic()
----------------
^^^^^^^^^^^^^^^^
Monotonic clock, cannot go backward. It is not affected by system clock
updates. The reference point of the returned value is undefined so only the
@ -138,8 +151,117 @@ precision than GetTickCount(). It is not reliable and has too many issues.
running for more than 49 days.
time.perf_counter()
^^^^^^^^^^^^^^^^^^^
Performance counter used for benchmarking. It is monotonic, cannot go backward.
It does include time elapsed during sleep. The reference point of the returned
value is undefined so only the difference between consecutive calls is valid
and is number of seconds.
Pseudo-code::
def perf_counter():
if perf_counter.use_performance_counter:
if perf_counter.perf_frequency is None:
try:
perf_counter.perf_frequency = float(_time.QueryPerformanceFrequency())
except OSError:
# QueryPerformanceFrequency() fails if the installed
# hardware does not support a high-resolution performance
# counter
perf_counter.use_performance_counter = False
else:
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
else:
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
if perf_counter.use_monotonic:
# Monotonic clock is preferred over system clock
try:
return time.monotonic()
except OSError:
perf_counter.use_monotonic = False
return time.time()
perf_counter.use_performance_counter = (os.name == 'nt')
if perf_counter.use_performance_counter:
perf_counter.perf_frequency = None
perf_counter.use_monotonic = hasattr(time, 'monotonic')
time.process_time()
^^^^^^^^^^^^^^^^^^^
Process time used for profiling: some of kernel and user-space CPU
time. It does not include time elapsed during sleep. The reference point of
the returned value is undefined so only the difference between consecutive
calls is valid.
Pseudo-code::
if os.name == 'nt':
def process_time():
handle = win32process.GetCurrentProcess()
process_times = win32process.GetProcessTimes(handle)
return (process_times['UserTime'] + process_times['KernelTime']) * 1e-7
elif (hasattr(time, 'clock_gettime')
and hasattr(time, 'CLOCK_PROCESS_CPUTIME_ID')):
def process_time():
return time.clock_gettime(time.CLOCK_PROCESS_CPUTIME_ID)
else:
try:
import resource
except ImportError:
def process_time():
return _time.clock()
else:
def process_time():
usage = resource.getrusage(resource.RUSAGE_SELF)
return usage[0] + usage[1]
Existing functions
------------------
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. It is not monotonic.
It is available on all platforms and cannot fail.
Pseudo-code [#pseudo]_::
if os.name == "nt":
def time():
return _time.GetSystemTimeAsFileTime()
else:
def time():
if hasattr(time, "clock_gettime"):
try:
# resolution = 1 nanosecond
return time.clock_gettime(time.CLOCK_REALTIME)
except OSError:
# CLOCK_REALTIME is not supported (unlikely)
pass
if hasattr(_time, "gettimeofday"):
try:
# resolution = 1 microsecond
return _time.gettimeofday()
except OSError:
# gettimeofday() should not fail
pass
if hasattr(_time, "ftime"):
# resolution = 1 millisecond
return _time.ftime()
else:
# resolution = 1 second
return _time.time()
time.sleep()
------------
^^^^^^^^^^^^
Suspend execution for the given number of seconds. The actual suspension time
may be less than that requested because any caught signal will terminate the
@ -184,9 +306,11 @@ Pseudo-code [#pseudo]_::
seconds = int(seconds)
_time.sleep(seconds)
Deprecated functions
--------------------
time.clock()
------------
^^^^^^^^^^^^
On Unix, return the current processor time as a floating point number expressed
in seconds. The precision, and in fact the very definition of the meaning of
@ -222,30 +346,6 @@ Pseudo-code [#pseudo]_::
clock = _time.clock
time.get_clock_info(name)
-------------------------
Get information on the specified clock. Supported clocks:
* "clock": time.clock()
* "monotonic": time.monotonic()
* "time": time.time()
Return a dictionary with the following keys:
* Mandatory keys:
* "function" (str): name of the underlying operating system function.
Examples: "QueryPerformanceCounter()", "clock_gettime(CLOCK_REALTIME)".
* "resolution" (float): resolution in seconds of the clock
* "is_monotonic" (bool): True if the clock cannot go backward
* Optional keys:
* "precision" (float): precision in seconds of the clock
* "is_adjusted" (bool): True if the clock can be adjusted (e.g. by a NTP
daemon)
Glossary
========
@ -890,6 +990,7 @@ Name Resolution Include sleep
========================= =============== =============
GetProcessTimes() 100 ns No
CLOCK_PROCESS_CPUTIME_ID 1 ns No
getrusage() 1 µs No
clock() \- No
========================= =============== =============
@ -899,9 +1000,10 @@ Examples of clock precision on x86_64:
Name Operating system Precision
========================= ================ ===============
CLOCK_PROCESS_CPUTIME_ID Linux 3.2 1 ns
clock() Linux 3.2 1 µs
clock() SunOS 5.11 1 µs
getrusage() Linux 3.0 4 ms
clock() FreeBSD 8.2 7.8 ms
clock() Linux 3.2 10 ms
clock() OpenBSD 5.0 10 ms
GetProcessTimes() Windows Seven 15.6 ms
========================= ================ ===============
@ -937,6 +1039,10 @@ Resolution:
* The clock resolution can be read using clock_getres().
* GetProcessTimes(): call GetSystemTimeAdjustment().
Python source code includes a portable library to get the process time (CPU
time): `Tools/pybench/systimes.py
<http://hg.python.org/cpython/file/tip/Tools/pybench/systimes.py>`_.
Thread time
-----------
@ -1123,8 +1229,10 @@ Other functions
Alternatives: API design
========================
Other names for time.monotonic()
--------------------------------
Other names for new functions
-----------------------------
time.monotonic():
* time.counter()
* time.seconds()
@ -1136,6 +1244,12 @@ Other names for time.monotonic()
The name "time.try_monotonic()" was also proposed when time.monotonic() was
falling back to the system clock when no monotonic clock was available.
time.perf_counter():
* time.hires()
* time.highres()
* time.timer()
Only expose operating system clocks
-----------------------------------
@ -1234,52 +1348,6 @@ Issues of a hacked monotonic function:
see the same clock value
Deferred API: time.perf_counter()
=================================
Performance counter used for benchmarking and profiling. The reference point of
the returned value is undefined so only the difference between consecutive calls is
valid and is number of seconds.
Pseudo-code::
def perf_counter():
if perf_counter.use_performance_counter:
if perf_counter.perf_frequency is None:
try:
perf_counter.perf_frequency = float(_time.QueryPerformanceFrequency())
except OSError:
# QueryPerformanceFrequency() fails if the installed
# hardware does not support a high-resolution performance
# counter
perf_counter.use_performance_counter = False
else:
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
else:
return _time.QueryPerformanceCounter() / perf_counter.perf_frequency
if perf_counter.use_monotonic:
# Monotonic clock is preferred over system clock
try:
return time.monotonic()
except OSError:
perf_counter.use_monotonic = False
return time.time()
perf_counter.use_performance_counter = (os.name == 'nt')
if perf_counter.use_performance_counter:
perf_counter.perf_frequency = None
perf_counter.use_monotonic = hasattr(time, 'monotonic')
Other names proposed for time.perf_counter():
* time.hires()
* time.highres()
* time.timer()
Python source code includes a portable library to get the process time (CPU
time): `Tools/pybench/systimes.py
<http://hg.python.org/cpython/file/tip/Tools/pybench/systimes.py>`_.
Footnotes
=========