python几种并发实现方案的性能比较.docx
- 文档编号:12942073
- 上传时间:2023-04-22
- 格式:DOCX
- 页数:21
- 大小:22.69KB
python几种并发实现方案的性能比较.docx
《python几种并发实现方案的性能比较.docx》由会员分享,可在线阅读,更多相关《python几种并发实现方案的性能比较.docx(21页珍藏版)》请在冰豆网上搜索。
python几种并发实现方案的性能比较
转自:
1.前言
偶然看到Erlangvs.Stacklesspython:
afirstbenchmark,对Erlang和StacklessPython的并发处理性能进展了实验比较,根本结论认为二者有比较相近的性能。
我看完产生的问题是,StacklessPython与Python的其他并发实现机制性能又会有多大区别呢,比方线程和进程。
因此我采用与这篇文章一样的方法来对StacklessPython、普通Python的thread模块、普通Python的threading模块、普通Python的processing模块这四种并发实现方案进展了性能实验,并将实验过程和根本结果记录在这里。
后来看到了基于greenlet实现的高性能网络框架Eventlet,因此更新了实验方案,将greenlet也参加了比较,虽然greenlet并非是一种真正意义上的并发处理,而是在单个线程下对程序块进展切换轮流执行。
(EditSection↓)
2.实验方案
实验方案与Erlangvs.Stacklesspython:
afirstbenchmark是一样的,用每种方案分别给出如下问题的实现,记录完成整个处理过程的总时间来作为评判性能的根据:
1.由n个节点组成一个环状网络,在上面传送共m个消息。
2.将每个消息〔共m个〕,逐个发送给1号节点。
3.第1到n-1号节点在接收到消息后,都转发给下一号节点。
4.第n号节点每次收到消息后,不再继续转发。
5.当m个消息都从1号逐个到达第n号节点时,认为全部处理完毕。
(EditSection↓)
硬件平台
MacbookPro3,1上的Vmware虚拟机中,注意这里给虚拟机只启用了cpu的单个核心:
∙原始Cpu:
Core2Duo,2.4GHz,2核心,4MBL2缓存,总线速度800MHz
∙分配给虚拟机的内存:
796M
〔单个CPU,还能比较并发吗?
〕
(EditSection↓)
软件平台
Vmware下的Debianetch:
∙原始Python:
Debian发行版自带
∙Stackless3.1b3060516
∙processing-0.52-py2.4-linux-i686.egg
∙原始Python下的greenlet实现:
(EditSection↓)
3.实验过程及结果
各方案的实现代码见后文。
实验时使用time指令记录每次运行的总时间,选用的都是不做任何输出的no_io实现〔Python的print指令还是挺耗资源的,假设不注释掉十有八九得影响测试结果〕,每次执行时设定n=300,m=10000〔Erlangvs.Stacklesspython:
afirstbenchmark文章中认为n可以设置为300,m那么可以取10000到90000之间的数值分别进展测试〕。
(EditSection↓)
StacklessPython的实验结果
real
user
sys
即使将m扩大到30000,实验结果仍然很突出:
real
user
sys
(EditSection↓)
使用thread模块的实验结果
real
user
sys
(EditSection↓)
使用threading模块配合Queue模块的实验结果
不太稳定,有时候这样:
real
user
sys
也有时这样:
real
user
sys
(EditSection↓)
使用processing模块配合Queue模块的实验结果
real
user
sys
(EditSection↓)
greenlet模块的实验结果
real
user
sys
(EditSection↓)
eventlet模块的实验结果
注意!
eventlet的这个实验结果是后来增补的,硬件平台没变,但是是直接在OSX自带Python2.5环境下执行出来的,同时系统中还有Firefox等很多程序也在争夺系统资源。
因此只能作为大致参考,不能与其他几组数据作直接比照。
〔其中eventlet的版本是〕
(EditSection↓)
4.结论与分析
(EditSection↓)
StacklessPython
毫无疑问,StacklessPython几乎有匪夷所思的并发性能,比其他方案快上几十倍,而且借助StacklessPython提供的channel机制,实现也相当简单。
也许这个结果向我们部分提醒了沈仙人基于StacklessPython实现的Eurasia3可以提供相当于c语言效果的恐惧并发性能的原因。
(EditSection↓)
Python线程
从道理上来讲,thread模块似乎应该和threading提供根本一样的性能,毕竟threading只是对thread的一种封装嘛,后台机制应该是一致的。
或许threading由于本身类实例维护方面的开销,应该会比直接用thread慢一点。
从实验结果来看,二者性能也确实差不多。
只是不大明白为何threading方案的测试结果不是很稳定,即使对其他方案的测试运行屡次,误差也不会像threading这么飘。
从代码实现体验来说,用threading配合Queue比直接用thread实在是轻松太多了,并且出错的时机也要少很多。
(EditSection↓)
Python进程
processing模块给出的进程方案大致比thread线程要慢一倍,并且这是在我特意调整虚拟机给它预备了足够空闲内存、防止使用交换分区的情况下获得的〔特意分给虚拟机700多M内存就是为了这个〕。
而其他方案仅仅占用数M内存,完全无需特意调大可用内存总量。
当然,假设给虚拟机多启用几个核心的话,processing也许会占上点廉价,毕竟目前thread模块是不能有效利用多cpu资源的〔经实验,StacklessPython在开启双核的情况下表现的性能和单核是一样的,说明也是不能有效利用多cpu〕。
因此一种比较合理的做法是根据cpu的数量,启用少量几个进程,而在进程内部再开启线程进展实际业务处理,这也是目前Python社区推荐的有效利用多cpu资源的方法。
好在processing配合其自身提供的Queue模块,编程体验还是比较轻松的。
(EditSection↓)
greenlet超轻量级方案
基于greenlet的实现那么性能仅次于StacklessPython,大致比StacklessPython慢一倍,比其他方案快接近一个数量级。
其实greenlet不是一种真正的并发机制,而是在同一线程内,在不同函数的执行代码块之间切换,施行“你运行一会、我运行一会〞,并且在进展切换时必须指定何时切换以及切换到哪。
greenlet的接口是比较简单易用的,但是使用greenlet时的考虑方式与其他并发方案存在一定区别。
线程/进程模型在大逻辑上通常从并发角度开始考虑,把可以并行处理的并且值得并行处理的任务别离出来,在不同的线程/进程下运行,然后考虑别离过程可能造成哪些互斥、冲突问题,将互斥的资源加锁保护来保证并发处理的正确性。
greenlet那么是要求从防止阻塞的角度来进展开发,当出现阻塞时,就显式切换到另一段没有被阻塞的代码段执行,直到原先的阻塞状况消失以后,再人工切换回原来的代码段继续处理。
因此,greenlet本质是一种合理安排了的串行,实验中greenlet方案可以得到比较好的性能表现,主要也是因为通过合理的代码执行流程切换,完全防止了死锁和阻塞等情况〔执行带屏幕输出的我们会看到脚本总是一个一个地处理消息,把一个消息在环上从头传到尾之后,再开始处理下一个消息〕。
因为greenlet本质是串行,因此在没有进展显式切换时,代码的其他部分是无法被执行到的,假设要防止代码长时间占用运算资源造成程序假死,那么还是要将greenlet与线程/进程机制结合使用〔每个线程、进程下都可以建立多个greenlet,但是跨线程/进程时greenlet之间无法切换或通讯〕。
Stackless那么比较特别,对很多资源从底层进展了并发改造,并且提供了channel等更适宜“并发〞的通讯机制实现,使得资源互斥冲突的可能性大大减小,并发性能自然得以进步。
粗糙来讲,greenlet是“阻塞了我就先干点儿别的,但是程序员得明确告诉greenlet能先干点儿啥以及什么时候回来〞;Stackless那么是“东西我已经改造好了,你只要用我的东西,并发冲突就不用操心,只管放心大胆地并发好了〞。
greenlet应该是学习了Stackless的上下文切换机制,但是对底层资源没有进展适宜并发的改造。
并且实际上greenlet也没有必要改造底层资源的并发性,因为它本质是串行的单线程,不与其他并发模型混合使用的话是无法造成对资源的并发访问的。
(EditSection↓)
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模型的并发性能虽然比StacklessPython和直接使用greenlet有一定差距,但仍然比标准线程模型有大约一个数量级的优势,这也就不奇怪近期很多强调并发性能的网络效劳器实现采取eventlet、线程、进程三者组合使用的实现方案。
(EditSection↓)
5.实验代码
实验代码下载:
∙版本3下载:
增加了eventlet方案的实验代码。
∙版本2下载:
增加了greenlet方案的实验代码。
∙版本1下载:
包括StacklessPython、thread、threading、processing四种方案的实验代码。
为方便阅读,将实验中用到的几个脚本的代码粘贴如下,其中StacklessPython方案的代码实现直接取自Erlangvs.Stacklesspython:
afirstbenchmark:
(EditSection↓)
1.#!
/Library/Frameworks/
2.#encoding:
utf-8
3.importsys
4.importstacklessasSL
5.
6.defrun_benchmark(n,m):
7. stackless3.1b3here(N=%d,M=%d)!
\n"%(n,m))
8. firstP=cin=()
9. forsinxrange(1,n):
10. seqn=s
11. cout=()
12. ##print("*>s=%d"%(seqn,))
13. t=(loop)(seqn,cin,cout)
14. cin=cout
15. else:
16. seqn=s+1
17. ##print("$>s=%d"%(seqn,))
18. t=(mloop)(seqn,cin)
19. forrinxrange(m-1,-1,-1):
20. ##print("+sendingMsg# %d"%r)
21. (r)
22. ()
23.defloop(s,cin,cout):
24. whileTrue:
25. r=()
26. (r)
27. ifr>0:
28. #print(":
Proc:
<%s>,Seq#:
%s,Msg#:
%s.."%(pid(),s,r))
29. pass
30. else:
31. #print("*Proc:
<%s>,Seq#:
%s,Msg#:
terminate!
"%(pid(),s))
32. break
33.defmloop(s,cin):
34. whileTrue:
35. r=()
36. ifr>0:
37. #print(">Proc:
<%s>,Seq#:
%s,Msg#:
%s.."%(pid(),s,r))
38. pass
39. else:
40. #print("@Proc:
<%s>,Seq#:
%s,ringterminated."%(pid(),s))
41. break
42.
43.defpid():
returnrepr(()).split()[-1][2:
-1]
44.
45.if__name__=='__main__':
46. run_benchmark(int(sys.argv[1]),int(sys.argv[2]))
[GetCode]
(EditSection↓)
1.#!
/Library/Frameworks/
2.#encoding:
utf-8
3.importsys,time
4.importthread
5.
6.SLEEP_TIME=
7.
8.defrun_benchmark(n,m):
9. stackless3.1b3here(N=%d,M=%d)!
\n"%(n,m))
10. locks=[thread.allocate_lock()foriinxrange(n)]
11. firstP=cin=[]
12. cin_lock_id=0
13. forsinxrange(1,n):
14. seqn=s
15. cout=[]
16. cout_lock_id=s
17. #print("*>s=%d"%(seqn,))
18. thread.start_new_thread(loop,(seqn,locks,cin,cin_lock_id,cout,cout_lock_id))
19. cin=cout
20. cin_lock_id=cout_lock_id
21. else:
22. seqn=s+1
23. #print("$>s=%d"%(seqn,))
24. thread.start_new_thread(mloop,(seqn,locks,cin,cin_lock_id))
25. forrinxrange(m-1,-1,-1):
26. #print("+sendingMsg# %d"%r)
27. lock=locks[0]
28. ()
29. (r)
30. ()
31. time.sleep(SLEEP_TIME)
32. try:
33. whileTrue:
34. time.sleep(SLEEP_TIME)
35. except:
36. pass
37.defloop(s,locks,cin,cin_lock_id,cout,cout_lock_id):
38. whileTrue:
39. lock=locks[cin_lock_id]
40. ()
41. iflen(cin)>0:
42. r=cin.pop(0)
43. ()
44. else:
45. ()
46. time.sleep(SLEEP_TIME)
47. continue
48. lock=locks[cout_lock_id]
49. ()
50. (r)
51. ()
52. ifr>0:
53. #print(":
Proc:
<%s>,Seq#:
%s,Msg#:
%s.."%(pid(),s,r))
54. pass
55. else:
56. #print("*Proc:
<%s>,Seq#:
%s,Msg#:
terminate!
"%(pid(),s))
57. break
58.defmloop(s,locks,cin,cin_lock_id):
59. whileTrue:
60. lock=locks[cin_lock_id]
61. ()
62. iflen(cin)>0:
63. r=cin.pop(0)
64. ()
65. else:
66. ()
67. time.sleep(SLEEP_TIME)
68. continue
69. ifr>0:
70. #print(">Proc:
<%s>,Seq#:
%s,Msg#:
%s.."%(pid(),s,r))
71. pass
72. else:
73. #print("@Proc:
<%s>,Seq#:
%s,ringterminated."%(pid(),s))
74. break
75. thread.interrupt_main()
76.
77.defpid():
returnthread.get_ident()
78.
79.if__name__=='__main__':
80. run_benchmark(int(sys.argv[1]),int(sys.argv[2]))
[GetCode]
(EditSection↓)
1.#!
/Library/Frameworks/
2.#encoding:
utf-8
3.importsys
4.importthreading,Queue
5.
6.defrun_benchmark(n,m):
7. stackless3.1b3here(N=%d,M=%d)!
\n"%(n,m))
8. firstP=cin=Queue.Queue()
9. forsinxrange(1,n):
10. seqn=s
11. cout=Queue.Queue()
12. #print("*>s=%d"%(seqn,))
13. t=Loop(seqn,cin,cout)
14. (False)
15. ()
16. cin=cout
17. else:
18. seqn=s+1
19. #print("$>s=%d"%(seqn,))
20. t=MLoop(seqn,cin)
21. (False)
22. ()
23. forrinxrange(m-1,-1,-1):
24. #print("+sendingMsg# %d"%r)
25. (r)
26.classLoop(threading.Thread):
27. def__init__(self,s,cin,cout):
28. threading.Thread.__init__(self)
29. self.cin=cin
30. self.cout=cout
31. self.s=s
32. defrun(self):
33. whileTrue:
34. r=self()
35. self(r)
36. if
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- python 并发 实现 方案 性能 比较
![提示](https://static.bdocx.com/images/bang_tan.gif)