iul*_*ian 25 python dictionary python-2.7 python-3.x python-internals
我遇到过一种(非常不寻常的)情况,我不得不使用一个map()
或一个列表推导表达式.然后我想知道哪一个更快.
这个 StackOverflow答案为我提供了解决方案,但后来我开始自己测试.基本上结果是一样的,但是我在切换到Python 3时发现了一个意外的行为,我很好奇,即:
? iulian-pc ~ ? python --version
Python 2.7.6
? iulian-pc ~ ? python3 --version
Python 3.4.3
? iulian-pc ~ ? python -mtimeit '{}'
10000000 loops, best of 3: 0.0306 usec per loop
? iulian-pc ~ ? python3 -mtimeit '{}'
10000000 loops, best of 3: 0.105 usec per loop
? iulian-pc ~ ? python -mtimeit 'dict()'
10000000 loops, best of 3: 0.103 usec per loop
? iulian-pc ~ ? python3 -mtimeit 'dict()'
10000000 loops, best of 3: 0.165 usec per loop
Run Code Online (Sandbox Code Playgroud)
我的假设是Python 3的是比Python 2快,但它在几个帖子横空出世(1,2),它的情况并非如此.然后我想也许Python 3.5在这么简单的任务中表现得更好,因为他们在下面说明README
:
语言大致相同,但许多细节,特别是字典和字符串等内置对象的工作方式发生了很大变化,并且最终删除了许多已弃用的功能.
但不,它表现得更糟:
? iulian-pc ~ ? python3 --version
Python 3.5.0
? iulian-pc ~ ? python3 -mtimeit '{}'
10000000 loops, best of 3: 0.144 usec per loop
? iulian-pc ~ ? python3 -mtimeit 'dict()'
1000000 loops, best of 3: 0.217 usec per loop
Run Code Online (Sandbox Code Playgroud)
我试图深入研究Python 3.5的源代码dict
,但是我对C语言的了解并不足以自己找到答案(或者,我甚至不会在正确的地方搜索).
是什么让较新版本的Python与较旧版本的Python相比,在一个相对简单的任务(如dict
定义)上更慢,因为通常意义上它反之亦然?我知道这些差异非常小,在大多数情况下它们可以忽略不计.这只是一个观察让我好奇为什么时间增加而至少不是一样的?
Kev*_*vin 31
因为没有人关心
您引用的差异大约为几十或几百纳秒.C编译器优化寄存器使用的方式略有不同,很容易引起这种变化(与任何其他C级优化差异一样).反过来,这可能是由许多事情引起的,例如Python的C实现(CPython)中局部变量的数量和使用的变化,甚至只是切换C编译器.
事实是,没有人积极优化这些微小的差异,所以没有人能够给你一个具体的答案.CPython的设计并不是绝对意义上的快速.它旨在可扩展.因此,例如,您可以将数百或数千个项目推送到字典中,并且它将继续表现良好.但是创建字典的绝对速度并不是Python实现者的主要关注点,至少当差异很小时.
Mos*_*oye 21
正如@Kevin已经说过:
CPython的设计并不是绝对意义上的快速.它旨在可扩展
试试这个:
$ python -mtimeit "dict([(2,3)]*10000000)"
10 loops, best of 3: 512 msec per loop
$
$ python3 -mtimeit "dict([(2,3)]*10000000)"
10 loops, best of 3: 502 msec per loop
Run Code Online (Sandbox Code Playgroud)
然后再次:
$ python -mtimeit "dict([(2,3)]*100000000)"
10 loops, best of 3: 5.19 sec per loop
$
$ python3 -mtimeit "dict([(2,3)]*100000000)"
10 loops, best of 3: 5.07 sec per loop
Run Code Online (Sandbox Code Playgroud)
这很漂亮地表明,你不能将Python3作为对这些微不足道的差异而对Python2的损失.从外观上看,Python3应该更好地扩展.
Tom*_*Rup 18
让我们拆解 {}
:
>>> from dis import dis
>>> dis(lambda: {})
1 0 BUILD_MAP 0
3 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
TARGET(BUILD_MAP)
{
x = _PyDict_NewPresized((Py_ssize_t)oparg);
PUSH(x);
if (x != NULL) DISPATCH();
break;
}
Run Code Online (Sandbox Code Playgroud)
TARGET(BUILD_MAP) {
int i;
PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
if (map == NULL)
goto error;
for (i = oparg; i > 0; i--) {
int err;
PyObject *key = PEEK(2*i);
PyObject *value = PEEK(2*i - 1);
err = PyDict_SetItem(map, key, value);
if (err != 0) {
Py_DECREF(map);
goto error;
}
}
while (oparg--) {
Py_DECREF(POP());
Py_DECREF(POP());
}
PUSH(map);
DISPATCH();
}
Run Code Online (Sandbox Code Playgroud)
这是更多的代码.
Python 3.4实现的BUILD_MAP id与2.7完全相同(感谢@ user2357112).我深入挖掘它看起来像Python 3分钟大小的dict是8 PyDict_MINSIZE_COMBINED const
PyDict_MINSIZE_COMBINED是任何新的非拆分字典的起始大小.8允许dicts不超过5个活动条目; 实验表明,这足以满足大多数dicts(主要包括为传递关键字参数而创建的通常小的dicts).使这8而不是4减少了大多数字典的调整大小,没有任何显着的额外内存使用.
在Python 3.4中查看_PyDict_NewPresized
PyObject *
_PyDict_NewPresized(Py_ssize_t minused)
{
Py_ssize_t newsize;
PyDictKeysObject *new_keys;
for (newsize = PyDict_MINSIZE_COMBINED;
newsize <= minused && newsize > 0;
newsize <<= 1)
;
new_keys = new_keys_object(newsize);
if (new_keys == NULL)
return NULL;
return new_dict(new_keys, NULL);
}
Run Code Online (Sandbox Code Playgroud)
在2.7
PyObject *
_PyDict_NewPresized(Py_ssize_t minused)
{
PyObject *op = PyDict_New();
if (minused>5 && op != NULL && dictresize((PyDictObject *)op, minused) == -1) {
Py_DECREF(op);
return NULL;
}
return op;
}
Run Code Online (Sandbox Code Playgroud)
在这两种情况下minused
都有价值1.
Python 2.7创建一个空字典,Python 3.4创建一个7元素字典.
归档时间: |
|
查看次数: |
1801 次 |
最近记录: |