Skip to content

Commit 4fe22c7

Browse files
authored
gh-111482: Use Argument Clinic for clock_gettime() (#111641)
Use Argument Clinic for time.clock_gettime() and time.clock_gettime_ns() functions. Benchmark on time.clock_gettime_ns(): import time import pyperf runner = pyperf.Runner() runner.timeit( 'clock_gettime_ns(CLOCK_MONOTONIC_COARSE)', setup='import time; clock_gettime_ns=time.clock_gettime_ns; CLOCK_MONOTONIC_COARSE=6', stmt='clock_gettime_ns(CLOCK_MONOTONIC_COARSE)') Result on Linux with CPU isolation: Mean +- std dev: [ref] 134 ns +- 1 ns -> [change] 55.7 ns +- 1.4 ns: 2.41x faster
1 parent 6a0d7b4 commit 4fe22c7

File tree

3 files changed

+142
-32
lines changed

3 files changed

+142
-32
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`time`: Make :func:`time.clock_gettime()` and
2+
:func:`time.clock_gettime_ns()` functions up to 2x faster by faster calling
3+
convention. Patch by Victor Stinner.

Modules/clinic/timemodule.c.h

Lines changed: 74 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/timemodule.c

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@
6363
#define SEC_TO_NS (1000 * 1000 * 1000)
6464

6565

66+
/*[clinic input]
67+
module time
68+
[clinic start generated code]*/
69+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a668a08771581f36]*/
70+
71+
6672
#if defined(HAVE_TIMES) || defined(HAVE_CLOCK)
6773
static int
6874
check_ticks_per_second(long tps, const char *context)
@@ -227,62 +233,85 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
227233
#pragma clang diagnostic ignored "-Wunguarded-availability"
228234
#endif
229235

230-
static PyObject *
231-
time_clock_gettime(PyObject *self, PyObject *args)
236+
static int
237+
time_clockid_converter(PyObject *obj, clockid_t *p)
232238
{
233-
int ret;
234-
struct timespec tp;
235-
236239
#if defined(_AIX) && (SIZEOF_LONG == 8)
237-
long clk_id;
238-
if (!PyArg_ParseTuple(args, "l:clock_gettime", &clk_id)) {
240+
long clk_id = PyLong_AsLong(obj);
239241
#else
240-
int clk_id;
241-
if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
242+
int clk_id = PyLong_AsInt(obj);
242243
#endif
243-
return NULL;
244+
if (clk_id == -1 && PyErr_Occurred()) {
245+
PyErr_Format(PyExc_TypeError,
246+
"clk_id should be integer, not %s",
247+
_PyType_Name(Py_TYPE(obj)));
248+
return 0;
244249
}
245250

246-
ret = clock_gettime((clockid_t)clk_id, &tp);
251+
// Make sure that we picked the right type (check sizes type)
252+
Py_BUILD_ASSERT(sizeof(clk_id) == sizeof(*p));
253+
*p = (clockid_t)clk_id;
254+
return 1;
255+
}
256+
257+
/*[python input]
258+
259+
class clockid_t_converter(CConverter):
260+
type = "clockid_t"
261+
converter = 'time_clockid_converter'
262+
263+
[python start generated code]*/
264+
/*[python end generated code: output=da39a3ee5e6b4b0d input=53867111501f46c8]*/
265+
266+
267+
/*[clinic input]
268+
time.clock_gettime
269+
270+
clk_id: clockid_t
271+
/
272+
273+
Return the time of the specified clock clk_id as a float.
274+
[clinic start generated code]*/
275+
276+
static PyObject *
277+
time_clock_gettime_impl(PyObject *module, clockid_t clk_id)
278+
/*[clinic end generated code: output=832b9ebc03328020 input=7e89fcc42ca15e5d]*/
279+
{
280+
struct timespec tp;
281+
int ret = clock_gettime(clk_id, &tp);
247282
if (ret != 0) {
248283
PyErr_SetFromErrno(PyExc_OSError);
249284
return NULL;
250285
}
251286
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
252287
}
253288

254-
PyDoc_STRVAR(clock_gettime_doc,
255-
"clock_gettime(clk_id) -> float\n\
256-
\n\
257-
Return the time of the specified clock clk_id.");
289+
/*[clinic input]
290+
time.clock_gettime_ns
291+
292+
clk_id: clockid_t
293+
/
294+
295+
Return the time of the specified clock clk_id as nanoseconds (int).
296+
[clinic start generated code]*/
258297

259298
static PyObject *
260-
time_clock_gettime_ns(PyObject *self, PyObject *args)
299+
time_clock_gettime_ns_impl(PyObject *module, clockid_t clk_id)
300+
/*[clinic end generated code: output=4a045c3a36e60044 input=aabc248db8c8e3e5]*/
261301
{
262-
int ret;
263-
int clk_id;
264302
struct timespec ts;
265-
_PyTime_t t;
266-
267-
if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
268-
return NULL;
269-
}
270-
271-
ret = clock_gettime((clockid_t)clk_id, &ts);
303+
int ret = clock_gettime(clk_id, &ts);
272304
if (ret != 0) {
273305
PyErr_SetFromErrno(PyExc_OSError);
274306
return NULL;
275307
}
308+
309+
_PyTime_t t;
276310
if (_PyTime_FromTimespec(&t, &ts) < 0) {
277311
return NULL;
278312
}
279313
return _PyTime_AsNanosecondsObject(t);
280314
}
281-
282-
PyDoc_STRVAR(clock_gettime_ns_doc,
283-
"clock_gettime_ns(clk_id) -> int\n\
284-
\n\
285-
Return the time of the specified clock clk_id as nanoseconds.");
286315
#endif /* HAVE_CLOCK_GETTIME */
287316

288317
#ifdef HAVE_CLOCK_SETTIME
@@ -1857,12 +1886,16 @@ init_timezone(PyObject *m)
18571886
}
18581887

18591888

1889+
// Include Argument Clinic code after defining converters such as
1890+
// time_clockid_converter().
1891+
#include "clinic/timemodule.c.h"
1892+
18601893
static PyMethodDef time_methods[] = {
18611894
{"time", time_time, METH_NOARGS, time_doc},
18621895
{"time_ns", time_time_ns, METH_NOARGS, time_ns_doc},
18631896
#ifdef HAVE_CLOCK_GETTIME
1864-
{"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc},
1865-
{"clock_gettime_ns",time_clock_gettime_ns, METH_VARARGS, clock_gettime_ns_doc},
1897+
TIME_CLOCK_GETTIME_METHODDEF
1898+
TIME_CLOCK_GETTIME_NS_METHODDEF
18661899
#endif
18671900
#ifdef HAVE_CLOCK_SETTIME
18681901
{"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc},

0 commit comments

Comments
 (0)