知乎自己在底层造了非常多的轮子,而且也在服务器部署方面和数据获取方面广泛使用 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 的运用和带来的思考的更多相关文章

  1. python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比

    python3下multiprocessing.threading和gevent性能对比----暨进程池.线程池和协程池性能对比   标签: python3 / 线程池 / multiprocessi ...

  2. {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二

    python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ...

  3. 并发编程 - 协程 - 1.协程概念/2.greenlet模块/3.gevent模块/4.gevent实现并发的套接字通信

    1.协程并发:切+保存状态单线程下实现并发:协程 切+ 保存状态 yield 遇到io切,提高效率 遇到计算切,并没有提高效率 检测单线程下 IO行为 io阻塞 切 相当于骗操作系统 一直处于计算协程 ...

  4. python全栈开发,Day43(引子,协程介绍,Greenlet模块,Gevent模块,Gevent之同步与异步)

    昨日内容回顾 I/O模型,面试会问道 I/O操作,不占用CPU,它内部有一个专门的处理I/O模块 print和写log属于I/O操作,它不占用CPU 线程 GIL保证一个进程中的多个线程在同一时刻只有 ...

  5. gevent调度流程解析

    gevent是目前应用非常广泛的网络库,高效的轮询IO库libev加上协程(coroutine),使得gevent的性能非常出色,尤其是在web应用中.本文介绍gevent的调度流程,主要包括geve ...

  6. Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块

    一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...

  7. 协程--gevent模块(单线程高并发)

    先恶补一下知识点,上节回顾 上下文切换:当CPU从执行一个线程切换到执行另外一个线程的时候,它需要先存储当前线程的本地的数据,程序指针等,然后载入另一个线程的本地数据,程序指针等,最后才开始执行.这种 ...

  8. python gevent 协程

    简介 没有切换开销.因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高, 不需要锁机制.因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断 ...

  9. Python自动化之select、greenlet和gevent和事件驱动模型初探

    进程.线程和协程的区别 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度. 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的). 协程和线程一样 ...

随机推荐

  1. 定时器 setTimeout()超时调用和 setInterval()间歇调用

    JavaScript是单线程语言,但它允许通过设置定时器,也就是设置超时值和间歇时间来调度代码在特定的时刻执行.前者是在指定的时间过后执行代码,而后者则是每隔指定的时间就执行一次代码. 超时调用需要使 ...

  2. Python 特殊函数解析(lambda 函数,map 函数,filter 函数,reduce 函数)

    写在之前 今天给大家介绍几个比较特殊的函数,他们具有函数式编程的特点,有人将它们视为 Python 可进行 「函数式编程」 的见证,至于什么是函数式编程,不是本篇文章的重点,感兴趣的可以去了解一下.老 ...

  3. 关于box-shadow和drop-shadow的显著区别

    一.box-shadow box-shadow是css3中新增的属性,用于增加边框阴影,让原有的元素变得更多样性,它名下有四位小弟,老大控制水平方向偏移,老二控制垂直方向偏移,老三控制模糊度,最小的老 ...

  4. ActiveMQ安装配置及使用 转发 https://www.cnblogs.com/hushaojun/p/6016709.html

    ActiveMQ安装配置及使用 ActiveMQ介绍 ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线.ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JM ...

  5. Linux中查看你的用户是否为root用户

    可以使用sudo -l命令: user@fafsf:/opt/user$ sudo -l [sudo] password for user: //这里是要输入你的密码 Sorry, user user ...

  6. ORA-28040: No matching authentication protocol

    1.2  前言部分 1.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 告警日志中频繁出现Using depr ...

  7. mysql概述

    MySql大致分为三层结构: 第一层:客户端并非MySql所独有,例如:连接处理.授权认证.安全等功能均在这一层处理 第二层:核心服务包括查询解析.分析.优化.缓存.内置函数(比如 : 时间.数学.加 ...

  8. [BZOJ 3709] Bohater

    Description 在一款电脑游戏中,你需要打败n只怪物(从1到n编号).为了打败第i只怪物,你需要消耗d[i]点生命值,但怪物死后会掉落血药,使你恢复a[i]点生命值.任何时候你的生命值都不能降 ...

  9. 从零开始搭建django前后端分离项目 系列六(实战之聚类分析)

    项目需求 本项目从impala获取到的数据为用户地理位置数据,每小时的数据量大概在8000万条,数据格式如下: 公司要求对这些用户按照聚集程度进行划分,将300米范围内用户数大于200的用户划分为一个 ...

  10. 【原创】一个线程oom,进程里其他线程还能运行吗?

    引言 这题是一个网友@大脸猫爱吃鱼给我的提问,出自今年校招美团三面的一个真题.大致如下 一个进程有3个线程,如果一个线程抛出oom,其他两个线程还能运行么? 先说一下答案,答案是还能运行 不瞒大家说, ...