Author: Philip Jenvey <pjen...@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) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit