🔒 EXCLUSIVE: Issue - Collection

This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: time.clock() should emit a DeprecationWarning
Type: Stage: resolved
Components: Library (Lib) Versions: Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: belopolsky, benhoyt, benjamin.peterson, ethan.furman, fdrake, lemburg, mrabarnett, serhiy.storchaka, vstinner
Priority: normal Keywords: patch

Created on 2017-10-17 13:00 by vstinner, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 4018 closed vstinner, 2017-10-17 13:05
PR 4020 merged vstinner, 2017-10-17 19:36
PR 4062 closed vstinner, 2017-10-20 17:08
Messages (35)
msg304502 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 13:00
The behaviour of the time.clock() function is not portable: on Windows it mesures wall-clock, whereas it measures CPU time on Unix. Python has time.process_time() and time.perf_counter(), since Python 3.3, which are portable, well defined and have a better resolution.

time.clock() was deprecated in Python 3.3 documentation, but calling time.clock() didn't raise a DeprecationWarning. I proposed to remove it from Python 3.7.

Sometimes, a deprecated function raises a DeprecationWarning in Python version N, before removing it from Python version N. time.clock() doesn't emit such warning, but I consider that it is possible to remove it anyway.

While it can be annoying to have to patch code to no more use time.clock(), I consider that it's worth it for portability and better clock resolution.

Attached PR removes time.clock() and time.get_clock_info() doens't accept 'clock' anymore.

Current time.clock() documentation:
----------------------------
.. function:: clock()

   .. index::
      single: CPU time
      single: processor time
      single: benchmarking

   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
   "processor time", depends on that of the C function of the same name.

   On Windows, this function returns wall-clock seconds elapsed since the first
   call to this function, as a floating point number, based on the Win32 function
   :c:func:`QueryPerformanceCounter`. The resolution is typically better than one
   microsecond.

   .. deprecated:: 3.3
      The behaviour of this function depends on the platform: use
      :func:`perf_counter` or :func:`process_time` instead, depending on your
      requirements, to have a well defined behaviour.
----------------------------
msg304503 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 13:09
With the PR, Python 3.7 will still requires the C clock() function to build on Unix, since time.process_time() uses it as the last fallback if all other functions failed.

Maybe we can require to have other functions used by time.process_time() (clock_gettime(CLOCK_PROF), getrusage(), times(), ...), but I consider that it can be done later. This change may impact the Python portability: compilation error when building Python, see bpo-22624.
msg304504 - (view) Author: Ben Hoyt (benhoyt) * Date: 2017-10-17 13:26
I don't think this is a good idea. I still use time.clock() out of habit (on Windows), and from the PR it's clear that the standard library and tests do too. I wouldn't be at all surprised to find others do as well.

I realize it's been deprecated since 3.3, but without a DeprecatingWarning, that's easy to miss. What about adding a DeprecatingWarning for 3.7 and deprecate it later, maybe in 3.9? (I know that's a long time, but time.clock() is an old function so we should be cautious!)
msg304505 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-17 13:27
I think it is better to not remove time.clock() until EOL of 2.7. And in any case it first should start emitting a deprecation warning for a one or two releases.
msg304506 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 13:35
Ben Hoyt: "... from the PR it's clear that the standard library and tests do too."

I disagree here. The standard library doesn't use time.clock() since Python 3.3. Better clocks like time.perf_counter() or time.monotonic() are now used in the standard library.

My PR onl changes two old tests and the very old turtledemo. I never used turtledemo, I didn't now that it exists neither :-)


"I wouldn't be at all surprised to find others do as well."

Oh, me neither. I'm quite sure that time.clock() is used in the wild. The problem is that you should not use it :-)


"I realize it's been deprecated since 3.3, but without a DeprecatingWarning, that's easy to miss. What about adding a DeprecatingWarning for 3.7 and deprecate it later, maybe in 3.9? (I know that's a long time, but time.clock() is an old function so we should be cautious!)"

I never understood the willingness of DeprecationWarning since almost nobody configures Python to run them. In my experience, even when they are displayed, they are usually ignored until the feature is really removed and then people only really start to look at the issue :-)

But I'm fine with keeping the function and emit a warning in Python 3.7, and remove it from Python 3.8.
msg304507 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-17 13:38
A runtime deprecation warning was not emitted in 3.3 because two alternatives, time.process_time() and time.perf_counter(), were not available before 3.3. It would be hard to write a warning-free code that supports 3.3 and earlier versions at the same time. But now 3.2 is virtually out of use and I think we can start emitting a deprecation warning at runtime.

And maybe make 2to3 replacing time.clock() with time.process_time() or time.perf_counter()?
msg304508 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 13:39
Serhiy: "I think it is better to not remove time.clock() until EOL of 2.7. And in any case it first should start emitting a deprecation warning for a one or two releases."

