Why is dict definition faster in Python 2.7 than in Python 3.x? -


i have encountered (not unusual) situation in had either use map() or list comprehension expression. , wondered 1 faster.

this stackoverflow answer provided me solution, started test myself. results same, found unexpected behavior when switching python 3 got curious about, , namely:

λ 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 

i had assumption python 3 faster python 2, turned out in several posts (1, 2) it's not case. thought maybe python 3.5 perform better @ such simple task, state in readme:

the language same, many details, how built-in objects dictionaries , strings work, have changed considerably, , lot of deprecated features have been removed.

but nope, performed worse:

λ 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 

i've tried dive python 3.5 source code dict, knowledge of c language not sufficient find answer myself (or, maybe don't search in right place).

so, question is:

what makes newer version of python slower comparing older version of python on relatively simple task such dict definition, common sense should vice-versa? i'm aware of fact these differences small in cases can neglected. observation made me curious why time increased , not remained same @ least?

let's disassemble {}:

>>> dis import dis >>> dis(lambda: {})   1           0 build_map                0               3 return_value 

python 2.7 implementation of build_map

target(build_map) {     x = _pydict_newpresized((py_ssize_t)oparg);     push(x);     if (x != null) dispatch();     break; } 

python 3.5 implementation of build_map

target(build_map) {     int i;     pyobject *map = _pydict_newpresized((py_ssize_t)oparg);     if (map == null)         goto error;     (i = oparg; > 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(); } 

it's little bit more code.

edit:

python 3.4 implementation of build_map id same 2.7 (thanks @user2357112). dig deeper , it's looks python 3 min size of dict 8 pydict_minsize_combined const

pydict_minsize_combined starting size new, non-split dict. 8 allows dicts no more 5 active entries; experiments suggested suffices majority of dicts (consisting of usually-small dicts created pass keyword arguments). making 8, rather 4 reduces number of resizes dictionaries, without significant memory use.

look @ _pydict_newpresized in python 3.4

pyobject * _pydict_newpresized(py_ssize_t minused) {     py_ssize_t newsize;     pydictkeysobject *new_keys;     (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); } 

and in 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; } 

in both cases minused has value 1.

python 2.7 create empty dict , python 3.4 create 7-element dict.


Comments

Popular posts from this blog

ios - RestKit 0.20 — CoreData: error: Failed to call designated initializer on NSManagedObject class (again) -

laravel - PDOException in Connector.php line 55: SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) -

java - Digest auth with Spring Security using javaconfig -