当前位置: 首页 > 知识库问答 >
问题:

创建集的Python性能比较-set()与{}literal[duplicate]

羊柏
2023-03-14

这个问题之后的讨论让我感到疑惑,所以我决定运行一些测试,比较set((x,y,z)){x,y,z}在Python中创建集合的创建时间(我使用的是Python3.7)。

我比较了使用timetimeit的两种方法。这两项结果均与以下结果一致*:

test1 = """
my_set1 = set((1, 2, 3))
"""
print(timeit(test1))

结果:0.30240735499999993

test2 = """
my_set2 = {1,2,3}
"""
print(timeit(test2))

所以第二种方法几乎比第一种方法快3倍。这对我来说是一个很大的差别。通过这种方式优化set文字相对于set()方法的性能,究竟是怎么回事?在哪些情况下,哪一个是可取的?

*注意:我只显示timeit测试的结果,因为它们是对许多样本进行平均的,因此可能更可靠,但使用time进行测试时的结果在这两种情况下显示出类似的差异。

编辑:我知道这个类似的问题,虽然它回答了我最初问题的某些方面,但它没有涵盖所有问题。问题中没有处理集合,由于空集合在python中没有literal语法,所以我很好奇使用literal创建集合与使用set()方法有何区别(如果有的话)。此外,我还想知道set((x,y,z)中元组参数的处理在幕后是如何发生的,以及它对运行时的可能影响是什么。

共有1个答案

公良云
2023-03-14

(这是对已经从初始问题中编辑出来的代码的响应)在第二种情况下,您忘记调用函数了。进行适当的修改,结果如预期的那样:

test1 = """
def foo1():
     my_set1 = set((1, 2, 3))
foo1()
"""    
timeit(test1)
# 0.48808742000255734
test2 = """
def foo2():
    my_set2 = {1,2,3}
foo2()
"""    
timeit(test2)
# 0.3064506609807722

现在,时间差异的原因是因为set()是一个需要查找符号表的函数调用,而{...}set构造是语法的一个人工产物,速度要快得多。

观察拆解后的字节码,差别就很明显了。

import dis

dis.dis("set((1, 2, 3))")
  1           0 LOAD_NAME                0 (set)
              2 LOAD_CONST               3 ((1, 2, 3))
              4 CALL_FUNCTION            1
              6 RETURN_VALUE
dis.dis("{1, 2, 3}")
  1           0 LOAD_CONST               0 (1)
              2 LOAD_CONST               1 (2)
              4 LOAD_CONST               2 (3)
              6 BUILD_SET                3
              8 RETURN_VALUE

在第一种情况下,函数调用是由元组(1,2,3)上的指令call_function进行的(它也有自己的开销,尽管开销很小--它是通过load_const作为常量加载的),而在第二种情况下,它只是一个build_set调用,这样效率更高。

Re:您关于构造元组所花费的时间的问题,我们看到这实际上是可以忽略不计的:

timeit("""(1, 2, 3)""")
# 0.01858693000394851

timeit("""{1, 2, 3}""")
# 0.11971827200613916

元组是不可变的,所以编译器通过将其加载为常量来优化这个操作--这叫做常量折叠(您可以从上面的load_const指令中清楚地看到这一点),所以所花费的时间可以忽略不计。这一点在集合中看不到,因为它们是可变的(感谢@user2357112指出了这一点)。

对于较大的序列,我们看到类似的行为。{..}语法使用集合理解构造集合的速度更快,而set()语法则必须从生成器生成集合。

timeit("""set(i for i in range(10000))""", number=1000)
# 0.9775058150407858

timeit("""{i for i in range(10000)}""", number=1000)
# 0.5508635920123197

作为参考,您还可以在更近期的版本上使用可迭代解包:

timeit("""{*range(10000)}""", number=1000)
# 0.7462548640323803

然而,有趣的是,set()range上直接调用时速度更快:

timeit("""set(range(10000))""", number=1000)
# 0.3746800610097125

这恰好比设定的构建更快。您将看到其他序列(如lists)的类似行为。

 类似资料:
  • 这个问题在StackOverflow上被问过很多次,但没有一次是基于性能的。 在《Effective Java书籍》中给出了 改进的版本简单如下:此版本使用单个String实例,而不是每次执行时都创建一个新的String实例。 因此,我尝试了这两种方法,发现性能有了显著改善: 大约需要399 372纳秒。 为什么会有这么多的性能提升?内部是否发生了编译器优化?

  • 本文向大家介绍set rs=conn.execute,set rs=server.createobject(“ADODB.recordset”)的性能对比,包括了set rs=conn.execute,set rs=server.createobject(“ADODB.recordset”)的性能对比的使用技巧和注意事项,需要的朋友参考一下 经常用asp的同行,可能会建议用set rs=conn.

  • 更新:为了更明显地说明我正在努力做的事情:我将拥有5000万以上的设备流媒体音频。流平均为100KB,峰值流量时为200K流/分钟。我正在寻找一种存储解决方案来满足这种需求。我一直在研究Bookkeeper、Kafka、Ignite、Cassandra和Redis。到目前为止,我只对redis和ignite进行了基准测试,但我很惊讶ignite这么慢。

  • 问题内容: 几天前,我开始使用新的OpenCV-Python界面。 我的问题是关于和接口的比较。 关于易用性,新界面得到了更大的改进,并且使用起来确实非常容易和有趣。 但是速度呢? 我制作了两个小代码段,一个在另一个中,以检查性能。两者具有相同的功能,访问图像的像素,对其进行测试,进行一些修改等。 下面是代码: : =========================================

  • 对于一个对象,有多个字段,其中有一些字段使用的频率比较高,而一些字段使用的频率很低。现在是都建在一个集合内,并使用 filter 获取到要使用的字段。还是把使用频率低的字段提取出来,生成一个新的集合,然后使用外键的方法进行引用。请问哪一种方法性能比较好

  • 问题内容: 我建立了一个分析引擎,可以从数据库中提取50-100行原始数据(称为),在PHP上对其进行一堆统计测量,然后精确给出140个数据点,然后将它们存储在另一个表中(让我们称之为)。所有这些数据点都是非常小的整数(“ 40”,“ 2.23”,“-1024”是数据类型的很好的示例)。 我知道mysql的最大列数非常高(4000+),但是当性能真正开始下降时,似乎有很多灰色区域。 这里有一些关于