I really hate using 2.7 EOL as the final countdown to start changing things. Are we building a new "Python 3" chaos where everything explode at once?

IMHO it's not that hard to write code compatible with Python 2 and Python 3:

try:
   from time import perf_counter # Python 3
except ImportError:
   from time import clock as perf_counter # Python 2

time.clock() is probably not the only code which requires two code paths in any non trivial application which wants to stay compatible with Python 2.

time.clock() is usually used for benchmarking. I hope that all benchmarking code was already patched to use time.perf_counter() when available, to make the code portable and get better resolution on Unix.
msg304513 - (view) Author: Matthew Barnett (mrabarnett) * (Python triager) Date: 2017-10-17 16:52
@Victor: True, people often ignore DeprecationWarning anyway, but that's their problem, at least you can say "well, you were warned". They might not have read the documentation on it recently because they have not felt the need to read again about a function with which they are already familiar.
msg304516 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 19:36
I proposed PR 4020 to emit a DeprecationWarning in time.clock() and time.get_clock_info('clock').
msg304536 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 21:46
New changeset 884d13a55fc328e2e1e3948a82b361b30804b818 by Victor Stinner in branch 'master':
time.clock() now emits a DeprecationWarning (GH-4020)
https://github.com/python/cpython/commit/884d13a55fc328e2e1e3948a82b361b30804b818
msg304537 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-17 21:49
Ok, I modified time.clock() and time.get_clock_info('clock') to emit a DeprecationWarning.

Let's wait for Python 3.8 to remove time.clock() ;-)

Thanks for the reviews Serhiy Storchaka and Diana Clarke!
msg304555 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2017-10-18 05:53
time.cock() is used in a lot of code. Why can't we simply replace the functionality with one of the other functions ?

The documentation certainly allows for such a change, since it pretty much just says that only the delta between two values has a meaning.
msg304556 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2017-10-18 06:32
I agree with MAL; removing functions just makes multi-version code more painful.  At least have the DeprecationWarning active for two releases before removing it.
msg304561 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-18 08:28
Marc Andre Lemburg: "time.cock() is used in a lot of code. Why can't we simply replace the functionality with one of the other functions ? The documentation certainly allows for such a change, since it pretty much just says that only the delta between two values has a meaning."

Currently time.clock() is the same clock than time.perf_counter() on Windows, but it's closer to CPU time as time.process_time() on Unix.

time.perf_counter() and time.process_time() have a different name because the they are different clocks and don't measure the same thing. The main obvious difference is that time.process_time() doesn't include time elapsed during sleep.

>>> import time
>>> c1=time.perf_counter(); p1=time.process_time(); time.sleep(1); c2=time.perf_counter(); p2=time.process_time()
>>> c2-c1, p2-p1
(1.0010841980038094, 7.308599999999998e-05)

I don't see how to modify clock() to make it behave the same on Windows and Unix without breaking any application which relies on the current clock() behaviour.
msg304565 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-18 08:45
(I reopen the issue since the discussion is not over.)

Marc-Andre Lemburg: "time.cock() is used in a lot of code."

I ran a quick search on GitHub. I found different use cases of time.clock():

1) Measure performance. On Windows, time.clock() has a very good precision, *much* better than any other clock. For example, time.process_time() has a resolution of 15.6 ms whereas time.clock() has a resolution of 100 ns (or 0.0001 ms):
https://www.python.org/dev/peps/pep-0564/#windows


2) An explicit check that time.clock() doesn't include sleep. I guess that people are using such snippet to explain this behaviour?

https://github.com/pbarton666/PES_Python_examples_and_solutions/blob/master/py_time.py

---

#py_time.py

import time
print(time.clock(), time.time())
time.sleep(1)  #seconds
print(time.clock(), time.time())
---


Ethan: "I agree with MAL; removing functions just makes multi-version code more painful."

We have two choices:

* Deprecate and then remove time.clock(): break the backward compatibility -- currently chosen solution
* Modify time.clock() to get a portable behaviour: break the backward compatibility

Depending which clock we choose for time.clock(), we may break more and less code, I would vote for using the time.perf_counter() clock in time.clock(). It means no change on Windows, but modify the behaviour on Unix if the code sleeps (time.sleep or I/O).

It seems like time.clock() is mostly used for benchmarking, and time.perf_counter() is documented as the best clock for such use case. In the benchmarks I saw on GitHub, the benchmarked code didn't sleep, it was more likely pure CPU-bound.


Marc-Andre, Ethan: What do you think of removing the deprecation warning from the C (my last commit), leave the deprecation warning in the documentation, and modify time.clock() to become an alias to time.perf_counter()?

By alias, I really mean time.clock = time.perf_counter, so time.clock.__name__ would say "perf_counter".
msg304567 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-18 09:09
Initially the time module was a thin wrapper around C and OS time-related functions. It may be confusing that the behavior of time.clock() differs from the behavior of C's clock().

