1.调试和性能分析的主要场景
- 代码本身有问题,找到并修复
- 代码效率有问题,如过度浪费资源
- 开发新的future时,需要测试
2.用pdb调试
- 一般使用print()进行调试
我们一般使用print()直接在相应的代码段进行打印,这种方法只适用于小程序。每次代码的运行都很快,使用print()很方便。
不适用于大型程序,特别是一些需要反复运行调试、追溯上下文代码,才能找到错误根源的代码。调试效率极低。
- IDE内置的debug工具
如可以很方便地在程序中设置断点,待程序运行到相应的断点处,自动停下。然后一一查看各变量的值。
但,一般公司中,产品的开发与迭代,是多语言支持的,公司内部会开发许多接口将多语言结合起来。大部分时候,IDE对混合代码不支持断点调试功能。
- 如何使用pdb
官方文档:https://docs.python.org/3/library/pdb.html
3.用cProfile进行性能分析
官主文档:https://docs.python.org/3.7/library/profile.html
例如计算斐波拉契数列的例子:
import cProfile
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
def fib_seq(n):
res = []
if n > 0:
res.extend(fib_seq(n-1))
res.append(fib(n))
return res
cProfile('fib_seq(30)')
1)ncalls,相应代码/函数被调用次数
2)tottime,对应代码/函数总共执行所需要的时间(不包括它调用的其他代码/函数的执行时间)
3)tottime percall,即tottime/ncalls
4)cumtime,即对应代码/函数总共执行所需要的时间,(包括它调用其他代码/函数的执行时间)
5)cumtime percall,即cumtime/ncalls
据cProfile进行进行性能分析后,发现对fib函数的调用有700多万次,其实许多是重复的,可以用字典来保存计算过的结果,防止重复。改进后的代码如下:
import cProfile
def memoize(f):
memo = {}
def helper(x):
if x not in memo:
memo[x] = f(x)
return memo[x]
return helper
@memoize
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)
def fib_seq(n):
res = []
if n > 0:
res.extend(fib_seq(n-1))
res.append(fib(n))
return res
cProfile.run('fib_seq(30)')