From 557a060b40742a5ec95f8504d8dd1ed7768da905 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 8 Mar 2025 17:12:46 +0000 Subject: [PATCH 1/4] 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/4] 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/4] 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/4] 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);