yuv*_*gin 18 python performance set python-3.x
关于这个问题的讨论让我感到疑惑,所以我决定运行一些测试并比较创建时间set((x,y,z))与{x,y,z}在Python中创建集合(我使用的是Python 3.7).
我使用time和比较了这两种方法timeit.两者都是一致*,结果如下:
test1 = """
my_set1 = set((1, 2, 3))
"""
print(timeit(test1))
Run Code Online (Sandbox Code Playgroud)
结果:0.30240735499999993
test2 = """
my_set2 = {1,2,3}
"""
print(timeit(test2))
Run Code Online (Sandbox Code Playgroud)
结果:0.10771795900000003
所以第二种方法几乎比第一种方法快3倍.这对我来说是一个非常惊人的差异.幕后发生了什么,以这种方式优化设置文字的性能set()?哪种情况最合适?
*注意:我只显示timeit测试结果,因为它们在许多样本上取平均值,因此可能更可靠,但测试时的time结果在两种情况下都显示出类似的差异.
编辑:我知道这个类似的问题,虽然它回答了我原来问题的某些方面,但它没有涵盖所有这些问题.在问题中没有解决集合,并且由于空集在python中没有文字语法,我很好奇(如果有的话)使用文字的集合创建与使用该set()方法不同.另外,我想知道的是如何处理的元组参数中set((x,y,z)会在幕后,什么是运行时可能产生的影响.coldspeed的最佳答案有助于澄清问题.
cs9*_*s95 31
(这是为了响应现在已经从初始问题中编辑过的代码)您忘记在第二种情况下调用函数.进行适当的修改,结果如预期:
test1 = """
def foo1():
my_set1 = set((1, 2, 3))
foo1()
"""
timeit(test1)
# 0.48808742000255734
Run Code Online (Sandbox Code Playgroud)
test2 = """
def foo2():
my_set2 = {1,2,3}
foo2()
"""
timeit(test2)
# 0.3064506609807722
Run Code Online (Sandbox Code Playgroud)
现在,时间差异的原因是因为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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,函数调用由指令作出CALL_FUNCTION的元组(1, 2, 3)(它也带有自己的开销,尽管短轴它加载为常数通过LOAD_CONST),而在所述第二指令仅仅是一个BUILD_SET呼叫,这是更高效.
Re:关于元组构造所花时间的问题,我们看到这实际上可以忽略不计:
timeit("""(1, 2, 3)""")
# 0.01858693000394851
timeit("""{1, 2, 3}""")
# 0.11971827200613916
Run Code Online (Sandbox Code Playgroud)
元组是不可变的,所以编译器通过加载它作为一个常量,这被称为优化此操作常量合并(可以从清楚地看到这LOAD_CONST上述指令),所以所用的时间是可以忽略不计.设置是不可见的,因为它们是可变的(感谢@ user2357112指出这一点).
对于较大的序列,我们看到类似的行为.{..}使用set comprehension构造集合时,语法更快,而不必set()从生成器构建集合.
timeit("""set(i for i in range(10000))""", number=1000)
# 0.9775058150407858
timeit("""{i for i in range(10000)}""", number=1000)
# 0.5508635920123197
Run Code Online (Sandbox Code Playgroud)
作为参考,您还可以在更新的版本上使用iterable unpacking:
timeit("""{*range(10000)}""", number=1000)
# 0.7462548640323803
Run Code Online (Sandbox Code Playgroud)
然而,有趣的是,set()直接调用时速度更快range:
timeit("""set(range(10000))""", number=1000)
# 0.3746800610097125
Run Code Online (Sandbox Code Playgroud)
这恰好比设定结构快.您将看到其他序列(例如lists)的类似行为.
我的建议是{...}在构造集合文字时使用集合理解,并作为传递生成器理解的替代方法set(); 而是用于set()将现有序列/ iterable转换为集合.
| 归档时间: |
|
| 查看次数: |
1193 次 |
| 最近记录: |