From a997c7b434631f51e00191acea2ba6097691e859 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 10 Oct 2017 02:51:50 -0700 Subject: bpo-31415: Add _PyTime_GetPerfCounter() and use it for -X importtime (#3936) * Add _PyTime_GetPerfCounter() * Use _PyTime_GetPerfCounter() for -X importtime --- Include/pytime.h | 16 +++++++++++ Modules/timemodule.c | 71 +++++++++++++++------------------------------ Python/import.c | 4 +-- Python/pytime.c | 81 +++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 112 insertions(+), 60 deletions(-) diff --git a/Include/pytime.h b/Include/pytime.h index 87ac7fc..95d482f 100644 --- a/Include/pytime.h +++ b/Include/pytime.h @@ -192,6 +192,22 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm); Return 0 on success, raise an exception and return -1 on error. */ PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm); +#ifdef MS_WINDOWS +PyAPI_FUNC(int) _PyTime_GetWinPerfCounterWithInfo( + _PyTime_t *t, + _Py_clock_info_t *info); +#endif + +/* Get the performance counter: clock with the highest available resolution to + measure a short duration. */ +PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void); + +/* Similar to _PyTime_GetPerfCounter(), + but get also clock info if info is non-NULL. */ +PyAPI_FUNC(int) _PyTime_GetPerfCounterWithInfo( + _PyTime_t *t, + _Py_clock_info_t *info); + #ifdef __cplusplus } #endif diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 34e057d..31050fb 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -60,6 +60,13 @@ Fractions of a second may be present if the system clock provides them."); #endif #endif +static PyObject* +_PyFloat_FromPyTime(_PyTime_t t) +{ + double d = _PyTime_AsSecondsDouble(t); + return PyFloat_FromDouble(d); +} + static PyObject * floatclock(_Py_clock_info_t *info) { @@ -81,47 +88,19 @@ floatclock(_Py_clock_info_t *info) } #endif /* HAVE_CLOCK */ -#ifdef MS_WINDOWS -#define WIN32_PERF_COUNTER -/* Win32 has better clock replacement; we have our own version, due to Mark - Hammond and Tim Peters */ -static PyObject* -win_perf_counter(_Py_clock_info_t *info) -{ - static LONGLONG cpu_frequency = 0; - static LONGLONG ctrStart; - LARGE_INTEGER now; - double diff; - - if (cpu_frequency == 0) { - LARGE_INTEGER freq; - QueryPerformanceCounter(&now); - ctrStart = now.QuadPart; - if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { - PyErr_SetFromWindowsErr(0); - return NULL; - } - cpu_frequency = freq.QuadPart; - } - QueryPerformanceCounter(&now); - diff = (double)(now.QuadPart - ctrStart); - if (info) { - info->implementation = "QueryPerformanceCounter()"; - info->resolution = 1.0 / (double)cpu_frequency; - info->monotonic = 1; - info->adjustable = 0; - } - return PyFloat_FromDouble(diff / (double)cpu_frequency); -} -#endif /* MS_WINDOWS */ - -#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK) +#if defined(MS_WINDOWS) || defined(HAVE_CLOCK) #define PYCLOCK static PyObject* pyclock(_Py_clock_info_t *info) { -#ifdef WIN32_PERF_COUNTER - return win_perf_counter(info); +#ifdef MS_WINDOWS + /* Win32 has better clock replacement; we have our own version, due to Mark + Hammond and Tim Peters */ + _PyTime_t t; + if (_PyTime_GetWinPerfCounterWithInfo(&t, info) < 0) { + return NULL; + } + return _PyFloat_FromPyTime(t); #else return floatclock(info); #endif @@ -939,13 +918,11 @@ static PyObject * pymonotonic(_Py_clock_info_t *info) { _PyTime_t t; - double d; if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) { assert(info != NULL); return NULL; } - d = _PyTime_AsSecondsDouble(t); - return PyFloat_FromDouble(d); + return _PyFloat_FromPyTime(t); } static PyObject * @@ -962,11 +939,11 @@ Monotonic clock, cannot go backward."); static PyObject* perf_counter(_Py_clock_info_t *info) { -#ifdef WIN32_PERF_COUNTER - return win_perf_counter(info); -#else - return pymonotonic(info); -#endif + _PyTime_t t; + if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) { + return NULL; + } + return _PyFloat_FromPyTime(t); } static PyObject * @@ -1448,13 +1425,11 @@ static PyObject* floattime(_Py_clock_info_t *info) { _PyTime_t t; - double d; if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) { assert(info != NULL); return NULL; } - d = _PyTime_AsSecondsDouble(t); - return PyFloat_FromDouble(d); + return _PyFloat_FromPyTime(t); } diff --git a/Python/import.c b/Python/import.c index 7554bf8..d396b4d 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1695,7 +1695,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, if (ximporttime) { import_level++; - t1 = _PyTime_GetMonotonicClock(); + t1 = _PyTime_GetPerfCounter(); accumulated = 0; } @@ -1711,7 +1711,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, mod != NULL); if (ximporttime) { - _PyTime_t cum = _PyTime_GetMonotonicClock() - t1; + _PyTime_t cum = _PyTime_GetPerfCounter() - t1; import_level--; fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n", diff --git a/Python/pytime.c b/Python/pytime.c index 8f275d2..bda6d8b 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -285,8 +285,8 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) #endif static int -_PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, - long unit_to_ns) +_PyTime_FromDouble(_PyTime_t *t, double value, _PyTime_round_t round, + long unit_to_ns) { /* volatile avoids optimization changing how numbers are rounded */ volatile double d; @@ -315,7 +315,7 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)"); return -1; } - return _PyTime_FromFloatObject(t, d, round, unit_to_ns); + return _PyTime_FromDouble(t, d, round, unit_to_ns); } else { long long sec; @@ -779,19 +779,80 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) return pymonotonic(tp, info, 1); } + +#ifdef MS_WINDOWS int -_PyTime_Init(void) +_PyTime_GetWinPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info) +{ + static LONGLONG cpu_frequency = 0; + static LONGLONG ctrStart; + LARGE_INTEGER now; + double diff; + + if (cpu_frequency == 0) { + LARGE_INTEGER freq; + QueryPerformanceCounter(&now); + ctrStart = now.QuadPart; + if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { + PyErr_SetFromWindowsErr(0); + return -1; + } + cpu_frequency = freq.QuadPart; + } + QueryPerformanceCounter(&now); + diff = (double)(now.QuadPart - ctrStart); + if (info) { + info->implementation = "QueryPerformanceCounter()"; + info->resolution = 1.0 / (double)cpu_frequency; + info->monotonic = 1; + info->adjustable = 0; + } + + diff = diff / (double)cpu_frequency; + return _PyTime_FromDouble(t, diff, _PyTime_ROUND_FLOOR, SEC_TO_NS); +} +#endif + + +int +_PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info) +{ +#ifdef MS_WINDOWS + return _PyTime_GetWinPerfCounterWithInfo(t, info); +#else + return _PyTime_GetMonotonicClockWithInfo(t, info); +#endif +} + + +_PyTime_t +_PyTime_GetPerfCounter(void) { _PyTime_t t; + if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) { + /* should not happen, _PyTime_Init() checked the clock at startup */ + Py_UNREACHABLE(); + } + return t; +} - /* ensure that the system clock works */ - if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) - return -1; - /* ensure that the operating system provides a monotonic clock */ - if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) +int +_PyTime_Init(void) +{ + /* check that the 3 most important clocks are working properly + to not have to check for exceptions at runtime. If a clock works once, + it cannot fail in next calls. */ + _PyTime_t t; + if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) { return -1; - + } + if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) { + return -1; + } + if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) { + return -1; + } return 0; } -- cgit v0.12