python性能分析和优化,GIL常考题

什么是Cpython GIL

  • Cpython解释器的内存管理并不是线程安全的
  • 保护多线程情况下对python对象访问
  • Cpython使用简单的锁机制避免多个线程同时执行字节码

GIL的影响

限制了程序的多核执行

  • 同一个时间只能有一个线程执行字节码
  • CPU密集程序难以利用多核优势
  • IO期间会释放GIL,对IO密集程序影响不大

如何规避GIL影响和IO密集程序

  • CPU密集可以使用多进程+进程池
  • IO密集使用多线程/协程
  • Cpython扩展

为什么有了GIl还要关注线程安全

python中什么操作才是原子的?一步到位执行完

  • 一个操作如果是一个字节码指令可以完成就是原子的
  • 原子的是可以保证线程安全的
  • 使用dis操作来分析字节码

原子操作

import dis

def update_list(l):
l[0] = 1 #原子操作,不用担心线程安全问题 dis.dis(update_list)
"""
6 0 LOAD_CONST 1 (1)
3 LOAD_FAST 0 (L)
6 LOAD_CONST 2 (0)
9 STORE_SUBSCR # 但字节码操作 线程安全
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
"""

非原子操作不是线程安全的

def incr_list(l):
l[0] += 1 #危险!!! 不是原子操作 dis.dis(incr_list)
"""
21 0 LOAD_FAST 0(l)
3 LOAD_CONST 1 (0)
6 DUP_TOPX 2
9 BINARY_SUBSCR
10 LOAD_CONST 2 (1)
13 INPLACE_ADD # 需要多个字节码操作,有可能在线程中切到其他线程
"""
import threading

lock = threading.Lock() # 加锁,保证线程安全
# 加锁操作对性能有一定影响
n = [0] def foo():
with lock:
n[0] = n[0] + 1
n[0] = n[0] + 1
threads = []
for i in range(5000);
t = threading.Thread(target=foo)
threads.append(t) for t in threads:
t.start() print(n)

如何剖析程序性能

使用各种profile工具(内置或第三方)

  • 二八定律,大部分时间耗时在少量代码上
  • 内置的profile/cprofile等工具
  • 使用pyflame(uber开源)的火焰图工具

服务端性能优化措施

web应用一般语言不会成为瓶颈

  • 数据结构与算法优化
  • 数据库层: 索引优化,满查询消除,批量操作减少IO,NoSql
  • 网络IO:批量操作,pipeline操作 减少IO
  • 缓存: 使用内存数据库 redis/memcached
  • 异步; asyncio , celery
  • 并发: gevent/多线程
python生成器与协程

Generator

  • 生成器就是可以生成值的函数
  • 当一个函数里有了yield关键字就成了生成器
  • 生成器可以挂起执行并且保持当前执行的状态
# 生成器
def simple_gen():
yield 'hello'
yield 'world' gen = simple_gen()
print(type(gen)) # 'generator' object
print(next(gen)) # 'hello'
print(next(gen)) # 'world'

基于生成器的协程(python2)

python3之前没有原生协程,只有基于生成器的协程

  • pep 342增强生成器功能
  • 生成器可以通过yield暂停执行和产出数据
  • 同时支持send()向生成器发送数据和throw()向生成器抛出异常
def coro():
hello = yield 'hello' # yield关键字在=右边作为表达式, 可以被sengd的值
yield hello c = cor0()
#输出 ' hello' ,这里调用next产出一个值 'hello',之后函数暂停
print(next(c))
# 再次调用send发送值, 此时hello变量赋值为'world', 然后yield产出hello变量的值 'world'
print(c.send('world'))
# 之后协程结束,后续再send值会抛出异常StopIteration

python3原生协程

python3.5引入async/await支持原生协程(natice copoutine)

import asyncio
import datetime
import random async def display_date(num, loop):
end_time = loop.time(0) + 50.0
while True:
print('Loop: {} Time: {}').format(num, datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(random.randint(0, 5)) loop = asyncio.get_event_loop()
asyncio.ensure_future(display_date(1, loop))
asyncio.ensure_future(display_date(2, loop))
loop.run_forever()

python 单元测试

什么是单元测试

  • 针对程序模式进行正确性检验
  • 一个函数, 一个类进行验证
  • 自底向上保证程序正确性

为什么要写单元测试

三无代码不可取(无文档,无注释,无单测)

  • 保证代码逻辑的正确性(甚至有些采用测试驱动开发TDD)
  • 单测影响设计,易测的代码往往是高内聚低耦合的
  • 回归测试,防止改一处整个服务不可用

单元测试相关的库

  • nose/pytest 较为常用
  • mock 模块用来模拟替换网络请求等
  • coverage 统计测试覆盖率
def test():
"""
如何设计测试用例:(等价类划分)
- 正常功能测试
- 边界值 (比如最大最小,最左最右值)
- 异常值 (比如None, 空值,非法值)
"""
assert binary_search([0,1,2,3,4,5],1) == 1
assert binary_search([0,1,2,3,4,5],6) == -1

python深拷贝与浅拷贝

  • 什么是深拷贝?什么是浅拷贝?
  • python中如何实现深拷贝?
  • 思考: python中如何正确初始化一个二维数组?

最新文章

  1. C/C++ 静态链接库(.a) 与 动态链接库(.so)
  2. IOS UIView 属性clipsToBounds
  3. BZOJ 1088
  4. Download SymmetricDS Data Sync Software for Free
  5. 201521123101 《Java程序设计》第2周学习总结
  6. mysql分库分表,做到永不迁移数据和避免热点
  7. OpenSSL 提取 pfx 数字证书公钥与私钥
  8. 一个简单的定向python爬虫爬取指定页面的jpg图片
  9. [ES]elasticsearch章5 ES的分词(一)
  10. JAVA获取微信小程序openid和获取公众号openid,以及通过openid获取用户信息
  11. 移动端video标签默认置顶的解决方案
  12. 1605--luogu(深搜dfs)
  13. django人类可读性
  14. Linux常用命令详解-目录文件操作命令
  15. 二分 poj 3273
  16. Linux下架构高可用性网络----HA+LB+lvs
  17. 洛谷P1415 拆分数列
  18. 葡萄城报表介绍:Java 报表
  19. Spring使用facotry-method创建单例Bean总结<转>
  20. eclipse的Maven项目pom.xml错误信息提示missingxxxjar解决方案

热门文章

  1. 【前端】input输入框只能输入大于等于0的正数
  2. 深入分析GCC
  3. [LeetCode] 543. Diameter of Binary Tree 二叉树的直径
  4. Windows10不能进入睡眠
  5. Mysql 学习整理
  6. mysql系统信息函数
  7. Python-09-文件处理
  8. 【LEETCODE】54、数组分类,简单级别,题目:605、532
  9. SAS学习笔记60 统计SAS实例之T检验
  10. Mysql字符集之utf8和utf8mb4的使用问题