时间都去哪儿了--python性能优化

2020-10-09 15:31:29 浏览数 (1)

当自动化测试的量特别大的时候,python性能就显得尤为重要。 往往高手和菜鸟的区别在性能上体现出来了。

菜鸟往往能够简单的实现功能,不停地加case,只要能达到目的,完成任务就行了。

高手往往能够看出当前架构的劣势,并不断完善框架,优化性能,以达到最好的效果。

我所知道的一个项目,API自动化100来个case. 业务场景也不复杂,就是对一些API返回做一些数据上的 smoke验证。 你猜它的代价是多大。 两个人花一年全职来写并维护,跑起来需要2小时。抛开接口反应时间不说,我看了代码,有很多地方可以优化的点。(对于这个结果,我这里只能呵呵了,贵公司真有钱!)

我在网上找了一些python性能优化的资料,总结如下:

1.timeit:

代码语言:javascript复制
import timeit
def fun():
    for i in range(100000):
        a = i * i

timeit.timeit('fun()', 'from __main__ import fun', number=1)
0.02922706632834235 

timeit只输出被测试代码的总运行时间,单位为秒,没有详细的统计。

2.profile

profile:纯Python实现的性能测试模块,接口和cProfile一样。

代码语言:javascript复制
 def fun():
   for i in range(100000):
      a = i * i

 profile.run('fun()')
         5 function calls in 0.031 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.016    0.016 :0(exec)
        1    0.016    0.016    0.016    0.016 :0(setprofile)
        1    0.016    0.016    0.016    0.016 <pyshell#13>:1(fun)
        1    0.000    0.000    0.016    0.016 <string>:1(<module>)
        1    0.000    0.000    0.031    0.031 profile:0(fun())
        0    0.000             0.000          profile:0(profiler)

ncall:函数运行次数

tottime:函数的总的运行时间,减去函数中调用子函数的运行时间

第一个percall:percall = tottime / nclall

cumtime:函数及其所有子函数调整的运行时间,也就是函数开始调用到结束的时间。

第二个percall:percall = cumtime / nclall

3.cProfile

profile:c语言实现的性能测试模块,接口和profile一样。

代码语言:javascript复制
import cProfile
def fun():
   for i in range(100000):
      a = i * i

 cProfile.run('fun()')
         4 function calls in 0.024 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.024    0.024    0.024    0.024 <pyshell#17>:1(fun)
        1    0.000    0.000    0.024    0.024 <string>:1(<module>)
        1    0.000    0.000    0.024    0.024 {built-in method exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

ncalls、tottime、percall、cumtime含义同profile。

4.line_profiler

安装:

代码语言:javascript复制
pip install line_profiler

安装之后kernprof.py会加到环境变量中。

line_profiler可以统计每行代码的执行次数和执行时间等,时间单位为微秒。

测试代码:

C:Python34test.py

代码语言:javascript复制

@profile
def fun():
    a = 0
  b = 0
  for i in range(100000):
        a = a   i * i

    for i in range(3):
        b  = 1
  time.sleep(0.1)

    return a   b

fun()

使用:

1.在需要测试的函数加上@profile装饰,这里我们把测试代码写在C:Python34test.py文件上.

2.运行命令行:kernprof -l -v C:Python34test.py

输出结果如下:

Total Time:测试代码的总运行时间 Hits:表示每行代码运行的次数 Time:每行代码运行的总时间 Per Hits:每行代码运行一次的时间 % Time:每行代码运行时间的百分比

5.memory_profiler:

memory_profiler工具可以统计每行代码占用的内存大小。

安装:

代码语言:javascript复制
pip install memory_profiler

pip install psutil

测试代码:

同line_profiler。

使用:

1.在需要测试的函数加上@profile装饰

2.执行命令:python -m memory_profiler C:Python34test.py

输出如下:

6.PyCharm图形化性能测试工具:

PyCharm提供了图像化的性能分析工具,使用方法利用PyCharm的Profile工具进行Python性能分析。

PyCharm提供了性能分析工具Run-》Profile,如下图所示。利用Profile工具可以对代码进行性能分析,找出瓶颈所在。

测试:

下面以一段测试代码来说明如何使用pycharm的Profile功能。

测试代码见下文,文件命名为Test.py, 一共有5个函数,每个函数都调用了time.sleep进行延时,其中fun5函数调用了fun4函数:

代码语言:javascript复制
import time

def fun1(a, b):
    print('fun1')
    print(a, b)
    time.sleep(1)

def fun2():
    print('fun2')
    time.sleep(1)

def fun3():
    print('fun3')
    time.sleep(2)

def fun4():
    print('fun4')
    time.sleep(1)

def fun5():
    print('fun5')
    time.sleep(1)
    fun4()

fun1('foo', 'bar')
fun2()
fun3()
fun5()

点击Run-》Profile开始测试,代码运行结束后会生成一栏测试结果,测试结果由两部分构成,Statistcs(性能统计)和Call Graph(调用关系图):

image

Statistcs(性能统计):

性能统计界面由Name、Call Count、Time(ms)、Own Time(ms) 4列组成一个表格,见下图。

1. 表头Name显示被调用的模块或者函数;Call Count显示被调用的次数;Time(ms)显示运行时间和时间百分比,时间单位为毫秒(ms)。

2.点击表头上的小三角可以升序或降序排列表格。

3.在Name这一个列中双击某一行可以跳转到对应的代码。

4.以fun4这一行举例:fun4被调用了一次,运行时间为1000ms,占整个运行时间的16.7%

image

Call Graph(调用关系图):

Call Graph(调用关系图)界面直观展示了各函数直接的调用关系、运行时间和时间百分比,见下图。

0.右上角的4个按钮表示放大、缩小、真实大小、合适大小;

1.箭头表示调用关系,由调用者指向被调用者;

2.矩形的左上角显示模块或者函数的名称,右上角显示被调用的次数;

3.矩形中间显示运行时间和时间百分比;

4.矩形的颜色表示运行时间或者时间百分比大小的趋势:红色 > 黄绿色 > 绿色,由图可以看出fun3的矩形为黄绿色,fun1为绿色,所有fun3运行时间比fun1长。

5.从图中可以看出Test.py直接调用了fun3、fun1、fun2和fun5函数;fun5函数直接调用了fun4函数;fun1、fun2、fun3、fun4和fun5都直接调用了print以及sleep函数;整个测试代码运行的总时间为6006ms,其中fun3的运行时间为1999ms,所占的时间百分比为33.3%,也就是 1999ms / 6006ms = 33.3%。

对于性能测试,我们需要知道时间都去哪儿了。找出耗时间的地方,做进一步的优化。

以上列举了这么多性能优化的方法,哪种才适合我呢? 对于我个人来说,我不想在原有的方法里面加代码,一来麻烦,二来破坏代码的完整性。 然后希望有完整的时间消耗明细。

个人觉得pycharm的Profile功能很适合我。不管我时候用没有用测试框架,都能直接看出各个地方的消耗时间。

0 人点赞