【问题标题】:Performance comparison: insert vs build Python set operations性能比较:insert vs build Python set 操作
【发布时间】:2011-04-29 18:07:34
【问题描述】:

在python中,是不是更快 a) 从包含 n 个项目的列表中构建一个集合 b) 将 n 项插入一个集合中?

我找到了这个页面 (http://wiki.python.org/moin/TimeComplexity) 但它没有足够的信息来断定哪个更快。

看来,一次插入一个项目在最坏的情况下可能需要 O(n*n) 时间(假设它使用 dicts),而在平均情况下需要 O(n*1) 时间。使用列表初始化集合是否会提高性能?

【问题讨论】:

  • 似乎您想比较从列表中构建一个集合与插入 n 个要设置的项目,但问题的第一行意味着不同的东西。
  • 糟糕!感谢您指出这一点。
  • 使用timeit自己很容易找到。

标签: python set time-complexity


【解决方案1】:

O() 的复杂性而言 - 这绝对是相同的,因为两种方法的作用完全相同 - 将 n 项插入集合中。

区别在于实现:从可迭代对象初始化的一个明显优势是您可以节省大量 Python 级别的函数调用 - 可迭代对象的初始化完全在 C 级别完成 (**)。

确实,对 5,000,000 个随机整数列表的一些测试表明,一个一个相加会更慢:

lst = [random.random() for i in xrange(5000000)]
set1 = set(lst)    # takes 2.4 seconds

set2 = set()       # takes 3.37 seconds
for item in lst:
    set2.add(item)

(**) 查看集合代码 (Objects/setobject.c),最终项目插入归结为对 set_add_key 的调用。当从一个可迭代对象初始化时,这个函数在一个紧凑的 C 循环中被调用:

while ((key = PyIter_Next(it)) != NULL) {
  if (set_add_key(so, key) == -1) {
    Py_DECREF(it);
    Py_DECREF(key);
    return -1;
  } 
  Py_DECREF(key);
}

另一方面,对set.add 的每次调用都会调用属性查找,该查找解析为C 的set_add 函数,该函数又调用set_add_key。由于添加项目本身相对较快(Python 的哈希表实现非常高效),这些额外的调用都会累积起来。

【讨论】:

  • Python 循环的性能比我预期的要接近得多,您可以通过创建一个包含对 set.add 的引用的局部变量并在循环中调用它来更接近,避免使用该属性抬头。在我的测试中,这仅比使用 set() 构造函数慢 15%!
【解决方案2】:
$ python -V
Python 2.5.2
$ python -m timeit -s "l = range(1000)" "set(l)"
10000 loops, best of 3: 64.6 usec per loop
$ python -m timeit -s "l = range(1000)" "s = set()" "for i in l:s.add(i)"
1000 loops, best of 3: 307 usec per loop

【讨论】:

  • 你可以通过s_add = s.add将第二个命令加速2倍:python -m timeit -s "l = range(1000)" "s = set(); s_add=s.add" "for i in l:s_add(i)"
【解决方案3】:

在我的 Thinkpad 上:

In [37]: timeit.timeit('for a in x: y.add(a)',
                       'y=set(); x=range(10000)', number=10000)
Out[37]: 12.18006706237793

In [38]: timeit.timeit('y=set(x)', 'y=set(); x=range(10000)', number=10000)
Out[38]: 3.8137960433959961

【讨论】:

    【解决方案4】:

    以下是使用timeit 运行比较的结果。似乎使用 list 初始化 set 更快,很想知道为什么会这样:

    from timeit import timeit
    timeit("set(a)","a=range(10)")
    # 0.9944498532640864
    
    timeit("for i in a:x.add(i)","a=range(10);x=set()")
    # 1.6878826778265648
    

    Python 版本:2.7

    【讨论】:

      猜你喜欢
      • 2023-03-25
      • 1970-01-01
      • 2015-01-28
      • 1970-01-01
      • 2011-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多