setdefault против производительности defaultdict
Я пишу код для приложения, где важна производительность. Мне интересно почему defaultdict
кажется быстрее setdefault
,
Я хотел бы иметь возможность использовать setdefault
В основном потому, что мне не нравится вывод на печать вложенных defaultdict
(см. реализацию ниже).
В моем коде мне нужно проверить, если element_id
это уже ключ к диктату.
Вот две функции, которые я тестирую:
def defaultdictfunc(subcases,other_ids,element_ids):
dict_name= defaultdict(lambda: defaultdict(lambda: defaultdict(dict)))
for subcase in subcases:
for other_id in other_ids:
for element_id in element_ids:
if element_id in dict_name[subcase][other_id]:
# error duplicate element_id
pass
else:
dict_name[subcase][other_id][element_id]=0
return dict_name
def setdefaultfunc(subcases,other_ids,element_ids):
dict_name={}
for subcase in subcases:
for other_id in other_ids:
for element_id in element_ids:
if element_id in dict_name.setdefault(subcase,{}).setdefault(other_id,{}):
# error duplicate element_id
pass
else:
dict_name[subcase][other_id][element_id]=0
return dict_name
IPython ввода и вывода:
In [1]: from numpy.random import randint
In [2]: subcases,other_ids,element_ids=(randint(0,100,100),randint(0,100,100),randint(0,100,100))
In [5]: from collections import defaultdict
In [6]: defaultdictfunc(subcases,other_ids,element_ids)==setdefaultfunc(subcases,other_ids,element_ids)
Out[6]: True
In [7]: %timeit defaultdictfunc(subcases,other_ids,element_ids)
10 loops, best of 3: 177 ms per loop
In [8]: % timeit setdefaultfunc(subcases,other_ids,element_ids)
1 loops, best of 3: 351 ms per loop
Почему setdefaultfunc
помедленнее. Я думал, что основной код будет таким же. Есть ли способ улучшить его скорость?
Спасибо
3 ответа
По словам пользователя aneroid:
Было бы понятно, что
defaultdict
быстрее, чемdict.setdefault()
поскольку первый устанавливает значение по умолчанию для всего dict во время создания, тогда как setdefault() делает это для каждого элемента, когда он читается. Одна из причин использования setdefault заключается в том, что назначаемое вами значение по умолчанию основано на ключе (или чем-то еще), а не на общем значении по умолчанию для всего dict.
val = 20_000_000
def defaultdict():
"""
defaultdict 1000000: 0.4460279941558838
defaultdict 10000000: 4.371468782424927
defaultdict 20000000: 8.807381391525269
"""
from collections import defaultdict
import time
a = defaultdict(list)
t = time.time()
for i in range(val):
key = i % (val / 2)
a[key].append(i)
print(f'defaultdict {val}:', time.time() - t)
def setdefault():
"""
setdefault 1000000: 0.3767530918121338
setdefault 10000000: 4.230009078979492
setdefault 20000000: 8.19938588142395
"""
import time
a = {}
t = time.time()
for i in range(val):
key = i % (val / 2)
a.setdefault(key, []).append(i)
print(f'setdefault {val}:', time.time() - t)
Питон 3.10
В setdefaultfunc
хуже, потому что вы вызываете конструктор dict несколько раз в цикле (поскольку {}
эквивалентно dict()
), пока defaultdict
избежать этого по собственной инициативе.
С небольшим изменением вы можете легко улучшить setdefaultfunc
:
def setdefaultfunc2(subcases,other_ids,element_ids):
dict_name={}
for subcase in subcases:
subcase_dict = dict_name.setdefault(subcase,{})
for other_id in other_ids:
other_id_dict = subcase_dict.setdefault(other_id,{})
for element_id in element_ids:
if element_id in other_id_dict:
# error duplicate element_id
pass
else:
other_id_dict[element_id]=0
return dict_name
С этим изменением результаты на моей машине были:
In [37]: defaultdictfunc(subcases,other_ids,element_ids)==setdefaultfunc2(subcases,other_ids,element_ids)
Out[37]: True
In [38]: %timeit defaultdictfunc(subcases,other_ids,element_ids)
286 ms ± 8.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [39]: %timeit setdefaultfunc(subcases,other_ids,element_ids)
434 ms ± 1.78 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [40]: %timeit setdefaultfunc2(subcases,other_ids,element_ids)
174 ms ± 348 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
ИМО, defaultdict
не обеспечивает достаточного прироста производительности, чтобы его можно было использовать.