Python为什么会相对比较慢?
2013-07-19 00:00
344 查看
在前面 《Python的数据类型/变量类型》 这个条目中,我们提到了,Python 既是动态类型语言 (因为它不使用显示数据类型声明),又是强类型语言(因为只要一个变量获得了一个数据类型,它实际上就一直是这个类型了)。
然后在 《为什么我们要学习Python?》 这个条目中,又谈到了Python语言的缺点。Python 语言唯一的不足是性能问题。
为什么 Python 会有这样的性能问题呢?下面是一篇老外的文章,解释了这个问题。
静态类型语言中,在声明变量时已经指定了数据类型和表示方法。动态类型语言是在运行期间检查数据的类型,不得不保持描述变量值的实际类型标记,程序在每次操作变量时,需要执行数据依赖分支。
而间接分支(Indirect branch)和数据局部性(data locality)对于运行时的性能是致命的。
这就是动态语言的JIT编译器基准测试要强调near-C的内循环速度,以及避免大的数据结构和数据处理问题的原因。
我也希望像 Python 这样的动态类型语言可以变快。我试过用 Python 来进行传统的服务器编程——“系统语言”领域——但是效果真的不好,我现在考虑用 Java 重写一个服务器。
因此,我花时间思考如何真正静态地编译Python。毕竟,那是我梦寐以求的编程语言!但当我思考如何将动态类型代码与静态编译Python结合起来时,我遇到了数据变慢的问题:
在动态语言中,通常所有数组中的元素(或其他数据结构)类型各不相同,所以有不同的表示值。因此,这些值都必须被单独存放为堆,而不是顺序地存为数组。这意味着如果对不相邻的内存执行数据依赖分支,则对缓存有更高的要求。
也有一些聪明的技巧,使用变量中的特殊bit,将一些原生类型(像整型)打包成一个类型,类似于指针,但这要求寄存器在操作过程中进行跟踪,会增加开销。
还有一些方法,比如使用JIT编译热路径(hot path)时,如果你直接插入没有标类型的值,而不是在堆里分别标记类型, 那么与JIT编译过的代码的互操作性会降低,如果其他代码改变了数组中的一个值的类型,就会出现非常严重的后果。
我一直在思考,在Python语言中,什么是静态的,什么不是。通过SSTA(统计静态分析)和逃逸分析可以判断,大量正常的程序是静态的。Paul Biggar给了我信心证实我的猜测是正确的,我的Python代码90%都是静态的。
有人会问,那另外的10%呢?通常情况下,我可以让所有的都是静态的,或者想象它被参数类型的限制范围特殊化了。除了Python语言的标准模 式,其他模式都由Web服务器分配给基于HTTP方法(如果收到GET请求就称为“get”方法)的Web处理器,这也需要程序员依照switch语句 (如elifs的长链)来进行修改。
Robert Harper对“从单一类型静态语言方面,动态语言是如何实现的”这个问题作出了很好的解释,下面这句话是我希望他能进一步进行解释的:
我深知“编译器可以优化它”,至少在某些情况下。
我确信他说的“某些情况”是指遇到non-escaping的情况,因为和后面的执行代码进行交互时,你应该要能够确定escape的类型。
一些动态调用是无污染的——编译器可以从代码检查中发现一些变量(或方法)是动态的,但动态的代码不表示其他变量也是动态的,因为不同类型的变量、方法、成员的存在或缺失都被限制成了可识别的类型(或null)。
但通常编译器是无法从代码检查中发现这些情况的,如果无法追踪到执行的情况,就无法知道代码如何依赖以及如何改变其他静态变量的值。因此,工作中断,所有变量再次变为动态的。
我一直在努力寻找把Monkey Patch(不改变初始源代码来扩展和修改动态语言运行代码的方法)、Set(属性或索引器元素赋值的“访问器”方法)、SetAttr(SetAttr 语句可以为一个文件设置属性信息)等解决办法移植到我虚构的Python编译器里,因为类型标记严重地降低了运行性能。
快速的数据结构对于内存访问模式和缓存位置是非常重要的,还可以减少分支和对这些分支的标记工作。
然后在 《为什么我们要学习Python?》 这个条目中,又谈到了Python语言的缺点。Python 语言唯一的不足是性能问题。
为什么 Python 会有这样的性能问题呢?下面是一篇老外的文章,解释了这个问题。
静态类型语言中,在声明变量时已经指定了数据类型和表示方法。动态类型语言是在运行期间检查数据的类型,不得不保持描述变量值的实际类型标记,程序在每次操作变量时,需要执行数据依赖分支。
而间接分支(Indirect branch)和数据局部性(data locality)对于运行时的性能是致命的。
这就是动态语言的JIT编译器基准测试要强调near-C的内循环速度,以及避免大的数据结构和数据处理问题的原因。
我也希望像 Python 这样的动态类型语言可以变快。我试过用 Python 来进行传统的服务器编程——“系统语言”领域——但是效果真的不好,我现在考虑用 Java 重写一个服务器。
因此,我花时间思考如何真正静态地编译Python。毕竟,那是我梦寐以求的编程语言!但当我思考如何将动态类型代码与静态编译Python结合起来时,我遇到了数据变慢的问题:
在动态语言中,通常所有数组中的元素(或其他数据结构)类型各不相同,所以有不同的表示值。因此,这些值都必须被单独存放为堆,而不是顺序地存为数组。这意味着如果对不相邻的内存执行数据依赖分支,则对缓存有更高的要求。
也有一些聪明的技巧,使用变量中的特殊bit,将一些原生类型(像整型)打包成一个类型,类似于指针,但这要求寄存器在操作过程中进行跟踪,会增加开销。
还有一些方法,比如使用JIT编译热路径(hot path)时,如果你直接插入没有标类型的值,而不是在堆里分别标记类型, 那么与JIT编译过的代码的互操作性会降低,如果其他代码改变了数组中的一个值的类型,就会出现非常严重的后果。
我一直在思考,在Python语言中,什么是静态的,什么不是。通过SSTA(统计静态分析)和逃逸分析可以判断,大量正常的程序是静态的。Paul Biggar给了我信心证实我的猜测是正确的,我的Python代码90%都是静态的。
有人会问,那另外的10%呢?通常情况下,我可以让所有的都是静态的,或者想象它被参数类型的限制范围特殊化了。除了Python语言的标准模 式,其他模式都由Web服务器分配给基于HTTP方法(如果收到GET请求就称为“get”方法)的Web处理器,这也需要程序员依照switch语句 (如elifs的长链)来进行修改。
Robert Harper对“从单一类型静态语言方面,动态语言是如何实现的”这个问题作出了很好的解释,下面这句话是我希望他能进一步进行解释的:
我深知“编译器可以优化它”,至少在某些情况下。
我确信他说的“某些情况”是指遇到non-escaping的情况,因为和后面的执行代码进行交互时,你应该要能够确定escape的类型。
一些动态调用是无污染的——编译器可以从代码检查中发现一些变量(或方法)是动态的,但动态的代码不表示其他变量也是动态的,因为不同类型的变量、方法、成员的存在或缺失都被限制成了可识别的类型(或null)。
但通常编译器是无法从代码检查中发现这些情况的,如果无法追踪到执行的情况,就无法知道代码如何依赖以及如何改变其他静态变量的值。因此,工作中断,所有变量再次变为动态的。
我一直在努力寻找把Monkey Patch(不改变初始源代码来扩展和修改动态语言运行代码的方法)、Set(属性或索引器元素赋值的“访问器”方法)、SetAttr(SetAttr 语句可以为一个文件设置属性信息)等解决办法移植到我虚构的Python编译器里,因为类型标记严重地降低了运行性能。
快速的数据结构对于内存访问模式和缓存位置是非常重要的,还可以减少分支和对这些分支的标记工作。
相关文章推荐
- 为什么动态类型语言相对比较慢?
- python 执行系统命令比较
- 为什么学习Python
- Python3为什么不集成mysql支持,而要用第三方的库
- 使用Python和vim插件结合让Vim支持多文件夹比较
- python语法[global与nonlocal比较]
- Python 中Python 为什么要继承 object 类
- 《无价:洞悉大众心理玩转价格游戏》:消费者对绝对价格不敏感,对相对价格比较敏感
- 为什么说 iPhone 是相对更安全的手机之一?
- OpenCV(C++ 与 Python 的比较)与 MATLAB 的比较
- Python中的相对文件路径的调用
- Python 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
- Python @classmethod 和 @staticmethod的比较
- Lua 的速度为什么比 Python 快?
- python的list,dict,tuple比较和应用
- 软件比较 - Sniffer、Omnipeek、科来网络分析系统过滤器比较之位过滤 在捕获数据包时,有时候需要对一个字节中的某一个位进行精确匹配,这时,我们就需要用到位过滤。位过滤相对于地址、端口、协
- Python爬虫之三种网页抓取方法性能比较
- Redis等缓存数据库为什么访问会比较快?
- 数百种编程语言,而我为什么要学 Python?
- 为什么有这么多 Python?