[pypy-commit] pypy py3k: merge py3k-get_clock_info
pjenvey
pypy.commits at gmail.com
Sat May 28 17:19:34 EDT 2016
Author: Philip Jenvey <pjenvey at underboss.org>
Branch: py3k
Changeset: r84803:7e5d1ee11bb2
Date: 2016-05-28 14:18 -0700
http://bitbucket.org/pypy/pypy/changeset/7e5d1ee11bb2/
Log: merge py3k-get_clock_info
adds time.get_clock_info for posix, windows is still a work in
progress
diff --git a/lib-python/conftest.py b/lib-python/conftest.py
--- a/lib-python/conftest.py
+++ b/lib-python/conftest.py
@@ -418,7 +418,7 @@
RegrTest('test_threading.py', usemodules="thread", core=True),
RegrTest('test_threading_local.py', usemodules="thread", core=True),
RegrTest('test_threadsignals.py', usemodules="thread"),
- RegrTest('test_time.py', core=True),
+ RegrTest('test_time.py', core=True, usemodules="struct"),
RegrTest('test_timeit.py'),
RegrTest('test_timeout.py'),
RegrTest('test_tk.py'),
diff --git a/pypy/module/time/__init__.py b/pypy/module/time/__init__.py
--- a/pypy/module/time/__init__.py
+++ b/pypy/module/time/__init__.py
@@ -40,6 +40,7 @@
'struct_time': 'app_time.struct_time',
'__doc__': 'app_time.__doc__',
'strptime': 'app_time.strptime',
+ 'get_clock_info': 'app_time.get_clock_info'
}
def startup(self, space):
diff --git a/pypy/module/time/app_time.py b/pypy/module/time/app_time.py
--- a/pypy/module/time/app_time.py
+++ b/pypy/module/time/app_time.py
@@ -1,7 +1,8 @@
# NOT_RPYTHON
from _structseq import structseqtype, structseqfield
-
+from types import SimpleNamespace
+import time
class struct_time(metaclass=structseqtype):
__module__ = 'time'
name = 'time.struct_time'
@@ -26,6 +27,27 @@
import _strptime # from the CPython standard library
return _strptime._strptime_time(string, format)
+def get_clock_info(name):
+ info = SimpleNamespace()
+ info.implementation = ""
+ info.monotonic = 0
+ info.adjustable = 0
+ info.resolution = 1.0
+
+ if name == "time":
+ time.time(info)
+ elif name == "monotonic":
+ time.monotonic(info)
+ elif name == "clock":
+ time.clock(info)
+ elif name == "perf_counter":
+ time.perf_counter(info)
+ elif name == "process_time":
+ time.process_time(info)
+ else:
+ raise ValueError("unknown clock")
+ return info
+
__doc__ = """This module provides various functions to manipulate time values.
There are two standard representations of time. One is the number
diff --git a/pypy/module/time/interp_time.py b/pypy/module/time/interp_time.py
--- a/pypy/module/time/interp_time.py
+++ b/pypy/module/time/interp_time.py
@@ -39,7 +39,9 @@
includes = ['windows.h'],
post_include_bits = [
"RPY_EXTERN\n"
- "BOOL pypy_timemodule_setCtrlHandler(HANDLE event);"],
+ "BOOL pypy_timemodule_setCtrlHandler(HANDLE event);\n"
+ "RPY_EXTERN ULONGLONG pypy_GetTickCount64(FARPROC address);"
+ ],
separate_module_sources=['''
static HANDLE interrupt_event;
@@ -60,6 +62,12 @@
return SetConsoleCtrlHandler(CtrlHandlerRoutine, TRUE);
}
+ ULONGLONG pypy_GetTickCount64(FARPROC address) {
+ ULONGLONG (WINAPI *func)();
+ *(FARPROC*)&func = address;
+ return func();
+ }
+
'''],
)
_setCtrlHandlerRoutine = rffi.llexternal(
@@ -68,6 +76,21 @@
compilation_info=eci,
save_err=rffi.RFFI_SAVE_LASTERROR)
+ pypy_GetTickCount64 = rffi.llexternal(
+ 'pypy_GetTickCount64',
+ [rffi.VOIDP],
+ rffi.ULONGLONG, compilation_info=eci)
+
+ from rpython.rlib.rdynload import GetModuleHandle, dlsym
+ hKernel32 = GetModuleHandle("KERNEL32")
+ try:
+ _GetTickCount64_handle = dlsym(hKernel32, 'GetTickCount64')
+ def _GetTickCount64():
+ return pypy_GetTickCount64(_GetTickCount64_handle)
+ except KeyError:
+ _GetTickCount64_handle = lltype.nullptr(rffi.VOIDP.TO)
+
+ HAS_GETTICKCOUNT64 = _GetTickCount64_handle != lltype.nullptr(rffi.VOIDP.TO)
class GlobalState:
def __init__(self):
self.init()
@@ -103,6 +126,14 @@
def get_interrupt_event(self):
return globalState.interrupt_event
+ # XXX: Can I just use one of the state classes above?
+ # I don't really get why an instance is better than a plain module
+ # attr, but following advice from armin
+ class TimeState(object):
+ def __init__(self):
+ self.n_overflow = 0
+ self.last_ticks = 0
+ time_state = TimeState()
_includes = ["time.h"]
if _POSIX:
@@ -112,7 +143,8 @@
class CConfig:
_compilation_info_ = ExternalCompilationInfo(
- includes = _includes
+ includes=_includes,
+ libraries=rtime.libraries
)
CLOCKS_PER_SEC = platform.ConstantInteger("CLOCKS_PER_SEC")
clock_t = platform.SimpleType("clock_t", rffi.ULONG)
@@ -150,6 +182,13 @@
("tm_mon", rffi.INT), ("tm_year", rffi.INT), ("tm_wday", rffi.INT),
("tm_yday", rffi.INT), ("tm_isdst", rffi.INT)])
+ # TODO: Figure out how to implement this...
+ CConfig.ULARGE_INTEGER = platform.Struct("struct ULARGE_INTEGER", [
+ ("tm_sec", rffi.INT),
+ ("tm_min", rffi.INT), ("tm_hour", rffi.INT), ("tm_mday", rffi.INT),
+ ("tm_mon", rffi.INT), ("tm_year", rffi.INT), ("tm_wday", rffi.INT),
+ ("tm_yday", rffi.INT), ("tm_isdst", rffi.INT)])
+
if _MACOSX:
CConfig.TIMEBASE_INFO = platform.Struct("struct mach_timebase_info", [
("numer", rffi.UINT),
@@ -190,7 +229,35 @@
glob_buf = lltype.malloc(tm, flavor='raw', zero=True, immortal=True)
if cConfig.has_gettimeofday:
- c_gettimeofday = external('gettimeofday', [rffi.VOIDP, rffi.VOIDP], rffi.INT)
+ c_gettimeofday = external('gettimeofday',
+ [cConfig.timeval, rffi.VOIDP], rffi.INT)
+ if _WIN:
+ GetSystemTimeAsFileTime = external('GetSystemTimeAsFileTime',
+ [rwin32.FILETIME],
+ lltype.VOID)
+ def gettimeofday(space, w_info=None):
+ with lltype.scoped_alloc(rwin32.FILETIME) as system_time:
+ GetSystemTimeAsFileTime(system_time)
+ # XXX:
+ #seconds = float(timeval.tv_sec) + timeval.tv_usec * 1e-6
+ # XXX: w_info
+ return space.w_None
+ else:
+ def gettimeofday(space, w_info=None):
+ with lltype.scoped_alloc(CConfig.timeval) as timeval:
+ ret = c_gettimeofday(timeval, rffi.NULL)
+ if ret != 0:
+ raise exception_from_saved_errno(space, space.w_OSError)
+
+ if w_info is not None:
+ _setinfo(space, w_info,
+ "gettimeofday()", 1e-6, False, True)
+
+ seconds = float(timeval.tv_sec) + timeval.tv_usec * 1e-6
+ return space.wrap(seconds)
+
+
+
TM_P = lltype.Ptr(tm)
c_time = external('time', [rffi.TIME_TP], rffi.TIME_T)
c_gmtime = external('gmtime', [rffi.TIME_TP], TM_P,
@@ -498,23 +565,41 @@
if not 0 <= rffi.getintfield(t_ref, 'c_tm_yday') <= 365:
raise oefmt(space.w_ValueError, "day of year out of range")
-def time(space):
+def time(space, w_info=None):
"""time() -> floating point number
Return the current time in seconds since the Epoch.
Fractions of a second may be present if the system clock provides them."""
+ if HAS_CLOCK_GETTIME:
+ with lltype.scoped_alloc(TIMESPEC) as timespec:
+ ret = c_clock_gettime(cConfig.CLOCK_REALTIME, timespec)
+ if ret == 0:
+ if w_info is not None:
+ with lltype.scoped_alloc(TIMESPEC) as tsres:
+ ret = c_clock_getres(cConfig.CLOCK_REALTIME, tsres)
+ if ret == 0:
+ res = _timespec_to_seconds(tsres)
+ else:
+ res = 1e-9
+ _setinfo(space, w_info, "clock_gettime(CLOCK_REALTIME)",
+ res, False, True)
+ return space.wrap(_timespec_to_seconds(timespec))
+ # XXX: rewrite the final fallback into gettimeofday w/ windows
+ # GetSystemTimeAsFileTime() support
secs = pytime.time()
+ if w_info is not None:
+ # XXX: time.time delegates to the host python's time.time
+ # (rtime.time) so duplicate its internals for now
+ if rtime.HAVE_GETTIMEOFDAY:
+ implementation = "gettimeofday()"
+ resolution = 1e-6
+ else: # assume using ftime(3)
+ implementation = "ftime()"
+ resolution = 1e-3
+ _setinfo(space, w_info, implementation, resolution, False, True)
return space.wrap(secs)
-def clock(space):
- """clock() -> floating point number
-
- Return the CPU time or real time since the start of the process or since
- the first call to clock(). This has as much precision as the system
- records."""
-
- return space.wrap(pytime.clock())
def ctime(space, w_seconds=None):
"""ctime([seconds]) -> string
@@ -716,10 +801,49 @@
if _WIN:
# untested so far
- _GetTickCount64 = rwin32.winexternal('GetTickCount64', [], rffi.ULONGLONG)
+ _GetTickCount = rwin32.winexternal('GetTickCount', [], rwin32.DWORD)
+ LPDWORD = rwin32.LPDWORD
+ _GetSystemTimeAdjustment = rwin32.winexternal(
+ 'GetSystemTimeAdjustment',
+ [LPDWORD, LPDWORD, rwin32.LPBOOL],
+ rffi.INT)
+ def monotonic(space, w_info=None):
+ result = 0
+ if HAS_GETTICKCOUNT64:
+ print('has count64'.encode('ascii'))
+ result = _GetTickCount64() * 1e-3
+ else:
+ print("nocount64")
+ ticks = _GetTickCount()
+ if ticks < time_state.last_ticks:
+ time_state.n_overflow += 1
+ time_state.last_ticks = ticks
+ result = math.ldexp(time_state.n_overflow, 32)
+ result = result + ticks
+ result = result * 1e-3
- def monotonic(space):
- return space.wrap(_GetTickCount64() * 1e-3)
+ if w_info is not None:
+ if HAS_GETTICKCOUNT64:
+ implementation = "GetTickCount64()"
+ else:
+ implementation = "GetTickCount()"
+ resolution = 1e-7
+ print("creating a thing".encode("ascii"))
+ with lltype.scoped_alloc(rwin32.LPDWORD.TO, 1) as time_adjustment, \
+ lltype.scoped_alloc(rwin32.LPDWORD.TO, 1) as time_increment, \
+ lltype.scoped_alloc(rwin32.LPBOOL.TO, 1) as is_time_adjustment_disabled:
+ print("CREATED".encode("ascii"))
+ ok = _GetSystemTimeAdjustment(time_adjustment,
+ time_increment,
+ is_time_adjustment_disabled)
+ if not ok:
+ # Is this right? Cargo culting...
+ raise wrap_windowserror(space,
+ rwin32.lastSavedWindowsError("GetSystemTimeAdjustment"))
+ resolution = resolution * time_increment[0]
+ print("out of with".encode("ascii"))
+ _setinfo(space, w_info, implementation, resolution, True, False)
+ return space.wrap(result)
elif _MACOSX:
c_mach_timebase_info = external('mach_timebase_info',
@@ -730,39 +854,60 @@
timebase_info = lltype.malloc(cConfig.TIMEBASE_INFO, flavor='raw',
zero=True, immortal=True)
- def monotonic(space):
+ def monotonic(space, w_info=None):
if rffi.getintfield(timebase_info, 'c_denom') == 0:
c_mach_timebase_info(timebase_info)
time = rffi.cast(lltype.Signed, c_mach_absolute_time())
numer = rffi.getintfield(timebase_info, 'c_numer')
denom = rffi.getintfield(timebase_info, 'c_denom')
nanosecs = time * numer / denom
+ if w_info is not None:
+ # Do I need to convert to float indside the division?
+ # Looking at the C, I would say yes, but nanosecs
+ # doesn't...
+ res = (numer / denom) * 1e-9
+ _setinfo(space, w_info, "mach_absolute_time()", res, True, False)
secs = nanosecs / 10**9
rest = nanosecs % 10**9
return space.wrap(float(secs) + float(rest) * 1e-9)
else:
assert _POSIX
- if cConfig.CLOCK_HIGHRES is not None:
- def monotonic(space):
- return clock_gettime(space, cConfig.CLOCK_HIGHRES)
- else:
- def monotonic(space):
- return clock_gettime(space, cConfig.CLOCK_MONOTONIC)
-
+ def monotonic(space, w_info=None):
+ if cConfig.CLOCK_HIGHRES is not None:
+ clk_id = cConfig.CLOCK_HIGHRES
+ implementation = "clock_gettime(CLOCK_HIGHRES)"
+ else:
+ clk_id = cConfig.CLOCK_MONOTONIC
+ implementation = "clock_gettime(CLOCK_MONOTONIC)"
+ w_result = clock_gettime(space, clk_id)
+ if w_info is not None:
+ with lltype.scoped_alloc(TIMESPEC) as tsres:
+ ret = c_clock_getres(clk_id, tsres)
+ if ret == 0:
+ res = _timespec_to_seconds(tsres)
+ else:
+ res = 1e-9
+ _setinfo(space, w_info, implementation, res, True, False)
+ return w_result
if _WIN:
- def perf_counter(space):
+ def perf_counter(space, w_info=None):
+ # What if the windows perf counter fails?
+ # Cpython falls back to monotonic and then clock
+ # Shouldn't we?
+ # TODO: Discuss on irc
+
+ # TODO: Figure out how to get at the internals of this
return space.wrap(win_perf_counter())
else:
- def perf_counter(space):
- return monotonic(space)
-
+ def perf_counter(space, w_info=None):
+ return monotonic(space, w_info=w_info)
if _WIN:
# untested so far
- def process_time(space):
+ def process_time(space, w_info=None):
from rpython.rlib.rposix import GetCurrentProcess, GetProcessTimes
current_process = GetCurrentProcess()
with lltype.scoped_alloc(rwin32.FILETIME) as creation_time, \
@@ -775,29 +920,46 @@
kernel_time.c_dwHighDateTime << 32)
user_time2 = (user_time.c_dwLowDateTime |
user_time.c_dwHighDateTime << 32)
+ if w_info is not None:
+ _setinfo(space, w_info, "GetProcessTimes()", 1e-7, True, False)
return space.wrap((float(kernel_time2) + float(user_time2)) * 1e-7)
else:
have_times = hasattr(rposix, 'c_times')
- def process_time(space):
+ def process_time(space, w_info=None):
if HAS_CLOCK_GETTIME and (
cConfig.CLOCK_PROF is not None or
cConfig.CLOCK_PROCESS_CPUTIME_ID is not None):
if cConfig.CLOCK_PROF is not None:
clk_id = cConfig.CLOCK_PROF
+ implementation = "clock_gettime(CLOCK_PROF)"
else:
clk_id = cConfig.CLOCK_PROCESS_CPUTIME_ID
+ implementation = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)"
with lltype.scoped_alloc(TIMESPEC) as timespec:
ret = c_clock_gettime(clk_id, timespec)
if ret == 0:
+ if w_info is not None:
+ with lltype.scoped_alloc(TIMESPEC) as tsres:
+ ret = c_clock_getres(clk_id, tsres)
+ if ret == 0:
+ res = _timespec_to_seconds(tsres)
+ else:
+ res = 1e-9
+ _setinfo(space, w_info,
+ implementation, res, True, False)
return space.wrap(_timespec_to_seconds(timespec))
+
if True: # XXX available except if it isn't?
from rpython.rlib.rtime import (c_getrusage, RUSAGE, RUSAGE_SELF,
decode_timeval)
with lltype.scoped_alloc(RUSAGE) as rusage:
ret = c_getrusage(RUSAGE_SELF, rusage)
if ret == 0:
+ if w_info is not None:
+ _setinfo(space, w_info,
+ "getrusage(RUSAGE_SELF)", 1e-6, True, False)
return space.wrap(decode_timeval(rusage.c_ru_utime) +
decode_timeval(rusage.c_ru_stime))
if have_times:
@@ -805,5 +967,44 @@
ret = rposix.c_times(tms)
if rffi.cast(lltype.Signed, ret) != -1:
cpu_time = float(tms.c_tms_utime + tms.c_tms_stime)
+ if w_info is not None:
+ _setinfo(space, w_info, "times()",
+ 1.0 / rposix.CLOCK_TICKS_PER_SECOND,
+ True, False)
return space.wrap(cpu_time / rposix.CLOCK_TICKS_PER_SECOND)
return clock(space)
+
+if _WIN:
+ def clock(space, w_info=None):
+ """clock() -> floating point number
+
+ Return the CPU time or real time since the start of the process or since
+ the first call to clock(). This has as much precision as the system
+ records."""
+ return space.wrap(win_perf_counter(space, w_info=w_info))
+
+else:
+ _clock = external('clock', [], clock_t)
+ def clock(space, w_info=None):
+ """clock() -> floating point number
+
+ Return the CPU time or real time since the start of the process or since
+ the first call to clock(). This has as much precision as the system
+ records."""
+ value = _clock()
+ # Is this casting correct?
+ if value == rffi.cast(clock_t, -1):
+ raise oefmt(space.w_RuntimeError,
+ "the processor time used is not available or its value"
+ "cannot be represented")
+ if w_info is not None:
+ _setinfo(space, w_info,
+ "clock()", 1.0 / CLOCKS_PER_SEC, True, False)
+ return space.wrap((1.0 * value) / CLOCKS_PER_SEC)
+
+
+def _setinfo(space, w_info, impl, res, mono, adj):
+ space.setattr(w_info, space.wrap('implementation'), space.wrap(impl))
+ space.setattr(w_info, space.wrap('resolution'), space.wrap(res))
+ space.setattr(w_info, space.wrap('monotonic'), space.wrap(mono))
+ space.setattr(w_info, space.wrap('adjustable'), space.wrap(adj))
diff --git a/pypy/module/time/test/test_time.py b/pypy/module/time/test/test_time.py
--- a/pypy/module/time/test/test_time.py
+++ b/pypy/module/time/test/test_time.py
@@ -378,3 +378,18 @@
t2 = time.process_time()
# process_time() should not include time spent during sleep
assert (t2 - t1) < 0.05
+
+ def test_get_clock_info(self):
+ import time
+ clocks = ['clock', 'perf_counter', 'process_time', 'time']
+ if hasattr(time, 'monotonic'):
+ clocks.append('monotonic')
+ for name in clocks:
+ info = time.get_clock_info(name)
+ assert isinstance(info.implementation, str)
+ assert info.implementation != ''
+ assert isinstance(info.monotonic, bool)
+ assert isinstance(info.resolution, float)
+ assert info.resolution > 0.0
+ assert info.resolution <= 1.0
+ assert isinstance(info.adjustable, bool)
diff --git a/rpython/rlib/rwin32.py b/rpython/rlib/rwin32.py
--- a/rpython/rlib/rwin32.py
+++ b/rpython/rlib/rwin32.py
@@ -46,6 +46,7 @@
LPWSTR = rffi_platform.SimpleType("LPWSTR", rffi.CWCHARP)
LPCWSTR = rffi_platform.SimpleType("LPCWSTR", rffi.CWCHARP)
LPDWORD = rffi_platform.SimpleType("LPDWORD", rffi.UINTP)
+ LPBOOL = rffi_platform.SimpleType("LPBOOL", rffi.LONGP)
SIZE_T = rffi_platform.SimpleType("SIZE_T", rffi.SIZE_T)
ULONG_PTR = rffi_platform.SimpleType("ULONG_PTR", rffi.ULONG)
More information about the pypy-commit
mailing list