1. 正运行的多快
2. 速度瓶颈在哪里
3. 内存使用率是多少
4. 内存泄露在哪里

# 用 `time` 粗粒度的计算时间

`````` \$ time python yourprogram.py
real    0m1.028s
user    0m0.001s
sys     0m0.003s
``````

• real — 指的是实际耗时
• user — 指的是内核之外的 CPU 耗时
• sys — 指的是花费在内核特定函数的 CPU 耗时

# 用 `timing context` 管理器细粒度的计算时间

timer.py

``````import time

class Timer(object):
def __init__(self, verbose=False):
self.verbose = verbose

def __enter__(self):
self.start = time.time()
return self

def __exit__(self, *args):
self.end = time.time()
self.secs = self.end - self.start
self.msecs = self.secs * 1000  # millisecs
if self.verbose:
print 'elapsed time: %f ms' % self.msecs
``````

``````from timer import Timer
from redis import Redis
rdb = Redis()

with Timer() as t:
rdb.lpush("foo", "bar")
print "=> elasped lpush: %s s" % t.secs

with Timer() as t:
rdb.lpop("foo")
print "=> elasped lpop: %s s" % t.secs
``````

# 使用 `profiler` 逐行计时和分析执行的频率

``````pip install line_profiler
``````

primes.py

``````@profile
def primes(n):
if n==2:
return [2]
elif n<2:
return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j<half:
s[j]=0
j+=m
i=i+1
m=2*i+3
return [2]+[x for x in s if x]
primes(100)
``````

``````kernprof.py -l -v fib.py
``````

`-l` 选项告诉 `kernprof` 把修饰符 `@profile` 注入你的脚本，`-v` 选项告诉 `kernprof` 一旦你的脚本完成后，展示计时信息。这是一个以上脚本的类似输出：

``````Wrote profile results to primes.py.lprof
Timer unit: 1e-06 s

File: primes.py
Function: primes at line 2
Total time: 0.00019 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
2                                           @profile
3                                           def primes(n):
4         1            2      2.0      1.1      if n==2:
5                                                   return [2]
6         1            1      1.0      0.5      elif n<2:
7                                                   return []
8         1            4      4.0      2.1      s=range(3,n+1,2)
9         1           10     10.0      5.3      mroot = n ** 0.5
10         1            2      2.0      1.1      half=(n+1)/2-1
11         1            1      1.0      0.5      i=0
12         1            1      1.0      0.5      m=3
13         5            7      1.4      3.7      while m <= mroot:
14         4            4      1.0      2.1          if s[i]:
15         3            4      1.3      2.1              j=(m*m-3)/2
16         3            4      1.3      2.1              s[j]=0
17        31           31      1.0     16.3              while j<half:
18        28           28      1.0     14.7                  s[j]=0
19        28           29      1.0     15.3                  j+=m
20         4            4      1.0      2.1          i=i+1
21         4            4      1.0      2.1          m=2*i+3
22        50           54      1.1     28.4      return [2]+[x for x
``````

# 它使用了多少内存？

``````\$ pip install -U memory_profiler
\$ pip install psutil
``````

`line_profiler` 一样， `memory_profiler` 要求在你设置 `@profile` 来修饰你的函数：

``````@profile
def primes(n):
...
...
``````

``````\$ python -m memory_profiler primes.py
``````

``````Filename: primes.py

Line #    Mem usage  Increment   Line Contents
==============================================
2                           @profile
3    7.9219 MB  0.0000 MB   def primes(n):
4    7.9219 MB  0.0000 MB       if n==2:
5                                   return [2]
6    7.9219 MB  0.0000 MB       elif n<2:
7                                   return []
8    7.9219 MB  0.0000 MB       s=range(3,n+1,2)
9    7.9258 MB  0.0039 MB       mroot = n ** 0.5
10    7.9258 MB  0.0000 MB       half=(n+1)/2-1
11    7.9258 MB  0.0000 MB       i=0
12    7.9258 MB  0.0000 MB       m=3
13    7.9297 MB  0.0039 MB       while m <= mroot:
14    7.9297 MB  0.0000 MB           if s[i]:
15    7.9297 MB  0.0000 MB               j=(m*m-3)/2
16    7.9258 MB -0.0039 MB               s[j]=0
17    7.9297 MB  0.0039 MB               while j<half:
18    7.9297 MB  0.0000 MB                   s[j]=0
19    7.9297 MB  0.0000 MB                   j+=m
20    7.9297 MB  0.0000 MB           i=i+1
21    7.9297 MB  0.0000 MB           m=2*i+3
22    7.9297 MB  0.0000 MB       return [2]+[x for x in s if x]

``````

# `line_profiler` 和 `memory_profiler` 的 IPython 快捷命令

`line_profiler``memory_profiler` 一个鲜为人知的特性就是在 IPython 上都有快捷命令。你所能做的就是在 IPython 上键入以下命令：

``````%load_ext memory_profiler
``````

``````In [1]: from primes import primes
In [2]: %mprun -f primes primes(1000)
In [3]: %lprun -f primes primes(1000)
``````

# 哪里内存溢出了？

cPython的解释器使用引用计数来作为它跟踪内存的主要方法。这意味着每个对象持有一个计数器，当增加某个对象的引用存储的时候，计数器就会增加，当一个引用被删除的时候，计数器就是减少。当计数器达到0， cPython 解释器就知道该对象不再使用，因此解释器将删除这个对象，并且释放该对象持有的内存。

``````pip install objgraph
``````

``````import pdb; pdb.set_trace()
``````

## 哪个对象最常见

``````pdb) import objgraph
(pdb) objgraph.show_most_common_types()

MyBigFatObject             20000
tuple                      16938
function                   4310
dict                       2790
wrapper_descriptor         1181
builtin_function_or_method 934
weakref                    764
list                       634
method_descriptor          507
getset_descriptor          451
type                       439
``````

## 哪个对象被增加或是删除了？

``````(pdb) import objgraph
(pdb) objgraph.show_growth()
.
.
.
(pdb) objgraph.show_growth()   # this only shows objects that has been added or deleted since last show_growth() call

traceback                4        +2
KeyboardInterrupt        1        +1
frame                   24        +1
list                   667        +1
tuple                16969        +1
``````

## 这个泄漏对象的引用是什么？

``````x = [1]
y = [x, [x], {"a":x}]
import pdb; pdb.set_trace()
``````

``````(pdb) import objgraph
(pdb) objgraph.show_backref([x], filename="/tmp/backrefs.png")
``````

• 显示占用 Python 程序内存的前 N 个对象
• 显示在一段时期内哪些对象被增加了，哪些对象被删除了
• 显示我们脚本中获得的所有引用

# Effort vs precision

原文作者：yexiaobai
原文地址: https://segmentfault.com/a/1190000000616798
本文转自网络文章，转载此文章仅为分享知识，如有侵权，请联系博主进行删除。