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
This commit is contained in:
Victor Stinner 2023-11-02 14:29:05 +01:00 committed by GitHub
parent 6a0d7b43df
commit 4fe22c7377
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 142 additions and 32 deletions

View File

@ -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
Modules/clinic/timemodule.c.h generated Normal file
View File

@ -0,0 +1,74 @@
/*[clinic input]
preserve
[clinic start generated code]*/
#if defined(HAVE_CLOCK_GETTIME)
PyDoc_STRVAR(time_clock_gettime__doc__,
"clock_gettime($module, clk_id, /)\n"
"--\n"
"\n"
"Return the time of the specified clock clk_id as a float.");
#define TIME_CLOCK_GETTIME_METHODDEF \
{"clock_gettime", (PyCFunction)time_clock_gettime, METH_O, time_clock_gettime__doc__},
static PyObject *
time_clock_gettime_impl(PyObject *module, clockid_t clk_id);
static PyObject *
time_clock_gettime(PyObject *module, PyObject *arg)
{
PyObject *return_value = NULL;
clockid_t clk_id;
if (!time_clockid_converter(arg, &clk_id)) {
goto exit;
}
return_value = time_clock_gettime_impl(module, clk_id);
exit:
return return_value;
}
#endif /* defined(HAVE_CLOCK_GETTIME) */
#if defined(HAVE_CLOCK_GETTIME)
PyDoc_STRVAR(time_clock_gettime_ns__doc__,
"clock_gettime_ns($module, clk_id, /)\n"
"--\n"
"\n"
"Return the time of the specified clock clk_id as nanoseconds (int).");
#define TIME_CLOCK_GETTIME_NS_METHODDEF \
{"clock_gettime_ns", (PyCFunction)time_clock_gettime_ns, METH_O, time_clock_gettime_ns__doc__},
static PyObject *
time_clock_gettime_ns_impl(PyObject *module, clockid_t clk_id);
static PyObject *
time_clock_gettime_ns(PyObject *module, PyObject *arg)
{
PyObject *return_value = NULL;
clockid_t clk_id;
if (!time_clockid_converter(arg, &clk_id)) {
goto exit;
}
return_value = time_clock_gettime_ns_impl(module, clk_id);
exit:
return return_value;
}
#endif /* defined(HAVE_CLOCK_GETTIME) */
#ifndef TIME_CLOCK_GETTIME_METHODDEF
#define TIME_CLOCK_GETTIME_METHODDEF
#endif /* !defined(TIME_CLOCK_GETTIME_METHODDEF) */
#ifndef TIME_CLOCK_GETTIME_NS_METHODDEF
#define TIME_CLOCK_GETTIME_NS_METHODDEF
#endif /* !defined(TIME_CLOCK_GETTIME_NS_METHODDEF) */
/*[clinic end generated code: output=b589a2132aa9df47 input=a9049054013a1b77]*/

View File

@ -63,6 +63,12 @@
#define SEC_TO_NS (1000 * 1000 * 1000) #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) #if defined(HAVE_TIMES) || defined(HAVE_CLOCK)
static int static int
check_ticks_per_second(long tps, const char *context) check_ticks_per_second(long tps, const char *context)
@ -227,23 +233,52 @@ _PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#pragma clang diagnostic ignored "-Wunguarded-availability" #pragma clang diagnostic ignored "-Wunguarded-availability"
#endif #endif
static PyObject * static int
time_clock_gettime(PyObject *self, PyObject *args) time_clockid_converter(PyObject *obj, clockid_t *p)
{ {
int ret;
struct timespec tp;
#if defined(_AIX) && (SIZEOF_LONG == 8) #if defined(_AIX) && (SIZEOF_LONG == 8)
long clk_id; long clk_id = PyLong_AsLong(obj);
if (!PyArg_ParseTuple(args, "l:clock_gettime", &clk_id)) {
#else #else
int clk_id; int clk_id = PyLong_AsInt(obj);
if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
#endif #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) { if (ret != 0) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
@ -251,38 +286,32 @@ time_clock_gettime(PyObject *self, PyObject *args)
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
} }
PyDoc_STRVAR(clock_gettime_doc, /*[clinic input]
"clock_gettime(clk_id) -> float\n\ time.clock_gettime_ns
\n\
Return the time of the specified clock clk_id."); clk_id: clockid_t
/
Return the time of the specified clock clk_id as nanoseconds (int).
[clinic start generated code]*/
static PyObject * 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; struct timespec ts;
_PyTime_t t; int ret = clock_gettime(clk_id, &ts);
if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) {
return NULL;
}
ret = clock_gettime((clockid_t)clk_id, &ts);
if (ret != 0) { if (ret != 0) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
return NULL; return NULL;
} }
_PyTime_t t;
if (_PyTime_FromTimespec(&t, &ts) < 0) { if (_PyTime_FromTimespec(&t, &ts) < 0) {
return NULL; return NULL;
} }
return _PyTime_AsNanosecondsObject(t); 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 */ #endif /* HAVE_CLOCK_GETTIME */
#ifdef HAVE_CLOCK_SETTIME #ifdef HAVE_CLOCK_SETTIME
@ -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[] = { static PyMethodDef time_methods[] = {
{"time", time_time, METH_NOARGS, time_doc}, {"time", time_time, METH_NOARGS, time_doc},
{"time_ns", time_time_ns, METH_NOARGS, time_ns_doc}, {"time_ns", time_time_ns, METH_NOARGS, time_ns_doc},
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
{"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc}, TIME_CLOCK_GETTIME_METHODDEF
{"clock_gettime_ns",time_clock_gettime_ns, METH_VARARGS, clock_gettime_ns_doc}, TIME_CLOCK_GETTIME_NS_METHODDEF
#endif #endif
#ifdef HAVE_CLOCK_SETTIME #ifdef HAVE_CLOCK_SETTIME
{"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc}, {"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc},