Python 性能分析

Python性能分析与优化

一个优秀的程序员,在保证业务正常的条件下都会追求自己的程序更快、更省。更快:运行时间短;更省:相对节省计算机资源(比如:CPU、Memory)。一般都是以这两种衡量方式来度量自己的程序及进一步优化自己程序的空间。更专业的性能分析软件一般有两类方法论:event-based profiling和statistical profiling

Event-based Profiling

并不是所有编程语言都支持这类性能分析,支持这类分析的语言主要有:

  • Java:JVMTI(JVM Tools Interface,JVM工具接口)为性能提供了钩子,可以跟踪函数调用和事件执行等等。
  • .NET :也提供了事件监听器,主要得益于.net 运行时。
  • Python:可以利用sys.setprofile函数来跟踪函数python(call,return,exception)或者c(call,return,exception).

基于事件的性能分析(event-based profiler or tracing profiler)是通过手机程序执行过程中的具体事件进行工作的,这些性能分析会产生大量的数据,基本而言,你监听的事件越多产生的数据量句越多。这导致它们不太实用,在开始对程序进行性能往往不是首选,当其他性能分析不够用或者不精准它们可以作为最后的选择。我们看一下一个python的例子:

Statistical Profiling

以固定的事件间隔对程序计数器进行抽样统计,可以查看每个函数的消耗时间。由于它对程序计数器进行抽样,所以数据结果是对真实值的统计近似。以便于查看分析程序的性能细节,查出性能瓶颈。

  • 分析的数据更少:只对程序执行过程进行抽样,而不用保留每一条数据。
  • 对性能造成的影响小:由于使用抽样(操作系统中断),目标程序的性能遭受干扰更小;虽然不能做到100%无干扰,但是要比基于事件的分析造成的干扰更小。

Python性能分析

现在我们来谈谈Python的性能分析,Python性能分析有很多工具和模块。比如:time粗粒度分析、cProfile,line_Profile等等。

time分析器

time.time()简单的衡量运行时间,我们看一下Demo:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import time

class Timer(object):
    def __init__(self):
        pass

    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, *args):
        self.end = time.time()
        self.secs = self.end - self.start
        print 'costed time: %f ms' % (self.secs * 1000)

def test():
    k = []
    for i in range(1000000):
        k.append(i**2+1000)
    return k
        
if __name__ == "__main__":
    with Timer() as t:
        test()
#运行结果:
costed time: 776.966095 ms

将要测量时间的代码用Python关键字with和Timer上下文管理器包起来。它会在你的代码运行的时候开始计时,并且在执行结束的完成计时。

性能分析器cProfile

cProfile是Python默认的性能分析器,它是一种确定性的性能分析器,提供了一组API来帮助开发者手机Python程序运行的信息。统计每个函数消耗的CPU时间,它只测量CPU时间,并不关心内存消耗和其他内存相关信息统计。

import cProfile
import pstats

def get_result():
    sum = 0
    for i in range(10000):
        sum += add_result(i,i+1)
    return sum

def add_result(a,b):
    return a+b
  
if __name__ == "__main__":
    cProfile.run("get_result()")
#结果如下:
         10004 function calls in 0.018 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.018    0.018 <string>:1(<module>)
    10000    0.006    0.000    0.006    0.000 untitled-1.py:10(add_result)
        1    0.011    0.011    0.018    0.018 untitled-1.py:4(get_result)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}

1004代表一共函数调用,花费了0.018秒,第一列ncalls代表了函数总共调用次数,第二列tottime总共运行时间,它不包括内部其它函数运行的时间,第三列cumtime函数总计运行时间,含调用的函数运行时间,tottime和cumtime是不一样的。可以这样理解:get_result花费时间等于tottime(get_result)+cumtime(add_result)最后一列比显示调用函数名。这是最常用的用法,cprofile也提供很多API,比如:查看函数调用了那些函数等等。

性能分析器line_Profile

这个是耕细粒度的性能分析,这个分析一行一行函数分析,不过得需要装饰器注册@profile然后还得需要kernprof脚本将会在执行的时候将它自动地注入到你的脚步的运行时。下面我们来看一下例子:

import cProfile
import pstats

@profile
def get_result():
    sum = 0
    for i in range(10000):
        sum += add_result(i,i+1)
    return sum

def add_result(a,b):
    return a+b
  
if __name__ == "__main__":
    get_result()

我们需要通过kernprof运行这个脚本,如下所示:
kernprof -l -v untitled-1.py
-l选项通知kernprof注入@profile装饰器到你的脚步的内建函数,-v选项通知kernprof在脚本执行完毕的时候显示计时信息。分析如下所示:
rote profile results to untitled-1.py.lprof
Timer unit: 1e-06 s

Total time: 0.008289 s
File: untitled-1.py
Function: get_result at line 4

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     4                                           @profile
     5                                           def get_result():
     6         1            2      2.0      0.0      sum = 0
     7     10001         2854      0.3     34.4      for i in range(10000):
     8     10000         5433      0.5     65.5          sum += add_result(i,i+1)
     9         1            0      0.0      0.0      return sum

这个很好理解,无需解释了。在下一个篇博客中,分析Python内存使用和如何查找内存溢出。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏腾讯Bugly的专栏

【MIG专项测试组】腾讯手机管家实战分析:内存突增是为神马?

应用版本升级后使用内存突增?如何跟踪?这次MIG专项测试组为大家分享内存问题跟踪实战过程! MIG专...

3374
来自专栏我的翻译

O API - REST APIs的替代品

过去,当接到为一个网站构建一套API的任务时,我会定义一组URL来处理想要完成的各种任务。

81918
来自专栏hotqin888的专栏

HydroCMS规范、图集查询系统设计

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

1602
来自专栏pangguoming

黑盒测试和白盒测试的区别

1.        软件测试方法:白盒测试、黑盒测试、灰盒测试、静态测试、动态测试

1941
来自专栏ImportSource

并发编程-多线程的好处

上一文:并发编程-并发的简史 如果线程使用得当,多线程可以降低你的开发和维护成本,而且还能改善复杂应用程序的性能。多线程让模仿人类工作方式以及交互变得简单,多线...

3636
来自专栏大数据文摘

维基百科中的数据科学:手把手教你用Python读懂全球最大百科全书

几年前谁能想到,匿名贡献者们的义务工作竟创造出前所未有的巨大在线知识库?维基百科不仅是你写大学论文时最好的信息渠道,也是一个极其丰富的数据源。

1613
来自专栏芋道源码1024

告诉你 Redis 是一个牛逼货

Redis 是一个 Key-Value 存储系统。和 Memcached 类似,它支持存储的 value 类型相对更多,包括 string(字符串)、 list...

1320
来自专栏北京马哥教育

运维开发:你可能会忽略的 Git 提交规范

如果你有一个项目,从始至终都是自己写,那么你想怎么写都可以,没有人可以干预你。可是如果在团队协作中,大家都张扬个性,那么代码将会是一团糟,好好的项目就被糟践了。...

1011
来自专栏Coco的专栏

【前端性能】浅谈域名发散与域名收敛

2722
来自专栏EAWorld

微服务RPC框架选美

1、RPC 框架谁最美? ? Hello,everybody!说到RPC框架,可能大家能想到一堆RPC开源框架,那么在微服务平台中,微服务间的服务调用,不可...

1K8

扫码关注云+社区

领取腾讯云代金券