又一个 Python Profiler
项目描述
亚皮
具有线程&协程&greenlet意识 的跟踪分析器。
强调
- 快:Yappi 很快。它完全是用 C 语言编写的,并且为了让它变得快速而投入了大量的爱与关怀。
- 独特:Yappi 支持多线程、asyncio和gevent分析。标记/过滤多个分析器结果有有趣的用例。
- 直观:Profiler 可以启动/停止,并且可以从任何时间和任何线程获取结果。
- 符合标准:Profiler 结果可以以 callgrind 或 pstat 格式保存。
- 丰富的功能集:Profiler 结果可以显示Wall Time或实际CPU Time,并且可以从不同的会话中聚合。为过滤和排序分析器结果定义了各种标志。
- 稳健:Yappi 已经见证了多年的生产使用。
动机
CPython 标准发行版带有三个确定性分析器。cProfile
,Profile
和hotshot
. cProfile
实现为基于 的 C 模块lsprof
,Profile
使用纯 Python,hotshot
可以看作是 cProfile 的一个小子集。主要问题是所有这些分析器都缺乏对多线程程序和 CPU 时间的支持。
如果要分析多线程应用程序,则必须为这些分析器提供入口点,然后可能合并输出。这些分析器都不是为长时间运行的多线程应用程序而设计的。也不可能使用这些分析器对启动/停止/检索跟踪的应用程序进行分析。
现在快进到 2019 年:随着asyncio
库和异步框架的最新改进,当前的大多数分析器都缺乏显示正确的 wall/cpu 时间甚至每个协程的调用计数信息的能力。因此,我们需要一种不同的方法来分析异步代码。Yappi 在 v1.2 中引入了coroutine profiling
. 使用coroutine-profiling
,您应该能够分析正确的 wall/cpu 时间和协程的调用计数。(也包括在上下文切换中花费的时间)。您可以在此处查看详细信息。
安装
可以通过 PyPI 安装
$ pip install yappi
或直接从源头获取。
$ pip install git+https://github.com/sumerc/yappi#egg=yappi
例子
一个简单的例子:
import yappi
def a():
for _ in range(10000000): # do something CPU heavy
pass
yappi.set_clock_type("cpu") # Use set_clock_type("wall") for wall time
yappi.start()
a()
yappi.get_func_stats().print_all()
yappi.get_thread_stats().print_all()
'''
Clock type: CPU
Ordered by: totaltime, desc
name ncall tsub ttot tavg
doc.py:5 a 1 0.117907 0.117907 0.117907
name id tid ttot scnt
_MainThread 0 139867147315008 0.118297 1
'''
分析多线程应用程序:
您可以通过 Yappi 分析多线程应用程序,并且可以通过ctx_id
使用get_func_stats
API 过滤轻松检索每个线程的配置文件信息。
import yappi
import time
import threading
_NTHREAD = 3
def _work(n):
time.sleep(n * 0.1)
yappi.start()
threads = []
# generate _NTHREAD threads
for i in range(_NTHREAD):
t = threading.Thread(target=_work, args=(i + 1, ))
t.start()
threads.append(t)
# wait all threads to finish
for t in threads:
t.join()
yappi.stop()
# retrieve thread stats by their thread id (given by yappi)
threads = yappi.get_thread_stats()
for thread in threads:
print(
"Function stats for (%s) (%d)" % (thread.name, thread.id)
) # it is the Thread.__class__.__name__
yappi.get_func_stats(ctx_id=thread.id).print_all()
'''
Function stats for (Thread) (3)
name ncall tsub ttot tavg
..hon3.7/threading.py:859 Thread.run 1 0.000017 0.000062 0.000062
doc3.py:8 _work 1 0.000012 0.000045 0.000045
Function stats for (Thread) (2)
name ncall tsub ttot tavg
..hon3.7/threading.py:859 Thread.run 1 0.000017 0.000065 0.000065
doc3.py:8 _work 1 0.000010 0.000048 0.000048
Function stats for (Thread) (1)
name ncall tsub ttot tavg
..hon3.7/threading.py:859 Thread.run 1 0.000010 0.000043 0.000043
doc3.py:8 _work 1 0.000006 0.000033 0.000033
'''
过滤/排序统计信息的不同方法:
您可以使用filter_callback
APIget_func_stats
过滤函数、模块或YFuncStat
对象中可用的任何内容。
import package_a
import yappi
import sys
def a():
pass
def b():
pass
yappi.start()
a()
b()
package_a.a()
yappi.stop()
# filter by module object
current_module = sys.modules[__name__]
stats = yappi.get_func_stats(
filter_callback=lambda x: yappi.module_matches(x, [current_module])
) # x is a yappi.YFuncStat object
stats.sort("name", "desc").print_all()
'''
Clock type: CPU
Ordered by: name, desc
name ncall tsub ttot tavg
doc2.py:10 b 1 0.000001 0.000001 0.000001
doc2.py:6 a 1 0.000001 0.000001 0.000001
'''
# filter by function object
stats = yappi.get_func_stats(
filter_callback=lambda x: yappi.func_matches(x, [a, b])
).print_all()
'''
name ncall tsub ttot tavg
doc2.py:6 a 1 0.000001 0.000001 0.000001
doc2.py:10 b 1 0.000001 0.000001 0.000001
'''
# filter by module name
stats = yappi.get_func_stats(filter_callback=lambda x: 'package_a' in x.module
).print_all()
'''
name ncall tsub ttot tavg
package_a/__init__.py:1 a 1 0.000001 0.000001 0.000001
'''
# filter by function name
stats = yappi.get_func_stats(filter_callback=lambda x: 'a' in x.name
).print_all()
'''
name ncall tsub ttot tavg
doc2.py:6 a 1 0.000001 0.000001 0.000001
package_a/__init__.py:1 a 1 0.000001 0.000001 0.000001
'''
分析 asyncio 应用程序:
您可以看到协程 wall-time 的配置文件正确。
import asyncio
import yappi
async def foo():
await asyncio.sleep(1.0)
await baz()
await asyncio.sleep(0.5)
async def bar():
await asyncio.sleep(2.0)
async def baz():
await asyncio.sleep(1.0)
yappi.set_clock_type("WALL")
with yappi.run():
asyncio.run(foo())
asyncio.run(bar())
yappi.get_func_stats().print_all()
'''
Clock type: WALL
Ordered by: totaltime, desc
name ncall tsub ttot tavg
doc4.py:5 foo 1 0.000030 2.503808 2.503808
doc4.py:11 bar 1 0.000012 2.002492 2.002492
doc4.py:15 baz 1 0.000013 1.001397 1.001397
'''
分析 gevent 应用程序:
您现在可以使用 yappi 来分析 greenlet 应用程序!
import yappi
from greenlet import greenlet
import time
class GreenletA(greenlet):
def run(self):
time.sleep(1)
yappi.set_context_backend("greenlet")
yappi.set_clock_type("wall")
yappi.start(builtins=True)
a = GreenletA()
a.switch()
yappi.stop()
yappi.get_func_stats().print_all()
'''
name ncall tsub ttot tavg
tests/test_random.py:6 GreenletA.run 1 0.000007 1.000494 1.000494
time.sleep 1 1.000487 1.000487 1.000487
'''
文档
-
协程分析 (1.2 中的新功能)
-
Greenlet 分析 (1.3 中的新功能)
注意:是的。我知道我应该将文档移至 readthedocs.io。敬请关注!
相关讲座
特别感谢 A.Jesse Jiryu Davis:
PyCharm 集成
Yappi 是PyCharm
. 如果您安装了 Yappi,PyCharm
将使用它。有关详细信息,请参阅官方文档。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。