it-swarm.com.ru

Набор против производительности Frozenset

Я возился с типами коллекций Python set и frozenset.

Первоначально я предполагал, что frozenset обеспечит лучшую производительность поиска, чем set, поскольку он неизменен и, следовательно, может использовать структуру хранимых элементов.

Однако, похоже, что это не так в отношении следующего эксперимента:

import random
import time
import sys

def main(n):
    numbers = []
    for _ in xrange(n):
        numbers.append(random.randint(0, sys.maxint))
    set_ = set(numbers)
    frozenset_ = frozenset(set_)

    start = time.time()
    for number in numbers:
        number in set_
    set_duration = time.time() - start

    start = time.time()
    for number in numbers:
        number in frozenset_
    frozenset_duration = time.time() - start

    print "set      : %.3f" % set_duration
    print "frozenset: %.3f" % frozenset_duration


if __== "__main__":
    n = int(sys.argv[1])
    main(n)

Я выполнил этот код, используя как CPython, так и PyPy, что дало следующие результаты:

> pypy set.py 100000000
set      : 6.156
frozenset: 6.166

> python set.py 100000000
set      : 16.824
frozenset: 17.248

Кажется, что frozenset на самом деле медленнее в отношении производительности поиска, как в CPython, так и в PyPy. У кого-нибудь есть идея, почему это так? Я не смотрел в реализации.

13
Sven Hager

Реализации frozenset и set в основном используются совместно; set - это просто frozenset с добавленными методами мутации с точно такой же реализацией хеш-таблицы. Смотрите Objects/setobject.c исходный файл ; определение верхнего уровня PyFrozenSet_Type разделяет функции с определением PySet_Type .

Здесь нет оптимизации для frozenset, так как нет необходимости вычислять хэши для элементов в переменной frozenset, когда вы проверяете членство. Элемент, который вы используете для проверки против набора, все еще должен рассчитать их хэш, чтобы найти правильный слот в хэш-таблице набора, чтобы вы могли выполнить тест на равенство.

Таким образом, ваши временные результаты, вероятно, отключены из-за других процессов, запущенных в вашей системе; Вы измерили время настенных часов и не отключили сборку мусора в Python, а также неоднократно проверяли одно и то же.

Попробуйте запустить свой тест, используя модуль timeit , с одним значением из numbers и одним значением не из набора:

import random
import sys
import timeit

numbers = [random.randrange(sys.maxsize) for _ in range(10000)]
set_ = set(numbers)
fset = frozenset(numbers)
present = random.choice(numbers)
notpresent = -1
test = 'present in s; notpresent in s'

settime = timeit.timeit(
    test,
    'from __main__ import set_ as s, present, notpresent')
fsettime = timeit.timeit(
    test,
    'from __main__ import fset as s, present, notpresent')

print('set      : {:.3f} seconds'.format(settime))
print('frozenset: {:.3f} seconds'.format(fsettime))

Это повторяет каждый тест 1 миллион раз и производит:

set      : 0.050 seconds
frozenset: 0.050 seconds
44
Martijn Pieters

Причина двух разных типов данных не в производительности, а в функциональности. Поскольку frozensets являются неизменяемыми, они могут быть использованы в качестве ключа в словарях. Наборы не могут быть использованы для этой цели.

1
T. Durbin