【问题标题】:Check for existence in a dict vs a set in python检查字典中是否存在与python中的集合
【发布时间】:2017-03-15 01:27:44
【问题描述】:

似乎检查字典键的设置要快一点:

import random
import string
import timeit

repeat = 3
numbers = 1000

def time(statement, _setup=None):
    print min(
        timeit.Timer(statement, setup=_setup or setup).repeat(
            repeat, numbers))

random.seed('slartibartfast')

# Integers
length = 100000
d = {}
for _ in range(length):
    d[random.randint(0, 10000000)] = 0
s = set(d)

setup = """from __main__ import s, d, length
"""

time('for i in xrange(length): check = i in d')
time('for i in xrange(length): check = i in s')

# Strings
d = {}
for _ in range(length):
    d[''.join(random.choice(string.ascii_uppercase) for __ in range(16))] = 0
s = set(d)

test_strings= []
for _ in range(length):
    test_strings.append(random.choice(string.ascii_uppercase) for __ in range(16))

setup = """from __main__ import s, d, length, test_strings
"""

time('for i in test_strings: check = i in d')
time('for i in test_strings: check = i in s')

打印类似:

10.1242966769
9.73939713014
10.5156763102
10.2767765061

这是意料之中的还是随机的神器?

想知道是否值得在性能密集型代码中为 dict 键创建集合。

编辑:我的测量结果确实让我对底层实现感到好奇,我并不是想节省微秒,我只是好奇 - 是的,如果事实证明底层实现真的有利于集合,我可以制作一组这些 dict 键 -与否(我实际上是在修补遗留代码)。

【问题讨论】:

  • 如果性能如此关键,以至于毫秒之间的微小差异都会产生影响,那么 Python 可能是不适合使用的语言。我会追求可读性,而不是每 100,000 次迭代节省 0.2 秒。
  • @John 是的,这本身并不是一个性能问题——我对底层实现感到好奇——dict 查找是由集合实现的吗?好像没有
  • 不,它们不是由集合实现的,但它们使用与集合相同的基本方法。也不能保证是这种情况,只有大 O 约束哦 O(1) 查找在合理条件下成立。
  • @Mr_and_Mrs_D 这是一个有趣的问题。没有多想,我的直觉是,在引擎盖下,集合是具有空值的字典,因此我猜想查找时间几乎是相同的。它似乎比这更复杂一些。
  • 用字符串基准编辑

标签: python performance python-2.7 dictionary set


【解决方案1】:

可能取决于多种因素。在我的运行中,字典查找稍微快了一点,但还不足以让我兴奋:

In [1]: import numpy as np

In [2]: d = {i: True for i in np.random.random(1000)}

In [3]: s = {i for i in np.random.random(1000)}

In [4]: checks = d.keys()[:500] + list(s)[:500]

In [5]: %timeit [k in d for k in checks]
10000 loops, best of 3: 83 µs per loop

In [6]: %timeit [k in s for k in checks]
10000 loops, best of 3: 88.4 µs per loop

In [7]: d = {i: True for i in np.random.random(100000)}

In [8]: s = {i for i in np.random.random(100000)}

In [9]: checks = d.keys()[:5000] + list(s)[:5000]

In [10]: %timeit [k in d for k in checks]
1000 loops, best of 3: 865 µs per loop

In [11]: %timeit [k in s for k in checks]
1000 loops, best of 3: 929 µs per loop

【讨论】:

    【解决方案2】:

    老实说,这在很大程度上取决于硬件、操作系统和数据大小/约束。一般来说,在您获得非常大的数据大小之前,性能几乎相同。注意这里的一些运行dict 稍微好一点。在较大的数据结构尺寸下,内部实现细节开始主导差异,在我的机器上set 往往表现得更好。

    现实情况是,在大多数情况下,增量无关紧要。如果您真的想要更好的查找性能,请考虑使用 cython 或 ctypes 迁移到 C 级操作,或者使用为更大数据量设计的库实现。当达到数百万个元素时,Python 基本类型并不意味着性能。

    >>> # With empty dict as setup in question
    >>> time('for i in xrange(length): check = i in d')
    2.83035111427
    >>> time('for i in xrange(length): check = i in s')
    2.87069892883
    >>> d = { random.random(): None for _ in xrange(100000) }
    >>> s = set(d)
    >>> time('for i in xrange(length): check = i in d')
    3.84766697884
    >>> time('for i in xrange(length): check = i in s')
    3.97955989838
    >>> d = { random.randint(0, 1000000000): None for _ in xrange(100000) }
    >>> s = set(d)
    >>> time('for i in xrange(length): check = i in d')
    3.96871709824
    >>> time('for i in xrange(length): check = i in s')
    3.62110710144
    >>> d = { random.randint(0, 1000000000): None for _ in xrange(10000000) }
    >>> s = set(d)
    >>> time('for i in xrange(length): check = i in d')
    10.6934559345
    >>> time('for i in xrange(length): check = i in s')
    5.7491569519
    

    【讨论】:

      猜你喜欢
      • 2012-01-03
      • 2018-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-23
      • 2021-11-30
      • 2021-10-04
      • 2023-02-09
      相关资源
      最近更新 更多