zoukankan      html  css  js  c++  java
  • python面试总结3(性能分析优化,GIl常考题)

    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中如何正确初始化一个二维数组?
  • 相关阅读:
    MyEclipse_安装和破解
    Gparted for partition of Linux on graphic interface
    hadoop native lib can't load use local lib
    oracle java for ubuntu apt-get
    vi set the tab width for python
    sqlite3 on python for newbies
    python multiprocessing pool
    HRESULT: 0x80040228
    获取鼠标坐标
    Oracle 与 ODAC 一起安装
  • 原文地址:https://www.cnblogs.com/only-me/p/11364283.html
Copyright © 2011-2022 走看看