[Python-Dev] an alternative to embedding policy in PEP 418 (was: PEP 418: Add monotonic clock)

Cameron Simpson cs at zip.com.au
Mon Apr 2 23:38:43 CEST 2012


On 02Apr2012 13:37, Victor Stinner <victor.stinner at gmail.com> wrote:
| > I've just finished sketching out a skeleton here:
| >  https://bitbucket.org/cameron_simpson/css/src/fb476fcdcfce/lib/python/cs/clockutils.py
| 
| get_clock() returns None if no clock has the requested flags, whereas
| I expected an exception (LookupError or NotImplementError?).

That is deliberate. People can easily write fallback like this:

  clock = get_clock(T_MONOTONIC|T_HIRES) or get_clock(T_MONOTONIC)

With exceptions one gets a complicated try/except/else chain that is
much harder to read. With a second fallback the try/except gets even
worse.

If one wants an exception it is easy to follow up with:

  if not clock:
    raise RunTimeError("no suitable clocks on offer on this platform")

| get_clock() doesn't remember if a clock works or not (if it raises an
| OSError) and does not fallback to the next clock on error. See
| "pseudo-codes" in the PEP 418.

I presume the available clocks are all deduced from the platform. Your
pseudo code checks for OSError at fetch-the-clock time. I expect that
to occur once when the module is loaded, purely to populate the table
of avaiable platform clocks.

If you are concerned about clocks being available/unavailable at
different times (unplugging the GPS peripheral? just guessing here)
that will have to raise OSError during the now() call (assuming the
clock even exposes the failure; IMO it should when now() is called).

| The idea of flags attached to each clock is interesting, but I don't
| like the need of different list of clocks.

There's no need, just quality of implementation for the monotonic()/hires()
convenience calls, which express the (hoped to be common) policy of what
clock to offer for each.

We've just had pages upon pages of discussion about what clock to offer
for the rather bald monotonic() (et al) calls. The ordering of the
MONTONIC_CLOCKS list would express the result of that discussion,
in that the "better" clocks come first.

| Should I use
| MONTONIC_CLOCKS or HIRES_CLOCKS when I would like a monotonic and
| high-resolution clock?

Note that you don't need to provide a clock list at all; get_clock(0
will use ALL_CLOCKS by default, and hires() and monotonic() should each
have their own default list.

I'll put in montonic() and montonic_clock(clocklist=MONOTONIC_CLOCKS)
into the skeleton to make this clear; I see I've omitted them.

Regarding the choice itself: as the _caller_ (not the library author),
you must decide what you want most. You're already planning offering
monotonic() and hires() calls without my proposal! Taking your query "Should
I use MONTONIC_CLOCKS or HIRES_CLOCKS when I would like a monotonic and
high-resolution clock" is _already_ a problem. Of course you must call
monotonic() or hires() first under the current scheme, and must answer this
question anyway. Do you prefer hires? Use it first! No preference? Then the
question does not matter.

If I, as the caller, have a preference then it is obvious what to use.
If I do not have a preference then I can just call get_clock() with both
flags and then arbitrarily fall back to hires() or monotonic() if that
does not work.

| It would be simpler to have only one global and
| *private* list.

No. No no no no no!

The whole point is to let the user be _able_ to control the choices to a
fair degree without platform special knowledge. The lists are
deliberately _optional_ parameters and anyway hidden in the hires() and
monotonic() convenince functions; the user does not need to care about
them. But the picky user may! The lists align exactly one to one with
the feature flags, so there is no special knowledge present here that is
not already implicit in publishing the feature flags.

| If you have only one list of clocks, how do sort the list to get
| QueryPerformanceCounter when the user asks for highres and
| GetTickCount when the user asks for monotonic?

This is exactly why there are supposed to be different lists.
You have just argued against your objection above.

| The "if clock.flags &
| flags == flags:" test in get_clock() is maybe not enough. I suppose
| that we would have the following flags for Windows functions:
| 
| QueryPerformanceCounter.flags = T_HIRES
| GetTickCount.flags = T_MONOTONIC | T_STEADY
| 
| (or maybe QueryPerformanceCounter.flags = T_HIRES | T_MONOTONIC ?)

Obviously these depend on the clock characteristics. Is
QueryPerformanceCounter monotonic?

| monotonic_clock() should maybe try to get a clock using the following
| list of conditions:
|  - T_MONOTONIC | T_STEADY
|  - T_MONOTONIC | T_HIGHRES
|  - T_MONOTONIC

