为什么Python中`datetime.date.today`比`datetime.datetime.now`慢?

5

出于好奇,我对这两个函数进行了以下基准测试:

In [12]: %timeit datetime.datetime.now()
100000 loops, best of 3: 5.09 µs per loop

In [13]: %timeit datetime.date.today()
100000 loops, best of 3: 6.4 µs per loop

我认为date对象包含的信息较少,因此应该更快,但实际上它却更慢。

可能的原因是什么?


2
也许可以看一下nowtoday的源代码。 - user1907906
有趣的是,utcnow 比 now 和 today 还要快... - user3012759
datetime模块是编译成C的,所以我不能轻易地检查它。不看代码,我会怀疑“today”可能使用“now”,然后解析出日期。同样调用硬件获取日期和时间,然后进行一些处理以得出正确的结果。 - Joop
2个回答

1

今天我被这个问题折磨了,所以我来给你展示一下我找到的内容——准备好了。

首先,date.today()的实现必须经过成员函数调用——这个查找似乎是慢的部分:

https://github.com/python/cpython/blob/b2bf2bc1ece673d387341e06c8d3c2bc6e259747/Modules/_datetimemodule.c#L2886-L2892

在此摘录:

static PyObject *
date_today(PyObject *cls, PyObject *dummy)
{
    PyObject *time;
    PyObject *result;
    _Py_IDENTIFIER(fromtimestamp);

    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_CallMethodIdOneArg(cls, &PyId_fromtimestamp, time);
    Py_DECREF(time);
    return result;
}

值得注意的是,它总是通过缓慢的路径。

所以我想,为什么不给它一个快速路径呢?

$ 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

缩放缩放

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

-2

我认为datetime.datetime.today()datetime.datetime.now()的一部分或者调用了它。但我不确定。从“now”中提取“today”需要额外的时间。这也取决于硬件和进程被调用的时间。你有没有进行多次读取并取平均值,还是延迟始终相同?


不,至少对于该模块的Python实现,它不会调用“now”。 - satoru
有趣的是,现在似乎可以做到今天所做的一切,而且还可以在此基础上进行更多(Python 方面的)。 - user3012759

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接