“让你的蛇蛇快爬吧”——使用各式JIT加速Python运行


为什么Python比C/C++慢?

动态特性

Python是一种动态、多功能的语言,因此运行起来会相对缓慢。

CC++JavaC#Go 这些静态类型语言中,必须在声明变量时指定变量的类型。而在动态类型语言中,虽然也有类型的概念,但变量的类型是可改变的。

a = 1
a = "foo"

在上面这个示例里,Python 将变量 a 一开始存储整数类型变量的内存空间释放了,并创建了一个新的存储字符串类型的内存空间,并且和原来的变量同名。

静态类型语言这样的设计并不是为了为难你,而是为了方便 CPU 运行而这样设计的。因为最终都需要将所有操作都对应为简单的二进制操作,因此必须将对象、类型这些高级的数据结构转换为低级数据结构。

Python 也实现了这样的转换,但用户看不到这些转换,也不需要关心这些转换。

不用必须声明类型并不是为了使 Python 运行慢,Python 的设计是让用户可以让各种东西变得动态:可以在运行时更改对象上的方法,也可以在运行时动态添加底层系统调用到值的声明上,可用性恨高。

但也正是这种设计使得 Python 的优化异常的难。

但我们不需责备Python,这也是Python的一种设计理念。

全局解释器锁

现代计算机都配备了具有多个核的 CPU ,有时甚至还会有多个处理器。为了更充分利用它们的处理能力,操作系统定义了一个称为线程的低级结构。某一个进程(例如 Chrome 浏览器)可以建立多个线程,在系统内执行不同的操作。在这种情况下,CPU 密集型进程就可以跨核心分担负载了,这样的做法可以大大提高应用程序的运行效率。

例如在我写这篇文章时,我的 Chrome 浏览器打开了 44 个线程。需要提及的是,基于 POSIX 的操作系统(例如 Mac OS、Linux)和 Windows 操作系统的线程结构、API 都是不同的,因此操作系统还负责对各个线程的调度。

如果你还没有写过多线程执行的代码,你就需要了解一下线程锁的概念了。多线程进程比单线程进程更为复杂,是因为需要使用线程锁来确保同一个内存地址中的数据不会被多个线程同时访问或更改。

CPython 解释器在创建变量时,首先会分配内存,然后对该变量的引用进行计数,这称为 引用计数(reference counting)。如果变量的引用数变为 0,这个变量就会从内存中释放掉。这就是在 for 循环代码块内创建临时变量不会增加内存消耗的原因。

而当多个线程内共享一个变量时,CPython 锁定引用计数的关键就在于使用了 GIL,它会谨慎地控制线程的执行情况,无论同时存在多少个线程,解释器每次只允许一个线程进行操作。

trn曾做过关于上文的presentation,大家应该有印象,也应该有印象

GIL Compete

由 David Beazley 提供的 GIL 竞争情况图原文链接

如何优化?

JIT

TL;DR:

即时编译(JIT) - Wikipedia

JIT的安装

PyPy

https://doc.pypy.org/en/latest/install.html

  • 使用打包好的 PyPy - 使用 Anaconda / Miniconda 的同学

    $ conda create -my_cool_pypy pypy
    $ conda activate my_cool_pypy
    $ conda install scipy
  • Windows安装预先构建的 PyPy

    http://www.pypy.org/download.html

    解压配置环境变量后可用

    python -> pypy3

    pip:如果加入PATH可能会引起紊乱,建议使用 pypy3 -m pip install [包名]

    $ pypy3 get-pip.py          # 安装 PyPy 的 PIP
    $ pypy3 -m pip install requests  # 安装 requests 包
  • Linux安装预先构建的 PyPy - 身心没被摧残完的同学

    $ tar xf pypy-x.y.z.tar.bz2
    $ ./pypy-x.y.z/bin/pypy
    Python 2.7.x (xxxxxxxxxxxx, Date, Time)
    [PyPy z.y.x with GCC z.y.x] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    And now for something completely different: ``PyPy is an exciting technology
    that lets you to write fast, portable, multi-platform interpreters with less
    effort''
    >>>>

PyPy 支持的包可以在此处查询: http://packages.pypy.org/

Pyjion

https://github.com/tonybaloney/Pyjion

Pypy 更快,但需要Python 3.10.0+及.Net 6

  • 安装

    $ python -m pip install pyjion
  • 使用

    import pyjion
    pyjion.enable()

    $ pyjion [你的文件].py

也提供了一些类似g++/cppO2/O3/Ofast优化的功能。(OI选手狂喜!)

https://pyjion.readthedocs.io/en/latest/optimizations.html


文章作者: sfc9982
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 sfc9982 !
  目录