Gevent 性能和 gevent.loop 的运用和带来的思考
知乎自己在底层造了非常多的轮子,而且也在服务器部署方面和数据获取方面广泛使用 gevent 来提高并发获取数据的能力。现在开始我将结合实际使用与测试慢慢完善自己对 gevent 更全面的使用和扫盲。
在对 gevent loop 的使用上,gevent tutorial 介绍得非常敷衍,以至于完全不知道他的使用办法。这里我将结合 timeit 测试更详细的介绍一下 gevnet.loop 的使用。以及他的父类 Group 的使用。
其实在使用 gevent 上面我个人一直有一个误区,就是我使用并发的 gevent 一定比我平时线性的操作速度更快,其实不是这样。让我们来看一个例子:
import timeit
import gevent
def task1():
pass def task():
for i in range(50):
pass def async():
x = gevent.spawn(task)
x.join() def sync():
for i in range(50):
task1() print timeit.timeit(stmt=async, setup='''
from __main__ import task, async, sync
''', number=1000)
print '同步开始了'
print timeit.timeit(stmt=sync, setup='''
from __main__ import task, async, sync
''', number=1000) output:
0.0216090679169
同步开始了
0.00430107116699
可以看到,我们同样跑一样的函数调用,如果使用 gevent.spawn 一个调用,我们会话费更多的资源,这导致了我们甚至没有线性完成得快。你可能会说,这是当然了,因为这里只 spwan 了一个 gevent 的 greenlet 实例。如果我们调用多个呢?
import timeit
import gevent def async1():
p = []
for i in range(50):
p.append(gevent.spawn(task1))
gevent.joinall(p) def task1():
pass def sync():
for i in range(50):
task1() print timeit.timeit(stmt=async1, setup='''
from __main__ import task, async1, sync
''', number=1000)
print '同步开始了'
print timeit.timeit(stmt=sync, setup='''
from __main__ import task, async1, sync
''', number=1000) output:
1.21793103218
同步开始了
0.0048680305481
情况似乎变得更糟糕了。。。。我们同时 spawn 了 50个 greenlet 实例实图一次性搞定这个事情,但是速度甚至变得更慢了。由此我们可以得出一个结论,也许在并不是在网络请求或者需要等待切换的情况下,使用 gevent 也许不是一个很好的解决方案。
那到底种情况可以使我们的性能获得巨大的提升?来看这个例子:
import timeit
import gevent def async1():
p = []
for i in range(50):
p.append(gevent.spawn(task1))
gevent.joinall(p) def task1():
gevent.sleep(0.001) def sync():
for i in range(50):
task1() print timeit.timeit(stmt=async1, setup='''
from __main__ import task1, async1, sync
''', number=100)
print '同步开始了'
print timeit.timeit(stmt=sync, setup='''
from __main__ import task1, async1, sync
''', number=100) output:
0.25629901886
同步开始了
6.91364789009
可以看出来,这次我 spawn 50个一起跑,就远远快于线性了。因为在线性的情况下,我们每次都会在 task1 任务运行的时候阻塞 0.001s, 但是 gevent 使得 async 函数几乎不受等待影响。非常快速的解决了这个问题。其实这个环境在我们进行网络 io 的时候非常常见。比如我们向某个地址下载图片,如果我们线性下载图片,我们需要等待第一张图片下载完成之后才能进行第二张图片的下载,但是我们使用 gevent 并发下载图片,我们可以先开始下载图片,然后在等待的时候切换到别的任务继续进行下载。当下载完毕之后我们会切换回来完成下载。不用等待任何一个任务下载完成,大大的提高了效率。
gevent 的 pool 函数可以控制并发的时候最多使用 greenlet 的数量。 这里我循环了50次,但是当我们在进行 io 的时候,我们设置了 1w 次,那么也会起 10000 个协程来运行这个程序,对于性能我们是不知道的。有可能会直接堵死服务器端,所以我们需要对此进行控制,我们限制最多同时使用 20 个 greenlet 实例进行处理,当有任务完成之后我们再开始别的任务,更好的控制我们的请求以及维护相当的效率让我们来看几个数据:
开 10个 greenlet 的情况
import timeit
import gevent
from gevent.pool import Pool x = Pool(40) def async1():
for i in range(50):
x.spawn(task1)
x.join() def task1():
gevent.sleep(0.001) def sync():
for i in range(50):
task1() print timeit.timeit(stmt=async1, setup='''
from __main__ import task1, async1, sync
''', number=100)
print '同步开始了'
print timeit.timeit(stmt=sync, setup='''
from __main__ import task1, async1, sync
''', number=100) output:
0.813331842422
同步开始了
6.89506411552
开 40 个实例的情况:
0.366757154465
同步开始了
6.78097295761
开80 个实例的情况:
0.222685098648
同步开始了
6.77246403694
开10000个的情况:
0.227874994278
同步开始了
6.81039714813
可以看到当我们超过阀值之后,开更多的实例已经没有任何意义了。而且有可能还造成一些性能上的浪费,所以选择一个合适的实例数量即可。
另外还有一个速度更快的函数可以提供使用:
def async1():
for i in range(50):
x.imap(task1)
官方文档上还有一句话,就是如果对出的结果并不要求顺序的话可以使用imap_unordered,速度更快:
def async1():
for i in range(50):
x.imap_unordered(task1)
pool饱和的情况下 上面的例子差不多只要 0.8s 就能处理完,imap 需要1s。使用join需要 0.22s。
Reference:
http://hhkbp2.github.io/gevent-tutorial/#_8 gevent-tutorial
Gevent 性能和 gevent.loop 的运用和带来的思考的更多相关文章
- python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比
python3下multiprocessing.threading和gevent性能对比----暨进程池.线程池和协程池性能对比 标签: python3 / 线程池 / multiprocessi ...
- {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二
python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ...
- 并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信
1.协程并发:切+保存状态单线程下实现并发:协程 切+ 保存状态 yield 遇到io切,提高效率 遇到计算切,并没有提高效率 检测单线程下 IO行为 io阻塞 切 相当于骗操作系统 一直处于计算协程 ...
- python全栈开发,Day43(引子,协程介绍,Greenlet模块,Gevent模块,Gevent之同步与异步)
昨日内容回顾 I/O模型,面试会问道 I/O操作,不占用CPU,它内部有一个专门的处理I/O模块 print和写log属于I/O操作,它不占用CPU 线程 GIL保证一个进程中的多个线程在同一时刻只有 ...
- gevent调度流程解析
gevent是目前应用非常广泛的网络库,高效的轮询IO库libev加上协程(coroutine),使得gevent的性能非常出色,尤其是在web应用中.本文介绍gevent的调度流程,主要包括geve ...
- Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块
一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...
- 协程--gevent模块(单线程高并发)
先恶补一下知识点,上节回顾 上下文切换:当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行.这种 ...
- python gevent 协程
简介 没有切换开销.因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高, 不需要锁机制.因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断 ...
- Python自动化之select、greenlet和gevent和事件驱动模型初探
进程.线程和协程的区别 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度. 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的). 协程和线程一样 ...
随机推荐
- centos7下安装docker(15.7容器跨主机网络---calico)
Calico是一个纯三层的虚拟网络方案,Calico为每个容器分配一个IP,每个host都是router,把不同host的容器连接起来.与vxlan不同的是:calico不对数据包进行封装,不需要NA ...
- hive并行执行作业; 强化在脑海的印象
如果集群资源充足可以设置:set hive.exec.parallel=true; (默认是false) 这样相互独立的job可以并行执行!!!! count(distinct) 最好改写为group ...
- centos7 mongodb安装
参考文档 http://www.runoob.com/mongodb/mongodb-connections.html https://www.cnblogs.com/layezi/p/7290082 ...
- Java 定义静态list
private final static List<String> imgList = Arrays.asList("group1/M00/0B/B0/rBGl-lvr7vWAN ...
- java kafka单列模式生产者客户端
1.所需要的依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...
- element not interactable,这种提示表示元素当前在页面上不可见
1.出现element not interactable,发现这个元素在页面上不可见,需要拖动下拉框才能看到这个元素 2.这个时候需要让元素在页面上可见,才可操作
- PAT A1095 Cars on Campus (30 分)——排序,时序,从头遍历会超时
Zhejiang University has 8 campuses and a lot of gates. From each gate we can collect the in/out time ...
- Centos7 安装配置mysql5.6
Centos7下完美安装并配置mysql5.6 Centos7将默认数据库mysql替换成了Mariadb,对于我们这些还想用mysql的人来说并不是一个好消息. 最近我搜罗了网上各种安装教程,各 ...
- Scripts may close only the windows that were opened by it
关闭当前窗体报以下js错误: Scripts may close only the windows that were opened by it (脚本只能关闭由它打开的窗口) 使用场景,在js中关闭 ...
- 震惊!!!python可以用中文来写代码
python可以用中文来写代码 说明: 偶尔间试了一下,python可以用中文来写代码,除了一些python内置函数,和运算符不能用中文外,其它的比如新定义的类名.函数名.变量名,甚至是函数间传的参数 ...