Skip to content

Commit 536a35b

Browse files
authored
bpo-36575: lsprof: Use _PyTime_GetPerfCounter() (GH-8378)
1 parent b3c92c6 commit 536a35b

File tree

2 files changed

+40
-86
lines changed

2 files changed

+40
-86
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The ``_lsprof`` module now uses internal timer same to ``time.perf_counter()`` by default.
2+
``gettimeofday(2)`` was used on Unix. New timer has better resolution on most Unix
3+
platforms and timings are no longer impacted by system clock updates since ``perf_counter()``
4+
is monotonic. Patch by Inada Naoki.

Modules/_lsprof.c

Lines changed: 36 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,6 @@
22
#include "frameobject.h"
33
#include "rotatingtree.h"
44

5-
/*** Selection of a high-precision timer ***/
6-
7-
#ifdef MS_WINDOWS
8-
9-
#include <windows.h>
10-
11-
static long long
12-
hpTimer(void)
13-
{
14-
LARGE_INTEGER li;
15-
QueryPerformanceCounter(&li);
16-
return li.QuadPart;
17-
}
18-
19-
static double
20-
hpTimerUnit(void)
21-
{
22-
LARGE_INTEGER li;
23-
if (QueryPerformanceFrequency(&li))
24-
return 1.0 / li.QuadPart;
25-
else
26-
return 0.000001; /* unlikely */
27-
}
28-
29-
#else /* !MS_WINDOWS */
30-
31-
#ifndef HAVE_GETTIMEOFDAY
32-
#error "This module requires gettimeofday() on non-Windows platforms!"
33-
#endif
34-
35-
#include <sys/resource.h>
36-
#include <sys/times.h>
37-
38-
static long long
39-
hpTimer(void)
40-
{
41-
struct timeval tv;
42-
long long ret;
43-
#ifdef GETTIMEOFDAY_NO_TZ
44-
gettimeofday(&tv);
45-
#else
46-
gettimeofday(&tv, (struct timezone *)NULL);
47-
#endif
48-
ret = tv.tv_sec;
49-
ret = ret * 1000000 + tv.tv_usec;
50-
return ret;
51-
}
52-
53-
static double
54-
hpTimerUnit(void)
55-
{
56-
return 0.000001;
57-
}
58-
59-
#endif /* MS_WINDOWS */
60-
615
/************************************************************/
626
/* Written by Brett Rosen and Ted Czotter */
637

