为什么`datetime.date.today`比`datetime.datetime.now`在Python中慢?
出于好奇,我对这两个函数做了一个性能测试:
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 个回答
-2
我觉得 datetime.datetime.today()
可能是 datetime.datetime.now()
的一部分,或者是调用了它。不过我不太确定。从“现在”中提取“今天”需要额外的时间。而且这也和硬件以及调用这个过程的时间有关。你是多次测量然后取个平均值,还是每次的延迟都差不多呢?
1
今天我被这个问题吸引住了,所以我想和大家分享我发现的东西——准备好吧。
首先,date.today()
的实现需要通过一个成员函数来调用——这个查找过程似乎是比较慢的部分:
这里摘录了一部分:
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