Skip to content

Commit a997c7b

Browse files
authored
bpo-31415: Add _PyTime_GetPerfCounter() and use it for -X importtime (#3936)
* Add _PyTime_GetPerfCounter() * Use _PyTime_GetPerfCounter() for -X importtime
1 parent 14aa00b commit a997c7b

File tree

4 files changed

+112
-60
lines changed

4 files changed

+112
-60
lines changed

Include/pytime.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
192192
Return 0 on success, raise an exception and return -1 on error. */
193193
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);
194194

195+
#ifdef MS_WINDOWS
196+
PyAPI_FUNC(int) _PyTime_GetWinPerfCounterWithInfo(
197+
_PyTime_t *t,
198+
_Py_clock_info_t *info);
199+
#endif
200+
201+
/* Get the performance counter: clock with the highest available resolution to
202+
measure a short duration. */
203+
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);
204+
205+
/* Similar to _PyTime_GetPerfCounter(),
206+
but get also clock info if info is non-NULL. */
207+
PyAPI_FUNC(int) _PyTime_GetPerfCounterWithInfo(
208+
_PyTime_t *t,
209+
_Py_clock_info_t *info);
210+
195211
#ifdef __cplusplus
196212
}
197213
#endif

Modules/timemodule.c

Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ Fractions of a second may be present if the system clock provides them.");
6060
#endif
6161
#endif
6262

63+
static PyObject*
64+
_PyFloat_FromPyTime(_PyTime_t t)
65+
{
66+
double d = _PyTime_AsSecondsDouble(t);
67+
return PyFloat_FromDouble(d);
68+
}
69+
6370
static PyObject *
6471
floatclock(_Py_clock_info_t *info)
6572
{
@@ -81,47 +88,19 @@ floatclock(_Py_clock_info_t *info)
8188
}
8289
#endif /* HAVE_CLOCK */
8390

84-
#ifdef MS_WINDOWS
85-
#define WIN32_PERF_COUNTER
86-
/* Win32 has better clock replacement; we have our own version, due to Mark
87-
Hammond and Tim Peters */
88-
static PyObject*
89-
win_perf_counter(_Py_clock_info_t *info)
90-
{
91-
static LONGLONG cpu_frequency = 0;
92-
static LONGLONG ctrStart;
93-
LARGE_INTEGER now;
94-
double diff;
95-
96-
if (cpu_frequency == 0) {
97-
LARGE_INTEGER freq;
98-
QueryPerformanceCounter(&now);
99-
ctrStart = now.QuadPart;
100-
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
101-
PyErr_SetFromWindowsErr(0);
102-
return NULL;
103-
}
104-
cpu_frequency = freq.QuadPart;
105-
}
106-
QueryPerformanceCounter(&now);
107-
diff = (double)(now.QuadPart - ctrStart);
108-
if (info) {
109-
info->implementation = "QueryPerformanceCounter()";
110-
info->resolution = 1.0 / (double)cpu_frequency;
111-
info->monotonic = 1;
112-
info->adjustable = 0;
113-
}
114-
return PyFloat_FromDouble(diff / (double)cpu_frequency);
115-
}
116-
#endif /* MS_WINDOWS */
117-
118-
#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK)
91+
#if defined(MS_WINDOWS) || defined(HAVE_CLOCK)
11992
#define PYCLOCK
12093
static PyObject*
12194
pyclock(_Py_clock_info_t *info)
12295
{
123-
#ifdef WIN32_PERF_COUNTER
124-
return win_perf_counter(info);
96+
#ifdef MS_WINDOWS
97+
/* Win32 has better clock replacement; we have our own version, due to Mark
98+
Hammond and Tim Peters */
99+
_PyTime_t t;
100+
if (_PyTime_GetWinPerfCounterWithInfo(&t, info) < 0) {
101+
return NULL;
102+
}
103+
return _PyFloat_FromPyTime(t);
125104
#else
126105
return floatclock(info);
127106
#endif
@@ -939,13 +918,11 @@ static PyObject *
939918
pymonotonic(_Py_clock_info_t *info)
940919
{
941920
_PyTime_t t;
942-
double d;
943921
if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) {
944922
assert(info != NULL);
945923
return NULL;
946924
}
947-
d = _PyTime_AsSecondsDouble(t);
948-
return PyFloat_FromDouble(d);
925+
return _PyFloat_FromPyTime(t);
949926
}
950927

951928
static PyObject *
@@ -962,11 +939,11 @@ Monotonic clock, cannot go backward.");
962939
static PyObject*
963940
perf_counter(_Py_clock_info_t *info)
964941
{
965-
#ifdef WIN32_PERF_COUNTER
966-
return win_perf_counter(info);
967-
#else
968-
return pymonotonic(info);
969-
#endif
942+
_PyTime_t t;
943+
if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) {
944+
return NULL;
945+
}
946+
return _PyFloat_FromPyTime(t);
970947
}
971948

