Python dis模块和常量折叠

原作者:Yasoob
原网址:https://pythontips.com/2019/02/26/python-dis-module-and-constant-folding
原日期:February 26,2019

Python dis模块和常量折叠

大家好!当我发现:

>>> pow(3,89)

>>> 3**89

运行慢时感到十分疑惑

我试图想出一个合适的答案,但时并没有找到。我使用Python3中的timeit模块对这两个语句的执行时间进行计算:

$ python3 -m timeit 'pow(3,89)'
500000 loops, best of 5: 688 nsec per loop

$ python3 -m timeit '3**89'
500000 loops, best of 5: 519 nsec per loop

时间差不大。虽然只有0.1微妙但这还是阻挠了我。如果我不能解释一些编程上的问题,我经常会有不眠之夜?

我在Freenode上的Python IRC频道找到了答案。pow稍微慢一些的原因是在CPython中有一个额外的步骤从命名空间加载pow。然而在3**9中却不需要这样的加载。这也意味着即使输入的数字越来越大,这个差值也会基本保持不变。

假设是成立的:

$ python3 -m timeit 'pow(3,9999)'
5000 loops, best of 5: 58.5 usec per loop

$ python3 -m timeit '3**9999'
5000 loops, best of 5: 57.3 usec per loop

在探索这个问题的解决方案的过程时我也学习了dis模块。它允许您反编译Python字节码并检查它。这是一个令人兴奋的发现,正是因为我现在正在学习关于逆向工程二进制文件的知识,而这个模块正好适合我。

我在Python中这样分解了上述情况的字节码:

>>> import dis
>>> dis.dis('pow(3,89)')
#  1           0 LOAD_NAME                0 (pow)
#              2 LOAD_CONST               0 (3)
#              4 LOAD_CONST               1 (89)
#              6 CALL_FUNCTION            2
#              8 RETURN_VALUE
 
>>> dis.dis('3**64')
#  1           0 LOAD_CONST               0 (3433683820292512484657849089281)
#              2 RETURN_VALUE
 
>>> dis.dis('3**65')
#  1           0 LOAD_CONST               0 (3)
#              2 LOAD_CONST               1 (65)
#              4 BINARY_POWER
#              6 RETURN_VALUE

通过阅读Stackoverflow上的这个问题的答案来学习如何理解dis.dis的输出。

好了,现在回到代码。pow的拆解是有意义的。它从命名空间加载pow,然后将3和89加载到register,最后才调用pow函数。但是,为什么后面两个拆解后的输出不同呢?唯一改变的只是指数从64变成了65。

这个问题使我了解了“常数折叠”这个新概念。它意味着,当我们有一个常量表达式时,Python会在编译时计算这个表达式的值,这样,当你真正运行程序时,它不会花费很长时间,因为Python使用了已经计算过的值。你可以这样想:

def one_plue_one():
    return 1+1

–vs–

def one_plue_one():
    return 2

Python将第一个函数编译为第二个函数,并在运行代码时使用它,难道不是吗?

那么为什么常数折叠适用于3**64而不是3**65呢?好吧,我不知道。这可能与限制系统在内存中预先计算的有一定关联。我可能完全错了。在我看来,下一步是在空闲时间涉猎Python的源代码,并且试图弄清楚发生了什么。我仍然在试图找出这个问题的答案。

我想让你从这篇文章中学到的是,你应该寻求最基本问题的答案。你永远不知道答案会把你带向何方。你也可能会学到一些全新的东西(就像我刚刚做的那样)!我希望你们能永远保持好奇心。下次见!?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值