bpo-31415: Add _PyTime_GetPerfCounter() and use it for -X importtime (#3936)
* Add _PyTime_GetPerfCounter() * Use _PyTime_GetPerfCounter() for -X importtime
This commit is contained in:
parent
14aa00b519
commit
a997c7b434
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -285,7 +285,7 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise)
|
|||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
/* volatile avoids optimization changing how numbers are rounded */
|
||||
|
@ -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_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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
/* ensure that the system clock works */
|
||||
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
|
||||
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) {
|
||||
return -1;
|
||||
|
||||
/* ensure that the operating system provides a monotonic clock */
|
||||
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
|
||||
}
|
||||
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) {
|
||||
return -1;
|
||||
|
||||
}
|
||||
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue