gpt4 book ai didi

python - 为什么使用参数会使这个函数慢得多?

转载 作者:太空狗 更新时间:2023-10-29 17:02:20 38 4
gpt4 key购买 nike

我最近一直致力于创建素数查找程序。但是,我开始注意到一个函数在使用参数时比在使用预设值时慢得多

在 3 个不同的版本中,很明显变量显着减慢了程序速度,我想知道原因。

版本 1:7.5 秒

这是原始的(针对这个问题做了一些简化)函数:

def version1(n, p):
return ((n*n - 2) & ((1 << p) - 1)) + ((n*n - 2) >> p)

当使用 timeit 模块运行 100 次时:

timeit.timeit("version1(200, 500000000)", "from __main__ import version1", number=100)

需要 7.5 秒。

版本 2:0.0001 秒

不过,这里是第二个版本,没有参数,数字直接放在返回值中。该等式与版本 1 完全相同:

def version2():
return ((200*200 - 2) & ((1 << 500000000) - 1)) + ((200*200 - 2) >> 500000000)

当使用 timeit 模块运行 100 次时:

timeit.timeit("version2()", "from __main__ import version2", number=100

这只需要 0.00001 秒!

版本 3:6.3 秒

最后,为了完整性,我尝试了一个没有参数的版本,但仍然将其值保留为变量:

def version3():
n = 200
p = 500000000
return ((n*n - 2) & ((1 << p) - 1)) + ((n*n - 2) >> p)

当使用 timeit 运行时:

timeit.timeit("version3()", "from __main__ import version3", number = 100)

耗时 6.3 秒,相对接近版本 1

为什么当涉及到变量时,相同的函数会花费如此长的时间,我如何才能使版本 1 更高效?

最佳答案

Python 在编译时预先计算计算为所谓的 peep-hole optimisation :

>>> import dis
>>> def version2():
... return ((200*200 - 2) & ((1 << 500000000) - 1)) + ((200*200 - 2) >> 500000000)
...
>>> dis.dis(version2)
2 0 LOAD_CONST 13 (39998)
2 RETURN_VALUE

version2() 返回已计算的值,并且不做任何实际工作。返回一个常量当然比每次都计算值要快得多。

参见 fold_binops_on_constants functionpeephole.c Python 源文件中了解有关编译器如何执行此操作的详细信息。

因此,编译 version2version1 花费(很多)时间:

>>> import timeit
>>> version1_text = '''\
... def version1(n, p):
... return ((n*n - 2) & ((1 << p) - 1)) + ((n*n - 2) >> p)
... '''
>>> version2_text = '''\
... def version2():
... return ((200*200 - 2) & ((1 << 500000000) - 1)) + ((200*200 - 2) >> 500000000)
... '''
>>> timeit.timeit("compile(t, '', 'exec')", 'from __main__ import version1_text as t', number=10)
0.00028649598243646324
>>> timeit.timeit("compile(t, '', 'exec')", 'from __main__ import version2_text as t', number=10)
2.2103765579813626

幸好 Python 缓存了编译的字节码结果!

每个子表达式的中间结果也存储在代码对象的co_consts属性中,其中一些比较大:

>>> import sys
>>> consts = version2.__code__.co_consts
>>> for obj in consts:
... size = sys.getsizeof(obj)
... print(f'{type(obj)!s:<18} {size:<8} {"<too large to print>" if size > 100 else obj}')
...
<class 'NoneType'> 16 None
<class 'int'> 28 200
<class 'int'> 28 2
<class 'int'> 28 1
<class 'int'> 28 500000000
<class 'int'> 28 40000
<class 'int'> 28 39998
<class 'int'> 66666692 <too large to print>
<class 'int'> 66666692 <too large to print>
<class 'int'> 28 39998
<class 'int'> 28 40000
<class 'int'> 28 39998
<class 'int'> 24 0
<class 'int'> 28 39998

所以这确实让字节码缓存变大了一点:

>>> import marshal
>>> len(marshal.dumps(version1.__code__))
129
>>> len(marshal.dumps(version2.__code__))
133333481

包含非参数版本的模块的 .pyc 文件至少有 127MB!

关于python - 为什么使用参数会使这个函数慢得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43823807/

38 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com