zoukankan      html  css  js  c++  java
  • 测试代码执行时间的模块-timeit

    有时候我们想看看一个函数的执行时间是多久,这时候我们可以使用装饰器,在函数的执行开始前,记录一个时间,在函数的执行结束后记录一个时间,然后求两个数的差,就可以得到这个函数本次的执行时间了。但是这样做的做法,太Low,接下来我们就说说Python 内置的timeit 模块

    timeit 模块可以用来测试一小段Python 代码的执行速度
    timeit 模块里面有一个Timer 类,首先需要实例化这个类,先看一下初始化这个类所可以接收的参数,下面是timeit 模块关于Timer 的源码

    1 class Timer:
    2     ...
    3     def __init__(self, stmt="pass", setup="pass", timer=default_timer, globals=None)
    4     ...

    资料显示这个是Python2.3 更新的一个完美计时工具,可以测量Python 代码的运行时间。
    首先Timer 是这个测量代码执行时间的类
    这个类实例化时可接收的参数
        stmt:要测试的代码语句(statment)
        setup:运行代码时需要的设置,也就是要测试代码的前置条件
        timer:参数时一个定时器函数,与平台有关
        globals:待查,后续更新


    这个类实例出来后有两个方法是我们经常用到的

    Timer.timeit(number=default_number)
    参数number 是测试代码时的测试次数,默认default_number 为1000000 次,这个方法返回执行代码的耗时,一个float 类型的秒数。

    Timer.repeat(repeat=default_repeat, number=default_number)
    参数repeat 是重复整个测试的次数,默认是重复3 次,参数number 是每个测试代码时的测试次数,默认也是1000000 次,
    这个方法返回一个列表,元素的个数就是重复整个测试的次数,每个元素值就是每个测试代码的耗时,是一个float 类型的秒数。

    由简单着手,具体如下:

     1 import timeit 
     2 # 创建timeit 对象
     3 timer_hello = timeit.Timer("print('hello'))
     4 # 调用timer 方法执行1000 次,并打印结果
     5 time_use = time_hello.timeit(1000)
     6 print('elapse :{}'.format(time_use))
     7 
     8 
     9 hello
    10 ...
    11 hello
    12 elapse :0.07077193709161148

    但是事实上没人会测试打印1000 次“hello”所用的时间,除非特别无聊,举这个例子只是为了方便直观的说明第二个参数的作用

     1 import timeit
     2 
     3 a = 'hello'
     4 
     5 timer_a = timeit.Timer("print(a)")
     6 time_use = time_a.timeit(1000)
     7 print('elapse :{}'.format(time_use))
     8 
     9 
    10 ...
    11 NameError: name 'a' is not defined

    为什么会这样呢?明明已经定义了变量a 啊,为什么还是提示未定义?
    这是因为这个类实例话的时候,会构建一个独立的虚拟空间用于测试待测试代码
    只需要传入第二个参数就可以解决这个问题

     1 import timeit
     2 
     3 # a = 'hello'
     4 
     5 timer_a = timeit.Timer("print(a)",“a='hello'”)
     6 time_use = time_a.timeit(1000)
     7 print('elapse :{}'.format(time_use))
     8 
     9 hello
    10 ...
    11 elapse :0.038286632634104326
    12 # 时间上会比直接打印‘hello’要稍微长一点

    这样就没问题了,可是我们当初的目的是用来打印一个函数啊,难不成要在第二个参数上写一个函数,比如匿名函数f = lambda :print(‘hello’)?

     1 import timeit
     2 
     3 # a = 'hello'
     4 
     5 timer_a = timeit.Timer("f()",“f=lambda : print('hello')”)
     6 time_use = time_a.timeit(1000)
     7 print('elapse :{}'.format(time_use))
     8 
     9 hello
    10 ...
    11 elapse :0.014775986865789454

    也没问题,但是这只是个简单的函数,如果函数的逻辑很复杂,代码量很多,这样就不太好用了,那怎么办呢?答案是导入,将当前模块导入到这个Timer 实例对象的环境中就行了

     1 import timeit
     2 
     3 # a = 'hello'
     4 def print_hello():
     5     print('hello')
     6 
     7 timer_a = timeit.Timer("print_hello()",“from __main__ import print_hello”)
     8 time_use = time_a.timeit(1000)
     9 print('elapse :{}'.format(time_use))
    10 
    11 
    12 hello
    13 ...
    14 elapse :0.038094632804770844

    下面是Python 中list 的一些操作测试

     1 def t1():
     2     l = []
     3     for i in range(1000):
     4     l = l + [i]
     5 def t2():
     6     l = []
     7     for i in range(1000):
     8     l.append(i)
     9 def t3():
    10     l = [i for i in range(1000)]
    11 def t4():
    12     l = list(range(1000))
    13 
    14 import timeit
    15 
    16 time_t1 = timeit.Timer('t1()', 'from __main__ import t1')
    17 print('concat use {} seconds'.format(time_t1.timeit(1000)))
    18 time_t2 = timeit.Timer('t2()', 'from __main__ import t2')
    19 print('append use {} seconds'.format(time_t2.timeit(1000)))
    20 time_t3 = timeit.Timer('t3()', 'from __main__ import t3')
    21 print('comprehension use {} seconds'.format(time_t3.timeit(1000)))
    22 time_t4 = timeit.Timer('t4()', 'from __main__ import t4')
    23 print('list range use {} seconds'.format(time_t4.timeit(1000)))
    24 
    25 concat use 1.1694602938130723 seconds
    26 append use 0.0634083880814329 seconds
    27 comprehension use 0.028811085501257327 seconds
    28 list range use 0.009896880091662119 seconds

    由此可以看出,Python 内置的list 函数强转的效率有多高,列表推导式仅次之,列表尾部添加元素的效率正常,而列表的拼接后重新引用的执行效率,则要差上很多了。

    相对于append,我们看一下列表头部插入的执行效率

    1 def t5():
    2     l = []
    3     for i in range(1000):
    4     l.insert(0, i)
    5 time_insert = timeit.Time('t5()', 'from __main__ import t5')
    6 print('insert use {} seconds'.format(time_t5.timeit(1000)))
    7 
    8 insert use 0.2727046464847587 seconds

    可以看出列表都不插入的效率比尾部插入的效率要低的多

    最后再来说一说repeat 方法

    print('append information :{}'.format(time_t2.repeat(4, 1000)))
    print('insert information :{}'.format(time_t5.repeat(4, 1000)))
    
    
    
    append information :[0.06173994512004878, 0.06133461214701143, 0.06315461052923509, 0.06169727849130799]
    insert information :[0.26799709511369324, 0.26826598376357, 0.2789770853537018, 0.26663220743803784]

    可以看出,对这些代码的运行,进行了输入个数的测试,并返回了一个列表,元素为每个测试的结果

  • 相关阅读:
    线程和进程
    Java多线程实现(四种方法)
    Java中的锁
    synchronized和java.util.concurrent.locks.Lock
    Lock的实现类ReentrantLock&Condition类的await/signal/signalAll(生产者消费者场景)
    synchronized&Object类的wait/notify/notifyAll(生产者消费者场景)
    SQL语句优化
    面试
    数据库三大范式
    设计模式之JDK动态代理源码分析
  • 原文地址:https://www.cnblogs.com/yungiu/p/10392333.html
Copyright © 2011-2022 走看看