@@ -66,8 +10,8 @@ struct _ProfilerEntry;
6610
/* represents a function called from another function */
6711
typedef struct _ProfilerSubEntry {
6812
rotating_node_t header;
69-
long long tt;
70-
long long it;
13+
_PyTime_t tt;
14+
_PyTime_t it;
7115
long callcount;
7216
long recursivecallcount;
7317
long recursionLevel;
@@ -77,17 +21,17 @@ typedef struct _ProfilerSubEntry {
7721
typedef struct _ProfilerEntry {
7822
rotating_node_t header;
7923
PyObject *userObj; /* PyCodeObject, or a descriptive str for builtins */
80-
long long tt; /* total time in this entry */
81-
long long it; /* inline time in this entry (not in subcalls) */
24+
_PyTime_t tt; /* total time in this entry */
25+
_PyTime_t it; /* inline time in this entry (not in subcalls) */
8226
long callcount; /* how many times this was called */
8327
long recursivecallcount; /* how many times called recursively */
8428
long recursionLevel;
8529
rotating_node_t *calls;
8630
} ProfilerEntry;
8731

8832
typedef struct _ProfilerContext {
89-
long long t0;
90-
long long subt;
33+
_PyTime_t t0;
34+
_PyTime_t subt;
9135
struct _ProfilerContext *previous;
9236
ProfilerEntry *ctxEntry;
9337
} ProfilerContext;
@@ -114,41 +58,46 @@ static PyTypeObject PyProfiler_Type;
11458

11559
/*** External Timers ***/
11660

117-
#define DOUBLE_TIMER_PRECISION 4294967296.0
118-
static PyObject *empty_tuple;
119-
120-
static long long CallExternalTimer(ProfilerObject *pObj)
61+
static _PyTime_t CallExternalTimer(ProfilerObject *pObj)
12162
{
122-
long long result;
123-
PyObject *o = PyObject_Call(pObj->externalTimer, empty_tuple, NULL);
63+
PyObject *o = _PyObject_CallNoArg(pObj->externalTimer);
12464
if (o == NULL) {
12565
PyErr_WriteUnraisable(pObj->externalTimer);
12666
return 0;
12767
}
68+
69+
_PyTime_t result;
70+
int err;
12871
if (pObj->externalTimerUnit > 0.0) {
12972
/* interpret the result as an integer that will be scaled
13073
in profiler_getstats() */
131-
result = PyLong_AsLongLong(o);
74+
err = _PyTime_FromNanosecondsObject(&result, o);
13275
}
13376
else {
13477
/* interpret the result as a double measured in seconds.
135-
As the profiler works with long long internally
78+
As the profiler works with _PyTime_t internally
13679
we convert it to a large integer */
137-
double val = PyFloat_AsDouble(o);
138-
/* error handling delayed to the code below */
139-
result = (long long) (val * DOUBLE_TIMER_PRECISION);
80+
err = _PyTime_FromSecondsObject(&result, o, _PyTime_ROUND_FLOOR);
14081
}
14182
Py_DECREF(o);
142-
if (PyErr_Occurred()) {
83+
if (err < 0) {
14384
PyErr_WriteUnraisable(pObj->externalTimer);
14485
return 0;
14586
}
14687
return result;
14788
}
14889

149-
#define CALL_TIMER(pObj) ((pObj)->externalTimer ? \
150-
CallExternalTimer(pObj) : \
151-
hpTimer())
90+
static inline _PyTime_t
91+
call_timer(ProfilerObject *pObj)
92+
{
93+
if (pObj->externalTimer != NULL) {
94+
return CallExternalTimer(pObj);
95+
}
96+
else {
97+
return _PyTime_GetPerfCounter();
98+
}
99+
}
100+
152101

153102
/*** ProfilerObject ***/
154103

@@ -332,14 +281,14 @@ initContext(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
332281
if (subentry)
333282
++subentry->recursionLevel;
334283
}
335-
self->t0 = CALL_TIMER(pObj);
284+
self->t0 = call_timer(pObj);
336285
}
337286

338287
static void
339288
Stop(ProfilerObject *pObj, ProfilerContext *self, ProfilerEntry *entry)
340289
{
341-
long long tt = CALL_TIMER(pObj) - self->t0;
342-
long long it = tt - self->subt;
290+
_PyTime_t tt = call_timer(pObj) - self->t0;
291+
_PyTime_t it = tt - self->subt;
343292
if (self->previous)
344293
self->previous->subt += tt;
345294
pObj->currentProfilerContext = self->previous;
@@ -631,12 +580,14 @@ profiler_getstats(ProfilerObject *pObj, PyObject* noarg)
631580
statscollector_t collect;
632581
if (pending_exception(pObj))
633582
return NULL;
634-
if (!pObj->externalTimer)
635-
collect.factor = hpTimerUnit();
636-
else if (pObj->externalTimerUnit > 0.0)
583+
if (!pObj->externalTimer || pObj->externalTimerUnit == 0.0) {
584+
_PyTime_t onesec = _PyTime_FromSeconds(1);
585+
collect.factor = (double)1 / onesec;
586+
}
587+
else {
637588
collect.factor = pObj->externalTimerUnit;
638-
else
639-
collect.factor = 1.0 / DOUBLE_TIMER_PRECISION;
589+
}
590+
640591
collect.list = PyList_New(0);
641592
if (collect.list == NULL)
642593
return NULL;
@@ -882,7 +833,6 @@ PyInit__lsprof(void)
882833
(PyObject*) &StatsEntryType);
883834
PyModule_AddObject(module, "profiler_subentry",
884835
(PyObject*) &StatsSubEntryType);
885-
empty_tuple = PyTuple_New(0);
886836
initialized = 1;
887837
return module;
888838
}

0 commit comments

Comments
 (0)