Sure, seems reasonable. That is library internal policy _for the convenince
monotonic() function()_.

| The T_HIGHRES flag in unclear, even in the PEP. According to the PEP,
| any monotonic clock is considered as a "high-resolution" clock. Do you
| agree?

Not particularly. I easily can imagine a clock with one second resolution
hich was monotonic. I would not expect it to have the T_HIRES flag.
Example: a synthetic monotonic clock based on a V7 UNIX time() call.

But, if it _happens_ that all the monotonic clocks are also hires, so be
it. That would be an empirical outcome, not policy.

| So we would have:
| 
| GetTickCount.flags = T_MONOTONIC | T_STEADY | T_HIGHRES
| 
| Even if GetTickCount has only an accuracy of 15 ms :-/

T_HIGHRES is a quality call, surely? If 15ms is too sloppy for a "high
resolution, the is should _not_ have the T_HIRES flag.

| Can list please give the list of flags of each clocks listed in the
| PEP? Only clocks used for time.time, time.monotonic and time.highres
| (not process and thread clocks nor QueryUnbiasedInterruptTime).
| 
| >      # get a clock object - often a singleton under the hood
| >      T = get_clock(T_MONOTONIC|T_HIRES) or get_clock(T_STEADY|T_HIRES)
| >      # what kind of clock did I get?
| >      print T.flags
| >      # get the current time
| >      now = T.now
| 
| The API looks much more complex than the API proposed in PEP 418 just
| to get the time. You have to call a function to get a function, and
| then call the function, instead of just calling a function directly.

One could have a flat interface as in the PEP, but then the results are
not inspectable; the user cannot find out what specific clock, or even
kind of clock, was used for the result returned.

Unless you want to subclass float for the return values.  You could return
instances of a float with meta information pointing at the clock used to
provide it. I'm -0.5 on that idea.

Another advantage of returning a clock object is that it avoids the
difficulty of switching implementations behind the user's back, an issue
raised in the discussion and rightly rejected as a bad occurence.

If the user is handed a clock object, _they_ keep the "current clock in
use" state by by having the object reference.

| Instead of returning an object with a now() method, I would prefer to
| get directly the function getting time, and another function to get
| "metadata" of the clock.

Then they're disconnected. How do I know the get-metadata call accesses
the clock I just used? Only by having library internal global state.

I agree some people probably want the flat "get me the time" call, and have
no real objection to such existing. But I strongly object to not giving
the user control over what they use, and the current API offers no
control.

| > This removes policy from the library functions and makes it both simple
| > and obvious in the user's calling code, and also makes it possible for
| > the user to inspect the clock and find out what quality/flavour of clock
| > they got.
| 
| I'm not sure that users understand correctly differences between all
| these clocks and are able to use your API correctly. How should I
| combinese these 3 flags (T_HIRES, T_MONOTONIC and T_STEADY)? Can I use
| any combinaison?

Of course. Just as with web searches, too many flags may get you an
empty result on some platforms, hence the need to fall back. But the
_nature_ of the fallback should be in the user's hands. The hires() et
al calls can of course offer convenient presupplied fallback according
to the preconceptions of the library authors, hopefully well tuned to
common users' needs. But if should not be the only mode offered, because
you don't know the user's needs.

| Which flags are "portable"? Or should I always use an explicit
| fallback to ensure getting a clock on any platform?

All the flag are portable, but if the platform doesn't supply a clock
with the requested flags, even if there's only one flag, the correct
result is "None" for the clock offered.

Note you can supply no flags!

You can always fall all the way back to 0 for the flags; in the skeleton
provided that will get you UNIXClock, which is a wrapper for the
existing time.time(). In fact, I'll make the flags parameter also
optional for get_clock(), defaulting to 0, to make that easy. That
becomes your totally portable call:-)

| Could you please update your code according to my remarks? I will try
| to integrate it into the PEP. A PEP should list all alternatives!

Surely.

The only updates I can see are to provide the flat interface
(instead of via clock-object indirection) and the missing hires_clock()
and monotonic_clock() convenience methods.

I'll do that. Followup post shortly with new code URL.
Would you propose other specific additions?

Cheers,
-- 
Cameron Simpson <cs at zip.com.au> DoD#743
http://www.cskk.ezoshosting.com/cs/

Whatever is not nailed down is mine.  What I can pry loose is not nailed
down. - Collis P. Huntingdon


More information about the Python-Dev mailing list