[Python-checkins] peps: PEP 418: Add time.perf_counter(), time.process_time(), deprecate time.clock()

victor.stinner python-checkins at python.org
Thu Apr 12 13:40:36 CEST 2012


http://hg.python.org/peps/rev/fb41f0b2e1bd
changeset:   4231:fb41f0b2e1bd
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Thu Apr 12 13:38:06 2012 +0200
summary:
  PEP 418: Add time.perf_counter(), time.process_time(), deprecate time.clock()

Reorder also sections.

files:
  pep-0418.txt |  300 ++++++++++++++++++++++++--------------
  1 files changed, 187 insertions(+), 113 deletions(-)


diff --git a/pep-0418.txt b/pep-0418.txt
--- a/pep-0418.txt
+++ b/pep-0418.txt
@@ -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 at gmail.com>, Victor Stinner <victor.stinner at gmail.com>
@@ -13,7 +13,8 @@
 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 @@
 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.
 
-Functions
-=========
+New functions:
 
-To fulfill the use cases, the functions' properties are:
+ * 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.time(): system clock, "wall clock".
-* time.monotonic(): monotonic clock
-* time.get_clock_info(name): get information on the specified time function
+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.
 
 
-time.time()
------------
+Python functions
+================
 
-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.
+New functions
+-------------
 
-It is available on all platforms and cannot fail.
+time.get_clock_info(name)
+^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Pseudo-code [#pseudo]_::
+Get information on the specified clock. Supported clocks:
 
-    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()
+ * "clock": time.clock()
+ * "monotonic": time.monotonic()
+ * "perf_counter": time.perf_counter()
+ * "process_time": time.process_time()
+ * "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)
 
 
 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 @@
    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 @@
             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 @@
         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 @@
 =========================  ===============  =============
 GetProcessTimes()          100 ns           No
 CLOCK_PROCESS_CPUTIME_ID   1 ns             No
+getrusage()                1 µs             No
 clock()                    \-               No
 =========================  ===============  =============
 
@@ -899,9 +1000,10 @@
 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 @@
 * 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 @@
 Alternatives: API design
 ========================
 
-Other names for time.monotonic()
---------------------------------
+Other names for new functions
+-----------------------------
+
+time.monotonic():
 
 * time.counter()
 * time.seconds()
@@ -1136,6 +1244,12 @@
 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 @@
    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
 =========
 

-- 
Repository URL: http://hg.python.org/peps


More information about the Python-checkins mailing list