前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python 之父亲自优化解释器性能

Python 之父亲自优化解释器性能

作者头像
初代庄主
发布于 2022-09-22 03:26:05
发布于 2022-09-22 03:26:05
31800
代码可运行
举报
文章被收录于专栏:初代庄主初代庄主
运行总次数:0
代码可运行



背景

2020 年 11 月 Python 之父(Guido)加入微软,按他个人的说法是自己的退休生活太无聊了。站在现在的这个时间点,一年半的时间过去了。Guido 的工作中对开发者来说感知最强的应该数 Cpython 解释器的性能优化了。


技术路线

这次的优化版本会与 Python-3.11 一起推出,也就是说想要体验更好的性能,我们要升级一下解释器。

整个 Python-3.11 的优化思路是解释器识别出热点代码块,根据识别出来的内容生成逻辑上一致的,但是执行效率更高的专用代码,用新的代码替换掉老的代码。

通过“识别”、“生成”、“替换” 这三步曲来提升执行性能,由于“识别”就是基于场景的,也就是说并不是所有的 Python 指令在 Python-3.11 上都得到了性能提升。


各个场景下的提升情况

根据官方的预发布文档,各个不同场景下的优化成果如下。

优化项

提升百分比

语法样例

二元运算符

10%

1 + 1

索引读取

10% ~ 25%

names[0]

索引赋值

10% ~ 25%

names[0] = 'tom'

函数调用

20%

print("abc")

对象方法加载

10% ~ 20%

o.meth()

对象属性赋值

2%

o.attr = 'abc'

拆包

8%

*seq

从官方给的优化项目来看,最高的也就 25% ,是不是说我的解释器升级到 Python-3.11 之后最多也就能提升 25% 呢?由于程序在各个步骤之间会依赖关系所以最终表现在概率上是用乘法来算的。优化前可能是 1 * 1 * 1 ,所以都按提升 20% 来计算优化后就是 0.8 * 0.8 * 08 ~=

0.512 。

下面我们看一个只优化函数调用就能提升性能 100% 的例子。


函数调用场景为例子

既然是要验证官方对函数调用的优化,那递归就是函数调用最好的例子,那我就写一个经典的递归算法吧。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/usr/bin/env python3

from time import time
from dis import dis

def fib(index:1):
    """
    计算斐波那契数列的第 n 位。

    index:
    -------
        int
    
    return:
    -------
        int
    """

    if index == 1 or index == 2:
        return 1
    else:
        return fib(index - 1) + fib(index - 2)
    

if __name__ == "__main__":
    # 统计耗时
    start_at = time()
    fib(34)
    stop_at = time()
    print(f"total cost {stop_at - start_at}")

    # 打印 fib 函数的字节码
    print("-" * 48)
    print(dis(fib))
    print("-" * 48)

Python-3.x 各个版本的耗时情况

先上结论!!!

版本

耗时

python-3.10

1.67s

python-3.11

0.75s


用 Python-3.10 这个解释器执行代码观察平均耗时

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python3.10 main.py 
total cost 1.672095012664795
------------------------------------------------
 19           0 LOAD_FAST                0 (target)
              2 LOAD_CONST               1 (1)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_TRUE         8 (to 16)
              8 LOAD_FAST                0 (target)
             10 LOAD_CONST               2 (2)
             12 COMPARE_OP               2 (==)
             14 POP_JUMP_IF_FALSE       10 (to 20)

 20     >>   16 LOAD_CONST               1 (1)
             18 RETURN_VALUE

 22     >>   20 LOAD_GLOBAL              0 (fib)
             22 LOAD_FAST                0 (target)
             24 LOAD_CONST               1 (1)
             26 BINARY_SUBTRACT
             28 CALL_FUNCTION            1
             30 LOAD_GLOBAL              0 (fib)
             32 LOAD_FAST                0 (target)
             34 LOAD_CONST               2 (2)
             36 BINARY_SUBTRACT
             38 CALL_FUNCTION            1
             40 BINARY_ADD
             42 RETURN_VALUE

用 Python-3.11 这个解释器执行代码观察平均耗时

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python3.11 main.py 
total cost 0.7522010803222656
------------------------------------------------
  6           0 RESUME                   0

 19           2 LOAD_FAST                0 (index)
              4 LOAD_CONST               1 (1)
              6 COMPARE_OP               2 (==)
             12 POP_JUMP_FORWARD_IF_TRUE     6 (to 26)
             14 LOAD_FAST                0 (index)
             16 LOAD_CONST               2 (2)
             18 COMPARE_OP               2 (==)
             24 POP_JUMP_FORWARD_IF_FALSE     2 (to 30)

 20     >>   26 LOAD_CONST               1 (1)
             28 RETURN_VALUE

 22     >>   30 LOAD_GLOBAL              1 (NULL + fib)
             42 LOAD_FAST                0 (index)
             44 LOAD_CONST               1 (1)
             46 BINARY_OP               10 (-)
             50 PRECALL                  1
             54 CALL                     1
             64 LOAD_GLOBAL              1 (NULL + fib)
             76 LOAD_FAST                0 (index)
             78 LOAD_CONST               2 (2)
             80 BINARY_OP               10 (-)
             84 PRECALL                  1
             88 CALL                     1
             98 BINARY_OP                0 (+)
            102 RETURN_VALUE

分析函数调用场景的优化方式

为什么换了个解释器之后突然就变快了?不同的解决器生成的字节码不一样,Python-3.11 识别到整个 fib 函数都是热点,对其做了内联化处理。从字节码上来就是由之前传统的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CALL_FUNCTION

优化成了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
PRECALL
CALL

参考文档

https://docs.python.org/3.11/whatsnew/3.11.html


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初代庄主 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档