Commit a997c7b4 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

bpo-31415: Add _PyTime_GetPerfCounter() and use it for -X importtime (#3936)

* Add _PyTime_GetPerfCounter()
* Use _PyTime_GetPerfCounter() for -X importtime
parent 14aa00b5
...@@ -192,6 +192,22 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm); ...@@ -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. */ Return 0 on success, raise an exception and return -1 on error. */
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm); 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 #ifdef __cplusplus
} }
#endif #endif
......
...@@ -60,6 +60,13 @@ Fractions of a second may be present if the system clock provides them."); ...@@ -60,6 +60,13 @@ Fractions of a second may be present if the system clock provides them.");
#endif #endif
#endif #endif
static PyObject*
_PyFloat_FromPyTime(_PyTime_t t)
{
double d = _PyTime_AsSecondsDouble(t);
return PyFloat_FromDouble(d);
}
static PyObject * static PyObject *
floatclock(_Py_clock_info_t *info) floatclock(_Py_clock_info_t *info)
{ {
...@@ -81,47 +88,19 @@ floatclock(_Py_clock_info_t *info) ...@@ -81,47 +88,19 @@ floatclock(_Py_clock_info_t *info)
} }
#endif /* HAVE_CLOCK */ #endif /* HAVE_CLOCK */
#ifdef MS_WINDOWS #if defined(MS_WINDOWS) || defined(HAVE_CLOCK)
#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)
#define PYCLOCK #define PYCLOCK
static PyObject* static PyObject*
pyclock(_Py_clock_info_t *info) pyclock(_Py_clock_info_t *info)
{ {
#ifdef WIN32_PERF_COUNTER #ifdef MS_WINDOWS
return win_perf_counter(info); /* 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 #else
return floatclock(info); return floatclock(info);
#endif #endif
...@@ -939,13 +918,11 @@ static PyObject * ...@@ -939,13 +918,11 @@ static PyObject *
pymonotonic(_Py_clock_info_t *info) pymonotonic(_Py_clock_info_t *info)
{ {
_PyTime_t t; _PyTime_t t;
double d;
if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) { if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) {
assert(info != NULL); assert(info != NULL);
return NULL; return NULL;
} }
d = _PyTime_AsSecondsDouble(t); return _PyFloat_FromPyTime(t);
return PyFloat_FromDouble(d);
} }
static PyObject * static PyObject *
...@@ -962,11 +939,11 @@ Monotonic clock, cannot go backward."); ...@@ -962,11 +939,11 @@ Monotonic clock, cannot go backward.");
static PyObject* static PyObject*
perf_counter(_Py_clock_info_t *info) perf_counter(_Py_clock_info_t *info)
{ {
#ifdef WIN32_PERF_COUNTER _PyTime_t t;
return win_perf_counter(info); if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) {
#else return NULL;
return pymonotonic(info); }
#endif return _PyFloat_FromPyTime(t);
} }
static PyObject * static PyObject *
...@@ -1448,13 +1425,11 @@ static PyObject* ...@@ -1448,13 +1425,11 @@ static PyObject*
floattime(_Py_clock_info_t *info) floattime(_Py_clock_info_t *info)
{ {
_PyTime_t t; _PyTime_t t;
double d;
if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) { if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) {
assert(info != NULL); assert(info != NULL);
return NULL; return NULL;
} }
d = _PyTime_AsSecondsDouble(t); return _PyFloat_FromPyTime(t);
return PyFloat_FromDouble(d);
} }
......
...@@ -1695,7 +1695,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, ...@@ -1695,7 +1695,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
if (ximporttime) { if (ximporttime) {
import_level++; import_level++;
t1 = _PyTime_GetMonotonicClock(); t1 = _PyTime_GetPerfCounter();
accumulated = 0; accumulated = 0;
} }
...@@ -1711,7 +1711,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, ...@@ -1711,7 +1711,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
mod != NULL); mod != NULL);
if (ximporttime) { if (ximporttime) {
_PyTime_t cum = _PyTime_GetMonotonicClock() - t1; _PyTime_t cum = _PyTime_GetPerfCounter() - t1;
import_level--; import_level--;
fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n", fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n",
......
...@@ -285,8 +285,8 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) ...@@ -285,8 +285,8 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise)
#endif #endif
static int static int
_PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, _PyTime_FromDouble(_PyTime_t *t, double value, _PyTime_round_t round,
long unit_to_ns) long unit_to_ns)
{ {
/* volatile avoids optimization changing how numbers are rounded */ /* volatile avoids optimization changing how numbers are rounded */
volatile double d; volatile double d;
...@@ -315,7 +315,7 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, ...@@ -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)"); PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
return -1; return -1;
} }
return _PyTime_FromFloatObject(t, d, round, unit_to_ns); return _PyTime_FromDouble(t, d, round, unit_to_ns);
} }
else { else {
long long sec; long long sec;
...@@ -779,19 +779,80 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) ...@@ -779,19 +779,80 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
return pymonotonic(tp, info, 1); return pymonotonic(tp, info, 1);
} }
#ifdef MS_WINDOWS
int 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; _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 */ int
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) _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; return -1;
}
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) {
return -1;
}
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
return -1;
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment