Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

date.today() is half as fast as datetime.now().date() #88473

Open
asottile mannequin opened this issue Jun 4, 2021 · 5 comments
Open

date.today() is half as fast as datetime.now().date() #88473

asottile mannequin opened this issue Jun 4, 2021 · 5 comments
Labels
extension-modules C modules in the Modules dir performance Performance or resource usage type-feature A feature request or enhancement

Comments

@asottile
Copy link
Mannequin

asottile mannequin commented Jun 4, 2021

BPO 44307
Nosy @asottile, @pganssle, @corona10, @rkm

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2021-06-04.01:50:33.984>
labels = ['extension-modules', '3.11', 'performance']
title = 'date.today() is half as fast as datetime.now().date()'
updated_at = <Date 2021-06-04.21:22:03.867>
user = 'https://github.com/asottile'

bugs.python.org fields:

activity = <Date 2021-06-04.21:22:03.867>
actor = 'Anthony Sottile'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Extension Modules']
creation = <Date 2021-06-04.01:50:33.984>
creator = 'Anthony Sottile'
dependencies = []
files = []
hgrepos = []
issue_num = 44307
keywords = []
message_count = 4.0
messages = ['395061', '395116', '395123', '395124']
nosy_count = 4.0
nosy_names = ['Anthony Sottile', 'p-ganssle', 'corona10', 'rkm']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'performance'
url = 'https://bugs.python.org/issue44307'
versions = ['Python 3.11']

Linked PRs

@asottile
Copy link
Mannequin Author

asottile mannequin commented Jun 4, 2021

$ python3.10 -m timeit -s 'from datetime import datetime' 'datetime.now().date()'
500000 loops, best of 5: 708 nsec per loop
$ python3.10 -m timeit -s 'from datetime import date' 'date.today()'
200000 loops, best of 5: 1.4 usec per loop

this surprised me so I dug into it -- it appears a fast path can be added to date.today() to make it faster than datetime.date.now() -- though I'm rather unfamiliar with the functions involved here

here is my ~sloppy patch attempting to add a fast path, I would need some guidance to improve it and get it accepted:

$ git diff -w
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 8ef2dad37a..7eaa5d1740 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -2875,6 +2875,17 @@ date_fromtimestamp(PyObject *cls, PyObject *obj)
 static PyObject *
 date_today(PyObject *cls, PyObject *dummy)
 {
+    /* fast path, don't call fromtimestamp */
+    if ((PyTypeObject *)cls == &PyDateTime_DateType) {
+        struct tm tm;
+        time_t t;
+        time(&t);
+        localtime_r(&t, &tm);
+        return new_date_ex(tm.tm_year + 1900,
+                           tm.tm_mon + 1,
+                           tm.tm_mday,
+                           (PyTypeObject*)cls);
+    } else {
         PyObject *time;
         PyObject *result;
         _Py_IDENTIFIER(fromtimestamp);
@@ -2893,6 +2904,7 @@ date_today(PyObject *cls, PyObject *dummy)
         Py_DECREF(time);
         return result;
     }
+}
 
 /*[clinic input]
 @classmethod

after this, date.today() is faster!

$ ./python -m timeit -s 'from datetime import datetime' 'datetime.now().date()'
500000 loops, best of 5: 764 nsec per loop
$ ./python -m timeit -s 'from datetime import date' 'date.today()'
500000 loops, best of 5: 407 nsec per loop

\o/

@asottile asottile mannequin added 3.11 only security fixes extension-modules C modules in the Modules dir performance Performance or resource usage labels Jun 4, 2021
@pganssle
Copy link
Member

pganssle commented Jun 4, 2021

Yeah, I knew this was slower and it's been on my long list to look at it (tied to this is the fact that datetime.today() is basically just a slow version of datetime.now(), in defiance of user expectations).

My inclination is that we shouldn't re-implement fromtimestamp in date.today, but rather call date_fromtimestamp in the fast path. I believe that incurs the overhead of creating one additional Python object (an integer), but if it's a sufficiently significant speedup, we could possibly refactor date_fromtimestamp to a version that accepts a C integer and a version that accepts a Python integer, then call the version accepting a C integer.

I think this won't give any speedup to datetime.today, since datetime.today will still take the slow path. If we care about this, we may be able to implement datetime.today as an alias for datetime.now(None), assuming there are no behavioral differences between the two.

@terryjreedy terryjreedy changed the title date.today() is 2x slower than datetime.now().date() date.today() is half as fast as datetime.now().date() Jun 4, 2021
@terryjreedy terryjreedy changed the title date.today() is 2x slower than datetime.now().date() date.today() is half as fast as datetime.now().date() Jun 4, 2021
@asottile
Copy link
Mannequin Author

asottile mannequin commented Jun 4, 2021

@terry.reddy -- I believe your title change makes this more difficult to understand

@asottile
Copy link
Mannequin Author

asottile mannequin commented Jun 4, 2021

*terry.reedy oops typo!

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@iritkatriel iritkatriel added 3.12 bugs and security fixes and removed 3.11 only security fixes labels Sep 7, 2022
@evanstjabadi
Copy link

I think this won't give any speedup to datetime.today, since datetime.today will still take the slow path. If we care about this, we may be able to implement datetime.today as an alias for datetime.now(None), assuming there are no behavioral differences between the two.

@pganssle, @iritkatriel - Is it possible that we can review the datetime.today() method if we re-implement it?
In my view, if datetime.now() returns a datetime object representing the current date and time and date.today() returns just a date object representing today's date, then datetime.today() should return a datetime object representing only today's date and should not prescribe any timeness to it.

date.today()
# datetime.date(2022, 12, 17)

# Then datetime.today() should be:
datetime.today()
# datetime.datetime(2022, 12, 17, 0, 0, 0, 0)

I understand that this is a widely used method and may cause lots of disruption. But maybe we can add a legacy flag?

# Legacy:
datetime.today(legacy=True) # true by default.
# datetime.datetime(2022, 12, 17, 9, 37, 30, 653381)

# Non-legacy:
datetime.today(legacy=False)
# datetime.datetime(2022, 12, 17, 0, 0, 0, 0)

@picnixz picnixz added type-feature A feature request or enhancement and removed 3.12 bugs and security fixes labels Mar 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extension-modules C modules in the Modules dir performance Performance or resource usage type-feature A feature request or enhancement
Projects
Development

No branches or pull requests

4 participants