在我们遇到性能问题的时候,很多时候需要去查看性能的瓶颈在哪里,本篇文章就是提供了多种常用的方案来监控函数的运行时间。
首先说明,time模块很多是系统相关的,在不同的OS中可能会有一些精度差异,如果需要高精度的时间测量,可以使用time.perf_counter。perf_counter仍然还是 基于时钟时间,很多因素会影响到它的精确度,比如机器负载。如果你对于执行时间更感兴趣,使用 time.process_time() 来代替它。 更多内容可以参考尾部的链接。
下面两个例子来源于《python cookbook》
import timefrom functools import wrapsdef timethis(func): ''' Decorator that reports the execution time. ''' @wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) end = time.perf_counter() print(func.__name__, end-start) return result return wrapper@timethisdef countdown(n): """ Counts down """ while n > 0: n -= 1countdown(100000)
import timefrom contextlib import contextmanager@contextmanagerdef timethis(label): start = time.perf_counter() try: yield finally: end = time.perf_counter() print('{}: {}'.format(label, end - start))# Example usewith timethis('counting'): n = 10000000 while n > 0: n -= 1
python提供了timeit模块,这个可以在python console中直接使用
$ python -m timeit -n 4 -r 5 -s "import timing_functions" "timing_functions.random_sort(2000000)"
输出为:
4 loops, best of 5: 2.08 sec per loop
这个模块在ipython中使用起来相对简洁很多。
上面的方法其实还是比较简单粗暴。profile模块是个更好的cProfile是profile的C实现,速度会更快。类似的包有pickle,也有个对应的Cpickle版本。 这个包可嵌入的代码中,类似下面这种:
import cProfilecProfile.run("myfunction()")
我个人最喜欢的还是下面这种(下面的代码可能需要加一下PYTHONPATH):
$python -m cProfile -o output.pkl my_main_file.py
首先无需更改现有代码结构,其次可以将结果保存到output.pkl中。强烈建议将profile的结果保存起来,因为生产中有些profile可能耗时很长,而且控制台输出的内容有限,当你想从结果里面提取点重要信息,又要重新来过,特别耗时。
当获取上面的output.pkl的时候,可以进入python console,使用pstats得到结果:
import pstatsp = pstats.Stats('output.pkl') # 文件名p.sort_stats('time') # 按照时间排序p.print_stats(10) # 最耗时的前10个,如果没有参数,默认输出全部
看每一行执行的时间占比,也大概知道原因出在什么地方了。自带的profile会深入到包的底层运算逻辑,不是特别清晰。下面是line_profiler的使用方法,个人感觉比装饰器的方式好用太多:
from line_profiler import LineProfilerimport randomdef do_stuff(numbers): s = sum(numbers) l = [numbers[i]/43 for i in range(len(numbers))] m = ['hello'+str(numbers[i]) for i in range(len(numbers))]numbers = [random.randint(1,100) for i in range(1000)]lp = LineProfiler() lp_wrapper = lp(do_stuff) # 函数lp_wrapper(numbers) # 参数lp.print_stats()
line_profiler在notebook的使用也超级方便,非常推荐。
%load_ext line_profilerclass A: def to_profile_func(): pass%lprun -f A.to_profile_func A.to_profile_func()
留言与评论(共有 0 条评论) “” |