Skip to content

Commit

Permalink
gh-111482: Use Argument Clinic for clock_gettime() (#111641)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vstinner authored Nov 2, 2023
1 parent 6a0d7b4 commit 4fe22c7
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`time`: Make :func:`time.clock_gettime()` and
:func:`time.clock_gettime_ns()` functions up to 2x faster by faster calling
convention. Patch by Victor Stinner.
74 changes: 74 additions & 0 deletions Modules/clinic/timemodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

97 changes: 65 additions & 32 deletions Modules/timemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@
#define SEC_TO_NS (1000 * 1000 * 1000)


/*[clinic input]
module time
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a668a08771581f36]*/


#if defined(HAVE_TIMES) || defined(HAVE_CLOCK)
static int
check_ticks_per_second(long tps, const char *context)
Expand Down Expand Up @@ -227,62 +233,85 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#pragma clang diagnostic ignored "-Wunguarded-availability"
#endif

static PyObject *
time_clock_gettime(PyObject *self, PyObject *args)
static int
time_clockid_converter(PyObject *obj, clockid_t *p)
{
int ret;
struct timespec tp;

#if defined(_AIX) && (SIZEOF_LONG == 8)
long clk_id;
if (!PyArg_ParseTuple(args, "l:clock_gettime", &clk_id)) {
long clk_id = PyLong_AsLong(obj);
#else
int clk_id;
if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
int clk_id = PyLong_AsInt(obj);
#endif
return NULL;
if (clk_id == -1 && PyErr_Occurred()) {
PyErr_Format(PyExc_TypeError,
"clk_id should be integer, not %s",
_PyType_Name(Py_TYPE(obj)));
return 0;
}

ret = clock_gettime((clockid_t)clk_id, &tp);
// Make sure that we picked the right type (check sizes type)
Py_BUILD_ASSERT(sizeof(clk_id) == sizeof(*p));
*p = (clockid_t)clk_id;
return 1;
}

/*[python input]
class clockid_t_converter(CConverter):
type = "clockid_t"
converter = 'time_clockid_converter'
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=53867111501f46c8]*/


/*[clinic input]
time.clock_gettime
clk_id: clockid_t
/
Return the time of the specified clock clk_id as a float.
[clinic start generated code]*/

static PyObject *
time_clock_gettime_impl(PyObject *module, clockid_t clk_id)
/*[clinic end generated code: output=832b9ebc03328020 input=7e89fcc42ca15e5d]*/
{
struct timespec tp;
int ret = clock_gettime(clk_id, &tp);
if (ret != 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
}

PyDoc_STRVAR(clock_gettime_doc,
"clock_gettime(clk_id) -> float\n\
\n\
Return the time of the specified clock clk_id.");
/*[clinic input]
time.clock_gettime_ns
clk_id: clockid_t
/
Return the time of the specified clock clk_id as nanoseconds (int).
[clinic start generated code]*/

static PyObject *
time_clock_gettime_ns(PyObject *self, PyObject *args)
time_clock_gettime_ns_impl(PyObject *module, clockid_t clk_id)
/*[clinic end generated code: output=4a045c3a36e60044 input=aabc248db8c8e3e5]*/
{
int ret;
int clk_id;
struct timespec ts;
_PyTime_t t;

if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
return NULL;
}

ret = clock_gettime((clockid_t)clk_id, &ts);
int ret = clock_gettime(clk_id, &ts);
if (ret != 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}

_PyTime_t t;
if (_PyTime_FromTimespec(&t, &ts) < 0) {
return NULL;
}
return _PyTime_AsNanosecondsObject(t);
}

PyDoc_STRVAR(clock_gettime_ns_doc,
"clock_gettime_ns(clk_id) -> int\n\
\n\
Return the time of the specified clock clk_id as nanoseconds.");
#endif /* HAVE_CLOCK_GETTIME */

#ifdef HAVE_CLOCK_SETTIME
Expand Down Expand Up @@ -1857,12 +1886,16 @@ init_timezone(PyObject *m)
}


// Include Argument Clinic code after defining converters such as
// time_clockid_converter().
#include "clinic/timemodule.c.h"

static PyMethodDef time_methods[] = {
{"time", time_time, METH_NOARGS, time_doc},
{"time_ns", time_time_ns, METH_NOARGS, time_ns_doc},
#ifdef HAVE_CLOCK_GETTIME
{"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc},
{"clock_gettime_ns",time_clock_gettime_ns, METH_VARARGS, clock_gettime_ns_doc},
TIME_CLOCK_GETTIME_METHODDEF
TIME_CLOCK_GETTIME_NS_METHODDEF
#endif
#ifdef HAVE_CLOCK_SETTIME
{"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc},
Expand Down

0 comments on commit 4fe22c7

Please sign in to comment.