Python运行时间评估: timeit module

目录

1. 概要

2. Why timeit?

3. timeit.timeit()

3.1 Example1

3.2 Example2

4. timeit.repeat()

5. 命令行使用


1. 概要

        本文介绍基于python内建模块timeit评估python小代码块的运行时间的方法。

2. Why timeit?

        用time模块可以进行时间评估,在要执行的代码之前和之后分别用time.time()将时间记录下来,然后相减就得到了待测对象代码块的运行时间。但是这种方法有一些小的瑕疵不是很精确,有以下几方面的原因:

  1. 它不能正确处理系统时间发生复位的情况(当然这个在评估小的代码块的时间时可能无关紧要,而且(time.monotonic()可以解决这个问题)
  2. 它统计的时间可能包含一些背景进程的运行时间,因为它是看系统时间而不是针对待测代码块的CPU使用时间进行检测(time.perf_counter()解决了这个问题)
  3. 它只是统计运行一次的时间,因而必然会有可能出现较大的波动。当然你可以写个loop去重复执行多次。。。

        Timeit解决了(3)的统计性偏差的问题,Timeit可以自动地进行大量次数的代码执行以获得更加精确的统计平均值。由于是进行了大量重复运行,因此由(2)所带来的背景进程运行所导致的干扰波动也会得到相应的抑制。所以Timeit用于对很小的代码片段(code snippet)进行运行时间评估非常方便。

        以下介绍timeit模块中的主要的几个工具,以及命令行运行的方式。

3. timeit.timeit()

        调用方式:timeit.timeit(stmt, setup, timer, number) 

        Stmt: 待评估对象,缺省值为’pass’

        Setup: 这是指定在执行stmt之前运行的代码,缺省值为pass。可以用于比如说导入运行stmt所需要的库模块等

        Timer: 指定一个timeit.Timer 对象。它通常有合理的缺省值,用户可以不管它的设置.

        Number: 指定重复运行次数,缺省值为1000000.

        返回值是秒单位的时间值。

3.1 Example1

        让我们看一个简单的例子。

# importing the required module
import timeit


# Example1
# code snippet whose execution time is to be measured
mycode1 = '''
mylist = list(range(1000))
for k in range(1000):
    if k in mylist:
        pass		
'''

mycode2 = '''
myset = set(range(1000))
for k in range(1000):
    if k in myset:
        pass		
'''

mycode3 = '''
mydict = dict.fromkeys(list(range(1000)),'')
for k in range(1000):
    if k in mydict:
        pass		
'''

# timeit statement
print('tCost for mycode1: ', timeit.timeit(stmt = mycode1, number = 1000) )
print('tCost for mycode2: ', timeit.timeit(stmt = mycode2, number = 1000) )
print('tCost for mycode3: ', timeit.timeit(stmt = mycode3, number = 1000) )

运行结果:

        tCost for mycode1:  5.059251000002405
        tCost for mycode2:  0.062394699998549186
        tCost for mycode3:  0.07763279999926453

         以上这个例子比较了python中list, dict, set的查询性能,从结果可以看出,dict和set查询性能相当,比list则要快2个数量级。

3.2 Example2

        再看一个利用setup的例子。

# Example2
mysetup1 = "from math import sqrt"
mycode1 = '''
mylist = []
for k in range(1000):
    mylist.append(sqrt(k))
'''

mysetup2 = "import numpy as np"
mycode2 = '''
mylist = []
for k in range(1000):
    mylist.append(np.sqrt(k))
'''

mysetup3 = "import numpy as np"
mycode3 = '''
myrslt = np.sqrt(np.array(list(range(1000))))
'''

print('tCost for mycode1: ', timeit.timeit(setup = mysetup1, stmt = mycode1, number = 1000) )
print('tCost for mycode2: ', timeit.timeit(setup = mysetup2, stmt = mycode2, number = 1000) )
print('tCost for mycode3: ', timeit.timeit(setup = mysetup3, stmt = mycode3, number = 1000) )

运行结果:

        tCost for mycode1:  0.12879980000070645
        tCost for mycode2:  0.7638708999984374
        tCost for mycode3:  0.05968639999991865

        这个例子比较了python内置的math.sqrt()函数与numpy.sqrt()的运行速度差异。

        令人意外的是,以标量形式进行运算的话,numpy.sqrt()竟然比math.sqrt()还要慢很多!此种必有蹊跷,回头再另行调查。

4. timeit.repeat()

        调用方式:timeit.repeat(stmt, setup, timer, number, repeat)      

        Timeit.repeat()在timeit.timeit()的基础上又追加了一个repeat参数,用于指定重复次数。

        老实讲,不明白为什么?把number*repeat设定给number然后调用timeit.timeit()有什么不好吗?不过,当然必须相信库开发者这样设计总是有它的道理的。搜了一艘,查到这么一个帖子:

        what is diffrence between number and repeat in python timeit? - Stack Overflow

        虽然隐约觉得明白了一些,但是还是不足以以自己的语言来解释一番,就不“为赋新词强说愁”了,等理解透彻并且自信能够解释清楚后再回来补课。先看例子吧:

# Example3, timeit.repeat()
print('\nExample3: ...')
mysetup3 = '''
from math import sqrt
def example3(N):
    mylist = []
    for k in range(N):
        mylist.append(sqrt(k))
'''
mycode3 = '''
example3(1000)
'''
print('tCost for mycode3: ', timeit.repeat(setup = mysetup3, stmt = mycode3, number = 1000, repeat=100) )

运行结果:

Example3: ...
tCost for mycode3:  [0.12954869999884977, 0.13357629999882192, 0.1325503000007302, 0.1383346000002348, 0.13686260000031325, 0.1361642000010761, 0.13500720000229194, 0.1356378999989829, 0.13645919999908074, 0.1313538000031258]

        与timeit.timeit只给出一个结果不同的是,timeit.repeat()将每次repeat看作是一次独立的统计,总共给出的repeat指定次数的结果。基于这repeat个样本数据可以方便进行更深入的统计分析,比如说最大值、最小值、均值、标准差、直方图等等等等blabla。。。(超出了本文的范围,就此打住)

    以上例子中还顺便还给出将待评估的代码(如果多行的话)封装为一个函数,从setup参数中导入,然后在stmt中值导入对函数的调用,提供了更进一步的灵活性。

5. 命令行使用

        比如说,在AnacondaPrompt(当然其它命令行终端应该也是一样,只要能运行python就行)执行以下命令:

        python -m timeit -s "from math import sqrt" -n 10000 -r 10 'x=sqrt(12345678)'

        其中,-m是用于指定模块名,-s, -n, -r则分别对应timeit.repeat()函数的setup, number和repeat参数,最后的’’中的代码即是对应stmt参数的指定内容了。

        此外在IPython环境下还可以用一下魔术命令的方式调用timeit:

        import numpy as np

        %timeit np.sqrt(123456789)

运行结果:

        705 ns ± 2.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

 

        另外关于python中的time模块的使用方法,请参考:        Python运行时间评估: time moduleicon-default.png?t=L892https://blog.csdn.net/chenxy_bwave/article/details/119458690

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值