From 557a060b40742a5ec95f8504d8dd1ed7768da905 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 8 Mar 2025 17:12:46 +0000 Subject: [PATCH 1/7] Implement fast path --- ...5-03-08-17-07-00.gh-issue-88473.qg23g8.rst | 1 + Modules/_datetimemodule.c | 43 +++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst new file mode 100644 index 00000000000000..a44126ed621557 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst @@ -0,0 +1 @@ +Implement a fast path for date types in :func:`date.today()` diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 07d7089be09d20..0a6b5ab3fd4d3e 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3259,21 +3259,36 @@ date_fromtimestamp(PyObject *cls, PyObject *obj) static PyObject * date_today(PyObject *cls, PyObject *Py_UNUSED(dummy)) { - PyObject *time; - PyObject *result; - time = time_time(); - if (time == NULL) - return NULL; + /* Use C implementation to boost performance for date type */ + if ((PyTypeObject *)cls == &PyDateTime_DateType) { + struct tm tm; + time_t t; + time(&t); - /* Note well: today() is a class method, so this may not call - * date.fromtimestamp. For example, it may call - * datetime.fromtimestamp. That's why we need all the accuracy - * time.time() delivers; if someone were gonzo about optimization, - * date.today() could get away with plain C time(). - */ - result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); - Py_DECREF(time); - return result; + if (_PyTime_localtime(t, &tm) != 0) + return NULL; + + return new_date_ex(tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + (PyTypeObject*)cls); + } else { + PyObject *time; + PyObject *result; + time = time_time(); + if (time == NULL) + return NULL; + + /* Note well: today() is a class method, so this may not call + * date.fromtimestamp. For example, it may call + * datetime.fromtimestamp. That's why we need all the accuracy + * time.time() delivers; if someone were gonzo about optimization, + * date.today() could get away with plain C time(). + */ + result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); + Py_DECREF(time); + return result; + } } /*[clinic input] From 0ecf2d46dc1d0735e4694090d0341ed45c3eb02f Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 8 Mar 2025 17:29:10 +0000 Subject: [PATCH 2/7] Fix docs --- .../next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst index a44126ed621557..3c0df6a5220dc6 100644 --- a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst +++ b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst @@ -1 +1 @@ -Implement a fast path for date types in :func:`date.today()` +Implement a fast path for date types in :func:`datetime.date.today()` From 1fa3270c84aff89801425c1564e623a1d8ae343e Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 8 Mar 2025 17:30:32 +0000 Subject: [PATCH 3/7] remove unnecessary parentheses in news --- .../next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst index 3c0df6a5220dc6..dfff6601b76765 100644 --- a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst +++ b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst @@ -1 +1 @@ -Implement a fast path for date types in :func:`datetime.date.today()` +Implement a fast path for date types in :func:`datetime.date.today` From 46d83e397ce1352a57172cb17b0a8bca1cc682ec Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 8 Mar 2025 21:27:32 +0000 Subject: [PATCH 4/7] Benedikts suggestions --- .../2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst | 4 +++- Modules/_datetimemodule.c | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst index dfff6601b76765..f38ec8ef810407 100644 --- a/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst +++ b/Misc/NEWS.d/next/Library/2025-03-08-17-07-00.gh-issue-88473.qg23g8.rst @@ -1 +1,3 @@ -Implement a fast path for date types in :func:`datetime.date.today` +Implement a fast path for :class:`datetime.date` objects in :func:`datetime.date.today` +which results in a 5x performance gain while proper subclasses retain their +previous performance. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0a6b5ab3fd4d3e..67c740f45a484a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3265,25 +3265,26 @@ date_today(PyObject *cls, PyObject *Py_UNUSED(dummy)) time_t t; time(&t); - if (_PyTime_localtime(t, &tm) != 0) + if (_PyTime_localtime(t, &tm) != 0) { return NULL; + } return new_date_ex(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, (PyTypeObject*)cls); - } else { + } + else { PyObject *time; PyObject *result; time = time_time(); - if (time == NULL) + if (time == NULL) { return NULL; + } /* Note well: today() is a class method, so this may not call * date.fromtimestamp. For example, it may call - * datetime.fromtimestamp. That's why we need all the accuracy - * time.time() delivers; if someone were gonzo about optimization, - * date.today() could get away with plain C time(). + * datetime.fromtimestamp. */ result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); Py_DECREF(time); From cf5c7c87e1fea4e9a74845467afd8cf208a73f12 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Wed, 12 Mar 2025 18:26:22 +0000 Subject: [PATCH 5/7] Slimming as per Benedikt --- Modules/_datetimemodule.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 67c740f45a484a..0727871c2b65ac 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3274,22 +3274,17 @@ date_today(PyObject *cls, PyObject *Py_UNUSED(dummy)) tm.tm_mday, (PyTypeObject*)cls); } - else { - PyObject *time; - PyObject *result; - time = time_time(); - if (time == NULL) { - return NULL; - } - - /* Note well: today() is a class method, so this may not call - * date.fromtimestamp. For example, it may call - * datetime.fromtimestamp. - */ - result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); - Py_DECREF(time); - return result; + PyObject *time = time_time(); + if (time == NULL) { + return NULL; } + + /* Note well: since today() is a class method, it may not call + * date.fromtimestamp, e.g., it may call datetime.fromtimestamp. + */ + PyObject *result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); + Py_DECREF(time); + return result; } /*[clinic input] From 372a764ada9a505767e9dd6d5afa0115daff62ad Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Wed, 12 Mar 2025 18:51:47 +0000 Subject: [PATCH 6/7] Benedikt's final nits ;-) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Modules/_datetimemodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0727871c2b65ac..ed49bcd4e1ac49 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3272,8 +3272,9 @@ date_today(PyObject *cls, PyObject *Py_UNUSED(dummy)) return new_date_ex(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - (PyTypeObject*)cls); + (PyTypeObject *)cls); } + PyObject *time = time_time(); if (time == NULL) { return NULL; From 38926d8a84a5141c92636bdb12b14898055c4c86 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 15 Sep 2025 19:48:03 +0100 Subject: [PATCH 7/7] Conflict fixup --- Modules/_datetimemodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 1cb057072fc3a4..897dd06e5b4574 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3292,7 +3292,7 @@ datetime_date_today_impl(PyTypeObject *type) /*[clinic end generated code: output=d5474697df6b251c input=21688afa289c0a06]*/ { /* Use C implementation to boost performance for date type */ - if ((PyTypeObject *)cls == &PyDateTime_DateType) { + if (type == &PyDateTime_DateType) { struct tm tm; time_t t; time(&t); @@ -3304,7 +3304,7 @@ datetime_date_today_impl(PyTypeObject *type) return new_date_ex(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - (PyTypeObject *)cls); + type); } PyObject *time = time_time(); @@ -3315,7 +3315,7 @@ datetime_date_today_impl(PyTypeObject *type) /* Note well: since today() is a class method, it may not call * date.fromtimestamp, e.g., it may call datetime.fromtimestamp. */ - PyObject *result = PyObject_CallMethodOneArg(cls, &_Py_ID(fromtimestamp), time); + PyObject *result = PyObject_CallMethodOneArg((PyObject*)type, &_Py_ID(fromtimestamp), time); Py_DECREF(time); return result; }