51

set私は Pythonと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 __name__ == "__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

frozensetCPython と PyPy の両方で、ルックアップのパフォーマンスに関して実際には遅いようです。なぜこれが当てはまるのか、誰にも分かりますか?私は実装を調べませんでした。

4

2 に答える 2

92

frozensetとのset実装は大部分が共有されています。aは、まったく同じハッシュテーブル実装を使用して、変更メソッドが追加されsetた単純な aです。ソースファイルfrozensetを参照してください。最上位の定義は、関数を定義と共有しますObjects/setobject.cPyFrozenSet_TypePySet_Type

メンバーシップをテストするときにアイテムハッシュを計算する必要がないため、ここではfrozensetの最適化はありません。セットに対してfrozensetテストするために使用するアイテムは、セットのハッシュテーブルで正しいスロットを見つけて等価テストを実行できるようにするために、ハッシュを計算する必要があります。

そのため、システムで実行されている他のプロセスが原因で、タイミングの結果がずれている可能性があります。壁時計の時間を測定し、Python ガベージ コレクションを無効にせず、同じことを繰り返しテストしませんでした。

timeitmoduleを使用してテストを実行してみてください。1 つの値はセットからnumbers、もう 1 つはセットにありません。

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))

これにより、各テストが 100 万回繰り返され、以下が生成されます。

set      : 0.050 seconds
frozenset: 0.050 seconds
于 2016-04-11T17:55:19.740 に答える