972949
static PyObject *
@@ -1448,13 +1425,11 @@ static PyObject*
14481425
floattime(_Py_clock_info_t *info)
14491426
{
14501427
_PyTime_t t;
1451-
double d;
14521428
if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) {
14531429
assert(info != NULL);
14541430
return NULL;
14551431
}
1456-
d = _PyTime_AsSecondsDouble(t);
1457-
return PyFloat_FromDouble(d);
1432+
return _PyFloat_FromPyTime(t);
14581433
}
14591434

14601435

Python/import.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,7 +1695,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
16951695

16961696
if (ximporttime) {
16971697
import_level++;
1698-
t1 = _PyTime_GetMonotonicClock();
1698+
t1 = _PyTime_GetPerfCounter();
16991699
accumulated = 0;
17001700
}
17011701

@@ -1711,7 +1711,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
17111711
mod != NULL);
17121712

17131713
if (ximporttime) {
1714-
_PyTime_t cum = _PyTime_GetMonotonicClock() - t1;
1714+
_PyTime_t cum = _PyTime_GetPerfCounter() - t1;
17151715

17161716
import_level--;
17171717
fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n",

Python/pytime.c

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise)
285285
#endif
286286

287287
static int
288-
_PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
289-
long unit_to_ns)
288+
_PyTime_FromDouble(_PyTime_t *t, double value, _PyTime_round_t round,
289+
long unit_to_ns)
290290
{
291291
/* volatile avoids optimization changing how numbers are rounded */
292292
volatile double d;
@@ -315,7 +315,7 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round,
315315
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
316316
return -1;
317317
}
318-
return _PyTime_FromFloatObject(t, d, round, unit_to_ns);
318+
return _PyTime_FromDouble(t, d, round, unit_to_ns);
319319
}
320320
else {
321321
long long sec;
@@ -779,19 +779,80 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
779779
return pymonotonic(tp, info, 1);
780780
}
781781

782+
783+
#ifdef MS_WINDOWS
782784
int
783-
_PyTime_Init(void)
785+
_PyTime_GetWinPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
786+
{
787+
static LONGLONG cpu_frequency = 0;
788+
static LONGLONG ctrStart;
789+
LARGE_INTEGER now;
790+
double diff;
791+
792+
if (cpu_frequency == 0) {
793+
LARGE_INTEGER freq;
794+
QueryPerformanceCounter(&now);
795+
ctrStart = now.QuadPart;
796+
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
797+
PyErr_SetFromWindowsErr(0);
798+
return -1;
799+
}
800+
cpu_frequency = freq.QuadPart;
801+
}
802+
QueryPerformanceCounter(&now);
803+
diff = (double)(now.QuadPart - ctrStart);
804+
if (info) {
805+
info->implementation = "QueryPerformanceCounter()";
806+
info->resolution = 1.0 / (double)cpu_frequency;
807+
info->monotonic = 1;
808+
info->adjustable = 0;
809+
}
810+
811+
diff = diff / (double)cpu_frequency;
812+
return _PyTime_FromDouble(t, diff, _PyTime_ROUND_FLOOR, SEC_TO_NS);
813+
}
814+
#endif
815+
816+
817+
int
818+
_PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
819+
{
820+
#ifdef MS_WINDOWS
821+
return _PyTime_GetWinPerfCounterWithInfo(t, info);
822+
#else
823+
return _PyTime_GetMonotonicClockWithInfo(t, info);
824+
#endif
825+
}
826+
827+
828+
_PyTime_t
829+
_PyTime_GetPerfCounter(void)
784830
{
785831
_PyTime_t t;
832+
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
833+
/* should not happen, _PyTime_Init() checked the clock at startup */
834+
Py_UNREACHABLE();
835+
}
836+
return t;
837+
}
786838

787-
/* ensure that the system clock works */
788-
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
789-
return -1;
790839

791-
/* ensure that the operating system provides a monotonic clock */
792-
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
840+
int
841+
_PyTime_Init(void)
842+
{
843+
/* check that the 3 most important clocks are working properly
844+
to not have to check for exceptions at runtime. If a clock works once,
845+
it cannot fail in next calls. */
846+
_PyTime_t t;
847+
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) {
793848
return -1;
794-
849+
}
850+
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) {
851+
return -1;
852+
}
853+
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
854+
return -1;
855+
}
795856
return 0;
796857
}
797858

0 commit comments

Comments
 (0)