为什么我的埃拉托色尼筛法处理整数比处理布尔值更快?
Why does my Sieve of Eratosthenes work faster with integers than with booleans?
我写了一个简单的埃拉托色尼筛法,它使用一个 1 列表,如果不是素数,则将它们变成零,如下所示:
def eSieve(n): #Where m is fixed-length list of all integers up to n
'''Creates a list of primes less than or equal to n'''
m = [1]*(n+1)
for i in xrange(2,int((n)**0.5)+1):
if m[i]:
for j in xrange(i*i,n+1,i):
m[j]=0
return [i for i in xrange(2,n) if m[i]]
我用 %timeit
测试了它 运行 的速度并得到:
#n: t
#10**1: 7 μs
#10**2: 26.6 μs
#10**3: 234 μs
#10**4: 2.46 ms
#10**5: 26.4 ms
#10**6: 292 ms
#10**7: 3.27 s
我假设,如果我将 [1]
和 0
更改为布尔值,它会 运行 更快......但它恰恰相反:
#n: t
#10**1: 7.31 μs
#10**2: 29.5 μs
#10**3: 297 μs
#10**4: 2.99 ms
#10**5: 29.9 ms
#10**6: 331 ms
#10**7: 3.7 s
为什么布尔值比较慢?
发生这种情况是因为 True
和 False
在 Python 中被查找为全局变量 2. 0
和 1
文字只是常量,看起来通过快速数组引用向上,而全局变量是全局命名空间中的字典查找(落入内置命名空间):
>>> import dis
>>> def foo():
... a = True
... b = 1
...
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (True)
3 STORE_FAST 0 (a)
3 6 LOAD_CONST 1 (1)
9 STORE_FAST 1 (b)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
True
值使用 LOAD_GLOBAL
字节码查找,而 1
文字值使用 LOAD_CONST
.
复制到堆栈
如果你让 True
和 False
locals 你可以再次让他们一样快:
def eSieve(n, True=True, False=False):
m = [True]*(n+1)
for i in xrange(2,int((n)**0.5)+1):
if m[i]:
for j in xrange(i*i,n+1,i):
m[j]=False
return [i for i in xrange(2,n) if m[i]]
将 True
和 False
作为参数的默认值分配给函数这些名称作为局部变量,具有完全相同的值;再次使用简化版本:
>>> def bar(True=True, False=False):
... True == False
...
>>> dis.dis(bar)
2 0 LOAD_FAST 0 (True)
3 LOAD_FAST 1 (False)
6 COMPARE_OP 2 (==)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
注意 LOAD_FAST
操作码,现在与 LOAD_CONST
字节码一样具有索引; CPython 函数中的局部变量像字节码常量一样存储在数组中。
随着这一变化,使用布尔值胜出,尽管差距很小;我的时间:
# n integers globals locals
# 10**1 4.31 µs 4.2 µs 4.2 µs
# 10**2 17.1 µs 17.3 µs 16.5 µs
# 10**3 147 µs 158 µs 144 µs
# 10**4 1.5 ms 1.66 ms 1.48 ms
# 10**5 16.4 ms 18.2 ms 15.9 ms
# 10**6 190 ms 215 ms 189 ms
# 10**7 2.21 s 2.47 s 2.18 s
区别并没有那么大,因为 Python 布尔值只是一个 int
子类。
请注意,在 Python 3 中,True
和 False
已成为关键字,不能再分配给它们,因此可以像整数文字一样对待它们。
我写了一个简单的埃拉托色尼筛法,它使用一个 1 列表,如果不是素数,则将它们变成零,如下所示:
def eSieve(n): #Where m is fixed-length list of all integers up to n
'''Creates a list of primes less than or equal to n'''
m = [1]*(n+1)
for i in xrange(2,int((n)**0.5)+1):
if m[i]:
for j in xrange(i*i,n+1,i):
m[j]=0
return [i for i in xrange(2,n) if m[i]]
我用 %timeit
测试了它 运行 的速度并得到:
#n: t
#10**1: 7 μs
#10**2: 26.6 μs
#10**3: 234 μs
#10**4: 2.46 ms
#10**5: 26.4 ms
#10**6: 292 ms
#10**7: 3.27 s
我假设,如果我将 [1]
和 0
更改为布尔值,它会 运行 更快......但它恰恰相反:
#n: t
#10**1: 7.31 μs
#10**2: 29.5 μs
#10**3: 297 μs
#10**4: 2.99 ms
#10**5: 29.9 ms
#10**6: 331 ms
#10**7: 3.7 s
为什么布尔值比较慢?
发生这种情况是因为 True
和 False
在 Python 中被查找为全局变量 2. 0
和 1
文字只是常量,看起来通过快速数组引用向上,而全局变量是全局命名空间中的字典查找(落入内置命名空间):
>>> import dis
>>> def foo():
... a = True
... b = 1
...
>>> dis.dis(foo)
2 0 LOAD_GLOBAL 0 (True)
3 STORE_FAST 0 (a)
3 6 LOAD_CONST 1 (1)
9 STORE_FAST 1 (b)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
True
值使用 LOAD_GLOBAL
字节码查找,而 1
文字值使用 LOAD_CONST
.
如果你让 True
和 False
locals 你可以再次让他们一样快:
def eSieve(n, True=True, False=False):
m = [True]*(n+1)
for i in xrange(2,int((n)**0.5)+1):
if m[i]:
for j in xrange(i*i,n+1,i):
m[j]=False
return [i for i in xrange(2,n) if m[i]]
将 True
和 False
作为参数的默认值分配给函数这些名称作为局部变量,具有完全相同的值;再次使用简化版本:
>>> def bar(True=True, False=False):
... True == False
...
>>> dis.dis(bar)
2 0 LOAD_FAST 0 (True)
3 LOAD_FAST 1 (False)
6 COMPARE_OP 2 (==)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
注意 LOAD_FAST
操作码,现在与 LOAD_CONST
字节码一样具有索引; CPython 函数中的局部变量像字节码常量一样存储在数组中。
随着这一变化,使用布尔值胜出,尽管差距很小;我的时间:
# n integers globals locals
# 10**1 4.31 µs 4.2 µs 4.2 µs
# 10**2 17.1 µs 17.3 µs 16.5 µs
# 10**3 147 µs 158 µs 144 µs
# 10**4 1.5 ms 1.66 ms 1.48 ms
# 10**5 16.4 ms 18.2 ms 15.9 ms
# 10**6 190 ms 215 ms 189 ms
# 10**7 2.21 s 2.47 s 2.18 s
区别并没有那么大,因为 Python 布尔值只是一个 int
子类。
请注意,在 Python 3 中,True
和 False
已成为关键字,不能再分配给它们,因此可以像整数文字一样对待它们。