python代码怎么运行_Python代码优化之运行时间分析

在实现了需求之后,若想使运行速度提高,则需要先对代码整体的运行时间做一个分析,找出瓶颈在何处。“二八原则”在这里也是适用的,即20%的代码占了80%的运行时间。

这里介绍pyhton中两个实现此功能的函数。

cProfile

简介

profile是Python的标准库。可以统计程序里每一个函数的运行时间,并且提供了多样化的报表,而cProfile则是它的C实现版本,剖析过程本身需要消耗的资源更少。所以在Python 3中,cProfile代替了profile,成为默认的性能剖析模块

使用

它的实现也很简单,以下面这段代码为例:

import cProfile
import time
def fun1():
    sum = 0
    for i in range(1000000):
        sum += i
def fun2():
    time.sleep(10)
def main():
    fun1()
    fun2()
cProfile.run('main()') # 注意这里的''

输出:

7 function calls in 10.044 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000   10.044   10.044 -11-b3eeba351a69>:10(main)1    0.045    0.045    0.045    0.045 -11-b3eeba351a69>:3(fun1)1    0.000    0.000    9.999    9.999 -11-b3eeba351a69>:7(fun2)1    0.000    0.000   10.044   10.044 :1()1    0.000    0.000   10.044   10.044 {built-in method builtins.exec}1    9.999    9.999    9.999    9.999 {built-in method time.sleep}1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

上面显示了函数的运行时间情况,可以看出fun2及其里面的time.sleep函数运行时间占用时间较多。

也可以在python解释器调用cProfile来分析代码运行时间,如输入以下命令,效果一样。

python -m cProfile prof1.py

输出的各项意义如下:

3ba5c70ed4345a9ecf308d6392d5da80.png
cProfile的统计结果

但是结果不方便我们查看,特别是函数特别多的时候。这个时候可以结合pstats模块的一个类Stats来使用。

Stats函数接受一个参数——就是cProfile的输出文件名。Stats提供了对cProfile输出结果进行排序、输出控制等功能。如我们把前文的程序改为如下:

# 省略前面的部分
def main():
    fun1()
    fun2()
cProfile.run('main()', 'prof.txt')
import pstats
p = pstats.Stats('prof.txt')
p.sort_stats('time').print_stats()

引入pstats之后,把结果按照运行时间排序,结果如下:

Thu Jul 30 13:30:57 2020    prof.txt

   7 function calls in 10.043 seconds

   Ordered by: internal time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1   10.001   10.001   10.001   10.001 {built-in method time.sleep}
    1    0.043    0.043    0.043    0.043 -11-b3eeba351a69>:3(fun1)1    0.000    0.000   10.043   10.043 {built-in method builtins.exec}1    0.000    0.000   10.001   10.001 -11-b3eeba351a69>:7(fun2)1    0.000    0.000   10.043   10.043 -11-b3eeba351a69>:10(main)1    0.000    0.000   10.043   10.043 :1()1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Stats有若干个函数,这些函数组合能输出不同的cProfile报表,功能非常强大,其中最重要的函数就是sort_stats和print_stats,通过这两个函数我们几乎可以用适当的形式浏览所有的信息了。下面是sort_stats支持的排序参数。

88cd7b787ea8430c9724e3d8a5bbfda6.png
sort_stats的排序参数

上面介绍的cProfile主要是针对较多的代码,若是想测试一下单行或者函数的耗时,用下面这个库实现会简单很多。

timeit

  1. 测试单行语句
from timeit import timeit
timeit('"-".join(str(n) for n in range(100))', number=10000)
output: 0.3018611848820001

其中,number参数表示代码的运行次数。

  1. 测试多行语句
from timeit import timeit
string = """
s = 0
for i in range(1000):
    s += i
"""
timeit(stmt=string,number=100)

  1. 测试函数
from timeit import timeit

def func():
    s = 0
    for i in range(1000):
        s += i
    print(s)

# timeit(函数名_字符串,运行环境_字符串,number=运行次数)
t = timeit('func()', 'from __main__ import func', number=1000)
print(t)
output: 0.04860229999758303

说到timeit模块,不得不先提一下它简便的魔术命令实现。

魔法函数: %timeit,它会自动计算接下来一行的 Python 语句的执行时间。例如,我们可能想了解列表综合的性能:

%timeit L = [n ** 2 for n in range(1000)]
output: 273 µs ± 1.51 µs per loop 
(mean ± std. dev. of 7 runs, 1000 loops each)

对于多行语句,可以加入第二个 %符号将其转变成单元魔法,以处理多行输入。例如,下面是 上面for循环的同等结构:

%%timeit
L = []
for n in range(1000):
    L.append(n**2)
output: 299 µs ± 1.72 µs per loop 
(mean ± std. dev. of 7 runs, 1000 loops each)

以上就是关于Python中代码计时的一些分享,最后分享一句话。

过早优化是编程中一切“罪恶”的根源。——Donald Knuth

在后续的一些优化技巧中会发现,过早的优化会让你的努力得不到应有的回报。

如果有任何问题或者本文有写的不对的地方,欢迎点击下方蓝字留言。

点此留言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值