The documentation for clock() on MSDN suggests to use GetProcessTimes() for the behavior conforming the C standard.

https://msdn.microsoft.com/en-us/library/4e2ess30.aspx
msg304569 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-18 09:15
"Initially the time module was a thin wrapper around C and OS time-related functions. It may be confusing that the behavior of time.clock() differs from the behavior of C's clock()."

Well, there is the theory, and there is the practice. Someone decided to implement time.clock() with QueryPerformanceCounter() on Windows, and so time.clock() is no more a thin wrapper to the C clock() function since at least Python 2.7 (I didn't check earlier versions).

Portability on clocks is even more than complex than in the os module, especially when you want the same behaviour on Windows and Unix. The PEP 418 added new clocks with a better defined behaviour to "fix" this issue.


"The documentation for clock() on MSDN suggests to use GetProcessTimes() for the behavior conforming the C standard."

time.process_time() is implemented with GetProcessTimes(). That's why time.clock() suggests to either replace it with time.perf_counter() or time.process_time().
msg304572 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2017-10-18 10:08
On 18.10.2017 11:45, STINNER Victor wrote:
> Marc-Andre, Ethan: What do you think of removing the deprecation warning from the C (my last commit), leave the deprecation warning in the documentation, and modify time.clock() to become an alias to time.perf_counter()?
> 
> By alias, I really mean time.clock = time.perf_counter, so time.clock.__name__ would say "perf_counter".

That's what I think would be a better solution, since the
absolute value of time.clock() is never used, only the difference.

If you then get better accuracy in that difference, things
can only get better, so this is not really backwards compatibility
issue (nothing gets worse).

Not sure whether the function name would cause an incompatibility
issue. I doubt it, but if it does we could have time.clock()
as function which then simply calls time.perf_counter().
msg304664 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2017-10-20 17:09
I wrote the PR 4062 which removes time.clock() implementation: time.clock becomes an alias to time.perf_counter.

haypo@selma$ ./python
Python 3.7.0a2+ (heads/clock-dirty:16b6a3033e, Oct 20 2017, 18:55:58) 
>>> import time
>>> time.clock is time.perf_counter
True
msg304668 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-20 17:46
This will break a code that depends on the current behavior on non-Windows platforms. And this will contradict the expectation of non-Windows programmers. If change the behavior of clock() on non-Windows platforms, it should be done only after the period of emitting FutureWarning.
msg304671 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2017-10-20 19:00
We definitely need time with either DeprecationWarning or FutureWarning before removing/substituting a function.
msg304746 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2017-10-22 12:39
On 20.10.2017 19:46, Serhiy Storchaka wrote:
> 
> This will break a code that depends on the current behavior on non-Windows platforms. And this will contradict the expectation of non-Windows programmers. If change the behavior of clock() on non-Windows platforms, it should be done only after the period of emitting FutureWarning.

Could you explain which behavior is changed by this on non-Windows
platforms ?

time.clock() only switches to a more accurate clock. That's pretty
much it. Which clock it used on which platform was platform
dependent anyway, so there's no real change in behavior.

For benchmarking and other measurements, time.time() was recommended
on Unix and time.clock() on Windows. time.clock() never good
resolution on Unix, so the situation only improves by using
a more accurate clock.
msg304747 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-22 13:14
On non-Windows platforms clock() returns the processor time, perf_counter() does include time elapsed during sleep.

>>> import time
>>> start = time.clock(); time.sleep(1); print(time.clock() - start)
9.700000000001374e-05
>>> start = time.perf_counter(); time.sleep(1); print(time.perf_counter() - start)
1.000714950998372
msg304748 - (view) Author: Marc-Andre Lemburg (lemburg) * (Python committer) Date: 2017-10-22 13:16
On 22.10.2017 15:14, Serhiy Storchaka wrote:
> 
> Serhiy Storchaka <[email protected]> added the comment:
> 
> On non-Windows platforms clock() returns the processor time, perf_counter() does include time elapsed during sleep.
> 
>>>> import time
>>>> start = time.clock(); time.sleep(1); print(time.clock() - start)
> 9.700000000001374e-05
>>>> start = time.perf_counter(); time.sleep(1); print(time.perf_counter() - start)
> 1.000714950998372

Thanks for pointing that out. I didn't know.

Is there a different clock with similar accuracy we can use
to only count CPU time on Unix ?
msg304749 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2017-10-22 13:38
Yes, it is time.process_time(). This was the cause of adding two new functions time.perf_counter() and time.process_time() and deprecating (in the documentation only) time.clock(). On Windows time.clock() does include time elapsed during sleep, on non-Windows it doesn't. time.clock() should be replaced with the one of these functions, depending on the purpose of its use. Only the user knows for what purposes it uses time.clock() and what is the correct replacement.