Skip to main content

又一个 Python Profiler

项目描述

亚皮

亚皮

具有线程&协程&greenlet意识 的跟踪分析器。

强调

  • :Yappi 很快。它完全是用 C 语言编写的,并且为了让它变得快速而投入了大量的爱与关怀。
  • 独特:Yappi 支持多线程、asynciogevent分析。标记/过滤多个分析器结果有有趣的用例
  • 直观:Profiler 可以启动/停止,并且可以从任何时间和任何线程获取结果。
  • 符合标准:Profiler 结果可以以 callgrind 或 pstat 格式保存。
  • 丰富的功能集:Profiler 结果可以显示Wall Time或实际CPU Time,并且可以从不同的会话中聚合。为过滤和排序分析器结果定义了各种标志。
  • 稳健:Yappi 已经见证了多年的生产使用。

动机

CPython 标准发行版带有三个确定性分析器。cProfile,Profilehotshot. cProfile实现为基于 的 C 模块lsprofProfile使用纯 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_statsAPI 过滤轻松检索每个线程的配置文件信息。

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_callbackAPIget_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
'''

文档

相关讲座

特别感谢 A.Jesse Jiryu Davis:

PyCharm 集成

Yappi 是PyCharm. 如果您安装了 Yappi,PyCharm将使用它。有关详细信息,请参阅官方文档。

下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

yappi-1.3.6.tar.gz (58.7 kB 查看哈希

已上传 source

内置发行版

yappi-1.3.6-cp310-cp310-win_amd64.whl (33.9 kB 查看哈希

已上传 cp310

yappi-1.3.6-cp310-cp310-win32.whl (31.4 kB 查看哈希

已上传 cp310

yappi-1.3.6-cp310-cp310-musllinux_1_1_x86_64.whl (94.8 kB 查看哈希

已上传 cp310

yappi-1.3.6-cp310-cp310-musllinux_1_1_i686.whl (90.8 kB 查看哈希

已上传 cp310

yappi-1.3.6-cp310-cp310-macosx_10_9_x86_64.whl (32.2 kB 查看哈希

已上传 cp310

yappi-1.3.6-cp39-cp39-win_amd64.whl (34.0 kB 查看哈希

已上传 cp39

yappi-1.3.6-cp39-cp39-win32.whl (31.5 kB 查看哈希

已上传 cp39

yappi-1.3.6-cp39-cp39-musllinux_1_1_x86_64.whl (93.6 kB 查看哈希

已上传 cp39

yappi-1.3.6-cp39-cp39-musllinux_1_1_i686.whl (89.7 kB 查看哈希

已上传 cp39