`

【转】Python几种并发实现方案的性能比较

 
阅读更多
http://www.cnblogs.com/sevenyuan/archive/2010/12/08/1900386.html



Python几种并发实现方案的性能比较


转自:http://www.elias.cn/Python/PyConcurrency
1.  前言

偶然看到Erlang vs. Stackless python: a first benchmark,对Erlang和Stackless Python的并发处理性能进行了实验比较,基本结论认为二者有比较相近的性能。我看完产生的问题是,Stackless Python与Python的其他并发实现机制性能又会有多大区别呢,比如线程和进程。因此我采用与这篇文章相同的办法来对Stackless Python、普通Python的thread模块、普通Python的threading模块、普通Python的processing模块这四种并发实现方案进行了性能实验,并将实验过程和基本结果记录在这里。

后来看到了基于greenlet实现的高性能网络框架Eventlet,因而更新了实验方案,将greenlet也加入了比较,虽然greenlet并非是一种真正意义上的并发处理,而是在单个线程下对程序块进行切换轮流执行。

2.  实验方案

实验方案与Erlang vs. Stackless python: a first benchmark是相同的,用每种方案分别给出如下问题的实现,记录完成整个处理过程的总时间来作为评判性能的依据:

由n个节点组成一个环状网络,在上面传送共m个消息。
将每个消息(共m个),逐个发送给1号节点。
第1到n-1号节点在接收到消息后,都转发给下一号节点。
第n号节点每次收到消息后,不再继续转发。
当m个消息都从1号逐个到达第n号节点时,认为全部处理结束。
2.1  硬件平台

Macbook Pro 3,1上的Vmware Fusion 1.0虚拟机中,注意这里给虚拟机只启用了cpu的单个核心:

原始Cpu:Core 2 Duo,2.4 GHz,2核心,4 MB L2 缓存,总线速度800 MHz
分配给虚拟机的内存:796M
2.2  软件平台

Vmware Fusion 1.0下的Debian etch:

原始Python:Debian发行版自带Python 2.4.4
Python 2.4.4 Stackless 3.1b3 060516
processing-0.52-py2.4-linux-i686.egg
原始Python下的greenlet实现:py lib 0.9.2
3.  实验过程及结果

各方案的实现代码见后文。实验时使用time指令记录每次运行的总时间,选用的都是不做任何输出的no_io实现(Python的print指令还是挺耗资源的,如果不注释掉十有八九得影响测试结果),每次执行时设定n=300,m=10000(Erlang vs. Stackless python: a first benchmark文章中认为n可以设置为300,m则可以取10000到90000之间的数值分别进行测试)。

3.1  Stackless Python的实验结果

real0m1.651s
user0m1.628s
sys0m0.020s
即使将m扩大到30000,实验结果仍然很突出:

real0m4.749s
user0m4.716s
sys0m0.028s
3.2  使用thread模块的实验结果

real1m13.009s
user0m2.476s
sys0m59.028s
3.3  使用threading模块配合Queue模块的实验结果

不太稳定,有时候这样:

real1m9.222s
user0m34.418s
sys0m34.622s
也有时这样:

real2m14.016s
user0m6.644s
sys2m7.260s
3.4  使用processing模块配合Queue模块的实验结果

real3m43.539s
user0m15.345s
sys3m27.953s
3.5  greenlet模块的实验结果

real0m9.225s
user0m0.644s
sys0m8.581s
3.6  eventlet模块的实验结果

注意!eventlet 的这个实验结果是后来增补的,硬件平台没变,但是是直接在 OSX 自带 Python 2.5 环境下执行出来的,同时系统中还有 Firefox 等很多程序也在争夺系统资源。因此只能作为大致参考,不能与其他几组数据作直接对比。(其中 eventlet 的版本是 0.9.5)

real    0m21.610s
user    0m20.713s
sys     0m0.215s
4.  结论与分析

4.1  Stackless Python

毫无疑问,Stackless Python几乎有匪夷所思的并发性能,比其他方案快上几十倍,而且借助Stackless Python提供的channel机制,实现也相当简单。也许这个结果向我们部分揭示了沈仙人基于Stackless Python实现的Eurasia3能够提供相当于c语言效果的恐怖并发性能的原因。

4.2  Python线程

从道理上来讲,thread模块似乎应该和threading提供基本相同的性能,毕竟threading只是对thread的一种封装嘛,后台机制应该是一致的。或许threading由于本身类实例维护方面的开销,应该会比直接用thread慢一点。从实验结果来看,二者性能也确实差不多。只是不大明白为何threading方案的测试结果不是很稳定,即使对其他方案的测试运行多次,误差也不会像threading这么飘。从代码实现体验来说,用threading配合Queue比直接用thread实在是轻松太多了,并且出错的机会也要少很多。

4.3  Python进程

processing模块给出的进程方案大致比thread线程要慢一倍,并且这是在我特意调整虚拟机给它预备了足够空闲内存、避免使用交换分区的情况下取得的(特意分给虚拟机700多M内存就是为了这个)。而其他方案仅仅占用数M内存,完全无需特意调大可用内存总量。当然,如果给虚拟机多启用几个核心的话,processing也许会占上点便宜,毕竟目前thread模块是不能有效利用多cpu资源的(经实验,Stackless Python在开启双核的情况下表现的性能和单核是一样的,说明也是不能有效利用多cpu)。因此一种比较合理的做法是根据cpu的数量,启用少量几个进程,而在进程内部再开启线程进行实际业务处理,这也是目前Python社区推荐的有效利用多cpu资源的办法。好在processing配合其自身提供的Queue模块,编程体验还是比较轻松的。

4.4  greenlet超轻量级方案

基于greenlet的实现则性能仅次于Stackless Python,大致比Stackless Python慢一倍,比其他方案快接近一个数量级。其实greenlet不是一种真正的并发机制,而是在同一线程内,在不同函数的执行代码块之间切换,实施“你运行一会、我运行一会”,并且在进行切换时必须指定何时切换以及切换到哪。greenlet的接口是比较简单易用的,但是使用greenlet时的思考方式与其他并发方案存在一定区别。线程/进程模型在大逻辑上通常从并发角度开始考虑,把能够并行处理的并且值得并行处理的任务分离出来,在不同的线程/进程下运行,然后考虑分离过程可能造成哪些互斥、冲突问题,将互斥的资源加锁保护来保证并发处理的正确性。greenlet则是要求从避免阻塞的角度来进行开发,当出现阻塞时,就显式切换到另一段没有被阻塞的代码段执行,直到原先的阻塞状况消失以后,再人工切换回原来的代码段继续处理。因此,greenlet本质是一种合理安排了的串行,实验中greenlet方案能够得到比较好的性能表现,主要也是因为通过合理的代码执行流程切换,完全避免了死锁和阻塞等情况(执行带屏幕输出的ring_greenlet.py我们会看到脚本总是一个一个地处理消息,把一个消息在环上从头传到尾之后,再开始处理下一个消息)。因为greenlet本质是串行,因此在没有进行显式切换时,代码的其他部分是无法被执行到的,如果要避免代码长时间占用运算资源造成程序假死,那么还是要将greenlet与线程/进程机制结合使用(每个线程、进程下都可以建立多个greenlet,但是跨线程/进程时greenlet之间无法切换或通讯)。

Stackless则比较特别,对很多资源从底层进行了并发改造,并且提供了channel等更适合“并发”的通讯机制实现,使得资源互斥冲突的可能性大大减小,并发性能自然得以提高。粗糙来讲,greenlet是“阻塞了我就先干点儿别的,但是程序员得明确告诉greenlet能先干点儿啥以及什么时候回来”;Stackless则是“东西我已经改造好了,你只要用我的东西,并发冲突就不用操心,只管放心大胆地并发好了”。greenlet应该是学习了Stackless的上下文切换机制,但是对底层资源没有进行适合并发的改造。并且实际上greenlet也没有必要改造底层资源的并发性,因为它本质是串行的单线程,不与其他并发模型混合使用的话是无法造成对资源的并发访问的。

greenlet 封装后的 eventlet 方案

eventlet 是基于 greenlet 实现的面向网络应用的并发处理框架,提供“线程”池、队列等与其他 Python 线程、进程模型非常相似的 api,并且提供了对 Python 发行版自带库及其他模块的超轻量并发适应性调整方法,比直接使用 greenlet 要方便得多。并且这个解决方案源自著名虚拟现实游戏“第二人生”,可以说是久经考验的新兴并发处理模型。其基本原理是调整 Python 的 socket 调用,当发生阻塞时则切换到其他 greenlet 执行,这样来保证资源的有效利用。需要注意的是:

eventlet 提供的函数只能对 Python 代码中的 socket 调用进行处理,而不能对模块的 C 语言部分的 socket 调用进行修改。对后者这类模块,仍然需要把调用模块的代码封装在 Python 标准线程调用中,之后利用 eventlet 提供的适配器实现 eventlet 与标准线程之间的协作。
再有,虽然 eventlet 把 api 封装成了非常类似标准线程库的形式,但两者的实际并发执行流程仍然有明显区别。在没有出现 I/O 阻塞时,除非显式声明,否则当前正在执行的 eventlet 永远不会把 cpu 交给其他的 eventlet,而标准线程则是无论是否出现阻塞,总是由所有线程一起争夺运行资源。所有 eventlet 对 I/O 阻塞无关的大运算量耗时操作基本没有什么帮助。
在性能测试结果方面,eventlet 消耗的运行时间大致是 greenlet 方案的 3 到 5 倍,而 Python 标准线程模型的 thread 方式消耗的运行时间大致是 eventlet 测试代码的 8 到 10 倍。其中前者可能是因为我们在 eventlet 的测试代码中,使用队列机制来完成所有的消息传递,而队列上的访问互斥保护可能额外消耗了一些运算资源。总体而言,eventlet 模型的并发性能虽然比 Stackless Python 和直接使用 greenlet 有一定差距,但仍然比标准线程模型有大约一个数量级的优势,这也就不奇怪近期很多强调并发性能的网络服务器实现采取 eventlet 、线程、进程三者组合使用的实现方案。

5.  实验代码

实验代码下载:

版本3 下载:增加了 eventlet 方案的实验代码。
版本2 下载:增加了 greenlet 方案的实验代码。
版本1 下载:包括 Stackless Python 、 thread 、 threading 、 processing 四种方案的实验代码。
为方便阅读,将实验中用到的几个脚本的代码粘贴如下,其中Stackless Python方案的代码实现直接取自Erlang vs. Stackless python: a first benchmark:

5.1  ring_no_io_slp.py

代码
5.2  ring_no_io_thread.py
代码
5.3  ring_no_io_queue.py
代码
5.4  ring_no_io_proc.py
代码
5.5  ring_no_io_greenlet.py
代码
5.6  ring_no_io_eventlet.py
代码
分享到:
评论

相关推荐

    python几种并发实现方案的性能比较.docx

    ### Python几种并发实现方案的性能比较 #### 一、引言 Python作为一种广泛使用的高级编程语言,因其简洁优雅的语法和强大的库支持而备受青睐。然而,在涉及高性能计算和高并发场景的应用中,Python的标准解释器...

    Python高性能网络编程并发框架研究.zip

    在Python的世界里,高性能网络编程和并发框架是构建大规模、高效率网络服务的关键技术。本研究主要探讨了Python中的一些核心并发模型和框架,旨在帮助开发者理解和应用这些工具,以实现更高效的网络服务。 首先,...

    Python并发编程GPU

    Python中有几个库可以帮助我们实现这一点: 1. **NVIDIA CUDA**: CUDA是NVIDIA提供的一个平台,允许程序员直接使用C++或Python等高级语言编写GPU并行代码。在Python中,可以通过`pycuda`库访问CUDA API,实现GPU...

    基于并发服务器几种实现方法(总结)

    ### 基于并发服务器几种实现方法(总结) 在现代互联网技术中,为了处理大量并发请求,提升服务器响应效率及稳定性,开发人员通常会采用多种技术手段实现并发服务器。本文将详细探讨几种常见的并发服务器实现方法,...

    详解Python并发编程之从性能角度来初探并发编程

    对于并发编程,Python的实现,总结了一下,大致有如下三种方法: 多线程 多进程 协程(生成器) 在之后的章节里,将陆陆续续地给大家介绍到这三个知识点。 . 并发编程的基本概念 在开始讲解理论知识之前,先过...

    Python技术使用优化方案与实际案例探讨.docx

    为了解决上述问题,可以采取以下几种优化策略: 1. **算法优化** - 选择高效的数据结构和算法可以显著提高代码的执行效率。 - 对于数值计算密集型的应用,可以利用NumPy和SciPy等科学计算库,这些库内部使用C语言...

    Python常用类库.docx

    - **并发性能比较**: 分析了Python几种并发实现方案的性能差异。 - **Web应用平台对比**: 对比了不同Python Web应用框架,如web.py、web2py等。 - **wxPython下的图像处理**: 探讨了在wxPython环境下进行图像处理的...

    python重写C++

    - C++的多线程和并发在Python中通常用`threading`或`concurrent.futures`模块来实现,但需要注意GIL(全局解释器锁)对多线程性能的影响。 7. **学习和实践**: - 通过实践将C++代码转换为Python,可以深入理解两...

    python 2019毕业论文参考文献.docx

    - **详细内容**:文章总结了几种有效的教学方法,如案例驱动教学法、项目导向学习法等,并分享了一些具体的课堂实践案例。这对于提高学生编程兴趣和技能有很大帮助。 #### Python语言的中文文本处理 - **知识点...

    基于激励型需求响应(Python实现)

    本项目将深入探讨如何使用Python编程语言来实现这一机制。 首先,我们需要理解需求响应系统的基本组成部分。这通常包括数据采集、数据分析、预测模型以及用户激励算法。在Python中,我们可以利用各种库来处理这些...

    Python技术使用优化方案.docx

    Python是一种流行的编程语言,因其简洁的语法和强大的功能而受到开发者的喜爱。然而,Python的动态类型、解释执行和全局解释锁(GIL)可能导致性能瓶颈。为了优化Python程序,我们需要关注以下几个关键点: 1. **...

    Python-一个由python写简单和容易使用的消息队列

    这里我们将主要讨论基于Python实现的简单消息队列,可能是指一个自定义的轻量级解决方案,而非上述大型开源项目。 创建一个简单的Python消息队列可以分为以下几个步骤: 1. **设计数据结构**:首先,你需要确定...

    C++扩展和嵌入(python).pdf

    通过学习这本书,读者不仅可以掌握C++和Python之间的互操作性,还能了解如何最大化利用这两种语言的优势,构建高效且灵活的混合编程解决方案。这对于那些需要在Python环境中利用C++高性能特性的开发者来说,是非常...

    Python编程实战 运用设计模式、并发和程序库创建高质量程序

    Python中的并发编程主要包括多线程、多进程和协程等几种方式。 1. **多线程**:通过创建多个线程来并发执行任务。适用于I/O密集型任务,但需要注意线程安全问题。 2. **多进程**:利用Python的`multiprocessing`库...

    Python-MrQueue一个Python的分布式worker任务队列使用Redis和gevent

    总结来说,Python-MrQueue是一个强大的工具,它结合了Redis的高效消息传递和gevent的并发处理能力,为Python开发者提供了实现分布式任务队列的解决方案。通过使用这个框架,开发者可以构建出更加健壮、高效的后台...

    基于Python和SQL Server的实验室管理系统_python_数据库管理系统_

    【基于Python和SQL Server的实验室管理系统】是一种利用Python编程语言和SQL Server数据库技术构建的管理信息系统,主要用于高校或研究机构的实验室日常管理。系统通常包括图形用户界面(GUI),为不同角色如学生、...

    codon使python提速

    4. **多线程和并发**: Codon主要针对单线程性能优化,如果你的程序依赖多线程或者并发,可能需要其他工具或策略来提升整体性能。 5. **社区支持和更新**:持续关注 Codon的更新和社区反馈,以获取最新的性能提升和...

    Python库 | python_tables_lib-1.0.0-py3-none-any.whl

    2. **数据结构**:Python Tables提供了几种特定的数据结构,如Array、EArray(可扩展数组)、Table、CArray(压缩数组)和Group(类似目录的结构)。这些数据结构为不同类型的数据提供优化的存储和访问方案。 3. **...

Global site tag (gtag.js) - Google Analytics