Skip to content
Comment thread
maurycy marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Speed up :meth:`~datetime.date.timetuple` and :meth:`~datetime.date.strftime`
by caching the ``time.struct_time`` and ``time.strftime`` lookups, and
:meth:`~datetime.datetime.today` by reading the clock directly instead of
calling :func:`time.time`. Patching :mod:`time` after importing
:mod:`datetime` no longer affects these methods. Patch by Maurycy
Pawłowski-Wieroński.
60 changes: 35 additions & 25 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ typedef struct {

/* The interned Unix epoch datetime instance */
PyObject *epoch;

PyObject *time_struct_time;
PyObject *time_strftime;
} datetime_state;

/* The module has a fixed number of static objects, due to being exposed
Expand Down Expand Up @@ -1879,10 +1882,12 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
assert(object && format && timetuple);
assert(PyUnicode_Check(format));

PyObject *strftime = PyImport_ImportModuleAttrString("time", "strftime");
if (strftime == NULL) {
PyObject *current_mod = NULL;
datetime_state *st = GET_CURRENT_STATE(current_mod);
if (st == NULL) {
return NULL;
}
PyObject *strftime = st->time_strftime;

/* Scan the input format, looking for %z/%Z/%f escapes, building
* a new format. Since computing the replacements for those codes
Expand Down Expand Up @@ -2042,7 +2047,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
Py_XDECREF(zreplacement);
Py_XDECREF(colonzreplacement);
Py_XDECREF(Zreplacement);
Py_XDECREF(strftime);
RELEASE_CURRENT_STATE(st, current_mod);
return result;

Error:
Expand All @@ -2055,41 +2060,26 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
* from C. Perhaps they should be.
*/

/* Call time.time() and return its result (a Python float). */
static PyObject *
time_time(void)
{
PyObject *result = NULL;
PyObject *time = PyImport_ImportModuleAttrString("time", "time");

if (time != NULL) {
result = PyObject_CallNoArgs(time);
Py_DECREF(time);
}
return result;
}

/* Build a time.struct_time. The weekday and day number are automatically
* computed from the y,m,d args.
*/
static PyObject *
build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag)
{
PyObject *struct_time;
PyObject *result;

struct_time = PyImport_ImportModuleAttrString("time", "struct_time");
if (struct_time == NULL) {
PyObject *current_mod = NULL;
datetime_state *st = GET_CURRENT_STATE(current_mod);
if (st == NULL) {
return NULL;
}

result = PyObject_CallFunction(struct_time, "((iiiiiiiii))",
PyObject *result = PyObject_CallFunction(st->time_struct_time,
"((iiiiiiiii))",
y, m, d,
hh, mm, ss,
weekday(y, m, d),
days_before_month(y, m) + d,
dstflag);
Py_DECREF(struct_time);
RELEASE_CURRENT_STATE(st, current_mod);
return result;
}

Expand Down Expand Up @@ -3311,7 +3301,11 @@ datetime_date_today_impl(PyTypeObject *type)
type);
}

PyObject *time = time_time();
PyTime_t ts;
if (PyTime_Time(&ts) < 0) {
return NULL;
}
PyObject *time = PyFloat_FromDouble(PyTime_AsSecondsDouble(ts));
if (time == NULL) {
return NULL;
}
Comment thread
maurycy marked this conversation as resolved.
Comment thread
maurycy marked this conversation as resolved.
Expand Down Expand Up @@ -7414,6 +7408,8 @@ init_state(datetime_state *st, PyObject *module, PyObject *old_module)
.us_per_week = Py_NewRef(st_old->us_per_week),
.seconds_per_day = Py_NewRef(st_old->seconds_per_day),
.epoch = Py_NewRef(st_old->epoch),
.time_struct_time = Py_NewRef(st_old->time_struct_time),
.time_strftime = Py_NewRef(st_old->time_strftime),
};
return 0;
}
Expand Down Expand Up @@ -7458,6 +7454,15 @@ init_state(datetime_state *st, PyObject *module, PyObject *old_module)
return -1;
}

st->time_struct_time = PyImport_ImportModuleAttrString("time", "struct_time");
if (st->time_struct_time == NULL) {
return -1;
}
st->time_strftime = PyImport_ImportModuleAttrString("time", "strftime");
if (st->time_strftime == NULL) {
return -1;
}

return 0;
}

Expand All @@ -7467,6 +7472,9 @@ traverse_state(datetime_state *st, visitproc visit, void *arg)
/* heap types */
Py_VISIT(st->isocalendar_date_type);

Py_VISIT(st->time_struct_time);
Py_VISIT(st->time_strftime);

return 0;
}

Expand All @@ -7482,6 +7490,8 @@ clear_state(datetime_state *st)
Py_CLEAR(st->us_per_week);
Py_CLEAR(st->seconds_per_day);
Py_CLEAR(st->epoch);
Py_CLEAR(st->time_struct_time);
Py_CLEAR(st->time_strftime);
return 0;
}

Expand Down
Loading