该图来自David Beazley的GIL可视化
如果你有一个网络应用(例如Django)并且使用WSGI,那么每一个对于你的网络应用的请求将是一个独立的Python解释器,因此每个请求只有一个锁。因为Python解释器启动很慢,一些WSGI便集成了能够使保持Python进程的“守护进程” 。
那么其他Python解释器的速度又如何呢?
PyPy拥有GIL,通常比CPython快至少三倍。
Jython没有GIL,因为在Jython中Python线程是用Java线程表示的,这得益于JVM内存管理系统。
Java是如何做到这一点的呢?
首先,所有的Java引擎使用标记加清除的垃圾收集系统,而之前提到GIL的基本诉求是CPython的存储管理算法。
Java没有GIL,但因为它是单线程的,所以也并不需要GIL。
Java通过事件循环和承诺/回调模式来实现异步编程的并发。Python有与异步事件循环相似的过程。
“因为它是解释型语言”
我经常听到这句话。我觉得这只是对于CPython实际运行方式的一种简单解释。如果你在终端中输入python my.py,那么CPython将对这段代码开始一系列的读取,词法分析,解析,编译,解释和运行。
这个过程中的重要步骤是在编译阶段创建一个.pyc 文件,这个字节码序列将被写入Python3下__pycache__/ 路径中的一个文件(对于Python2,文件路径相同)。这个步骤不仅仅应用于脚本文件,也应用于所有导入的代码,包括第三方模块。
所以大多时候(除非你写的代码只运行一次),Python是在解释字节码并且本地执行。下面我们将Java和C#.NET相比较:
Java编译成一门“中间语言”,然后Java虚拟机读取字节代码并即时编译为机器代码。.NET的通用中间语言(CIL)是一样的,它的通用语言运行时间(CLR)也采用即时编译的方法转化为机器代码。
那么,如果Python用的是和Java和C#一样的虚拟机和某种字节代码,为什么在基准测试中它却慢得多?首先,.NET和Java是采用JIT编译的。
JIT,又称即时编译,需要一种中间语言来把代码进行分块(或者叫数据帧)。预编译(AOT, Ahead of Time)器的设计保证了CPU能够在交互之前理解代码中的每一行。
JIT本身不会使执行速度更快,因为它仍然执行相同的字节码序列。但是,JIT允许在运行时进行优化。好的JIT优化器可以检测哪些部分执行次数比较多,这些部分被称为“热点”。然后,它将用更高效的代码替换它们,完成优化。
这就意味着当计算机应用程序需要重复做一件事情的时候,它就会更加地快。另外,我们要知道Java和C#是强类型语言(变量需要预定义),因此优化器可以对代码做更多的假设。
PyPy使用即时编译器,并且前文也有提到它比CPython更快。这篇关于基准测试的文章介绍得更为详细——什么版本的Python最快?
链接:
https://hackernoon.com/which-is-the-fastest-version-of-python-2ae7c61a6b2b
那么,为什么CPython不使用即时编译器呢?
JIT存在一些缺点:其中一个是启动时间。CPython启动时间已经相对较慢,PyPy比CPython还要慢2-3倍。众所周知,Java虚拟机的启动速度很慢。为了解决这个问题,.NET CLR在系统启动的时候就开始运行,但CLR的开发人员还开发了专门运行CLR的操作系统来加快它。
如果你有一个运行时间很长的Python进程,并且其代码可以被优化(因为它包含前文所述的“热点”),那么JIT就能够起到很大作用。
但是,CPython适用于各类应用。因此,如果你使用Python开发命令行应用程序,每次调用CLI时都必须等待JIT启动,这将非常缓慢。
CPython必须尽量多地尝试不同的案例以保证通用性,而把JIT插入到CPython中可能会让这个项目停滞不前。
如果你想要借助JIT的力量,而且你的工作量还比较大,那么使用PyPy吧。
“因为它是一个动态类型语言”
在静态类型语言中,定义变量时必须声明类型。C, C++, Java, C#, Go都是这种语言。
在动态类型语言中,类型的概念依旧存在,但是这个变量的类型是动态变化的。
a = 1a = “foo”
在上面这个例子中,Python创建第二个变量的时候用了同样的名字,但是变量类型是str(字符型),这样就对先前在内存中给a分配的空间进行了释放和再分配。
静态类型语言的这种设计并不是为了麻烦大家——它们是按照CPU的运行方式设计的。如果最终需要将所有内容都转化为简单的二进制操作,那就必须将对象和类型转换为低级数据结构。
Python自动完成了这个过程,我们看不见,也没必要看见。
不必声明类型不是使Python变慢的原因。Python语言的设计使我们几乎可以创建任何动态变量。我们可以在运行时替换对象中的方法,也可以胡乱地把低级系统调用赋给一个值。几乎怎么修改都可以。
正是这种设计使得优化Python变得异常困难。
为了阐明我的观点,我将使用一个MacOS中的应用。它是一个名为Dtrace的系统调用跟踪工具。CPython发行版没有内置DTrace,因此你必须重新编译CPython。以下演示中使用3.6.6版本。
wget https://github.com/python/cpython/archive/v3.6.6.zipunzip v3.6.6.zipcd v3.6.6./configure --with-dtracemake
现在python.exe将在整条代码中使用Dtrace跟踪器。Paul Ross就Dtrace做了一篇很棒的短演讲。 你可以下载Python的DTrace启动文件来测试函数调用、执行时间、CPU时间、系统调用等各种有意思的事情。例如:
sudo dtrace -s toolkit/.d -c ‘…/cpython/python.exe .py’
DTrace启动文件:
https://github.com/paulross/dtrace-py/tree/master/toolkit
演讲链接:
https://github.com/paulross/dtrace-py#the-lightning-talk
py_callflow跟踪器显示应用程序中的所有函数调用
因此,是Python的动态类型让它变慢的吗?
比较和转换类型是耗时的,因为每次读取、写入变量或引用变量类型时都会进行检查
很难优化一种如此动态的语言。其他语言之所以那么快是因为他们牺牲了一定的灵活性,从而提高了性能。
了解一下Cython,它结合了C-Static类型和Python来优化已知类型的代码,可以提供84倍速度的性能提升。
结论
Python的缓慢主要是由于它动态和多用途的特点。它可以用于解决几乎所有问题,但是更加优化而快捷的替代方案可能存在。
但是,有一些方法可以通过利用异步计算,理解分析工具,以及考虑使用多个解释器来优化Python应用程序。
如果你也是看准了Python,想自学Python,在这里为大家准备了丰厚的免费学习大礼包,带大家一起学习,给大家剖析Python兼职、就业行情前景的这些事儿。
一、Python所有方向的学习路线
Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、学习软件
工欲善其必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。
三、全套PDF电子书
书籍的好处就在于权威和体系健全,刚开始学习的时候你可以只看视频或者听某个人讲课,但等你学完之后,你觉得你掌握了,这时候建议还是得去看一下书籍,看权威技术书籍也是每个程序员必经之路。
四、入门学习视频
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
四、实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、面试资料
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
成为一个Python程序员专家或许需要花费数年时间,但是打下坚实的基础只要几周就可以,如果你按照我提供的学习路线以及资料有意识地去实践,你就有很大可能成功!
最后祝你好运!!!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!