]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111482: Use Argument Clinic for clock_gettime() (#111641)
authorVictor Stinner <vstinner@python.org>
Thu, 2 Nov 2023 13:29:05 +0000 (14:29 +0100)
committerGitHub <noreply@github.com>
Thu, 2 Nov 2023 13:29:05 +0000 (14:29 +0100)
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

Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst [new file with mode: 0644]
Modules/clinic/timemodule.c.h [new file with mode: 0644]
Modules/timemodule.c

diff --git a/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst b/Misc/NEWS.d/next/Library/2023-11-02-12-15-46.gh-issue-111482.FWqZIU.rst
new file mode 100644 (file)
index 0000000..d73e45c
--- /dev/null
@@ -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.
diff --git a/Modules/clinic/timemodule.c.h b/Modules/clinic/timemodule.c.h
new file mode 100644 (file)
index 0000000..bbc0748
--- /dev/null
@@ -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]*/
index bf48c89f343948c22562d30900ad885d98912332..e82f6eb98ebaf357c3c8ac2917e0169b891d8bdf 100644 (file)
 #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)
@@ -227,23 +233,52 @@ _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;
@@ -251,38 +286,32 @@ time_clock_gettime(PyObject *self, PyObject *args)
     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
@@ -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},