You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为基于Hypercorn的FastAPI应用做性能分析(适配fork进程)

针对Hypercorn+FastAPI应用的性能分析方案

问题背景

我正尝试对Python模块进行性能分析以定位性能瓶颈。该模块启动Hypercorn服务器,其上运行着异步FastAPI应用。以下是main.py中的启动逻辑大致示例:

hypercorn_config = Config()
hypercorn_config.bind = "localhost:8000"
hypercorn_config.workers = 1 # 单worker和多worker模式都试过
hypercorn_config.application_path = f"service:create_app({config_str})"
run(hypercorn_config)

create_app()方法用于创建并返回FastAPI应用实例。

尝试过的工具及遇到的问题

我尝试了多款性能分析工具:

  • yappi
  • scalene
  • cProfile
  • pyinstrument
  • py-spy
  • memray(用于内存分析)

但遇到了以下问题:

  1. Hypercorn会fork进程,导致yappi、scalene等工具无法正确追踪子进程;
  2. pyinstrument仅能生成单请求分析报告,而我需要多请求的聚合分析结果;
  3. py-spy可附加到进程,但在异步密集型工作负载中存在采样不足的问题;
  4. memray也难以正确追踪fork出的工作进程的内存使用情况。

请问如何针对运行在Hypercorn上的FastAPI应用进行正确的CPU与内存性能分析,适配其fork行为?理想情况下我希望捕获多请求的端到端性能分析结果,是否有标准工具、技巧或最佳实践可遵循?


解决方案与最佳实践

1. 适配Hypercorn Fork机制:子进程初始化时启动分析工具

Hypercorn的worker进程通过fork父进程创建,直接在父进程启动分析工具会因fork后的状态继承问题导致追踪失效。核心思路是让分析工具在子进程启动时初始化:

  • 开启Hypercorn的preload_app=True配置,确保应用在fork前加载完成,然后通过FastAPI的启动事件触发分析工具:
from fastapi import FastAPI
import yappi
import os

def create_app():
    app = FastAPI()

    @app.on_event("startup")
    async def startup_event():
        # 仅在子进程中启动分析(避免父进程重复执行)
        if os.getppid() != 1:
            yappi.set_clock_type("wall")
            yappi.start()

    @app.on_event("shutdown")
    async def shutdown_event():
        yappi.stop()
        # 导出聚合分析结果
        stats = yappi.get_func_stats()
        stats.save("hypercorn_profile.yappi", type="pstat")
        stats.print_all()

    # 业务路由示例
    @app.get("/")
    async def root():
        return {"message": "Hello World"}

    return app
  • 内存分析工具memray也可以用同样的方式在子进程启动:
import memray

@app.on_event("startup")
async def startup_event():
    memray.start("hypercorn_memray.bin", follow_fork=False)

关闭服务器后会自动生成内存分析文件,用memray flamegraph hypercorn_memray.bin生成可视化火焰图。

2. 多请求聚合分析:cProfile结合FastAPI中间件

如果需要聚合多请求的性能数据,可以编写中间件在每个请求中记录cProfile数据,最后合并结果:

import cProfile
import pstats
from fastapi import FastAPI, Request

profiler = cProfile.Profile()
stats = pstats.Stats(profiler)

def profile_middleware(app: FastAPI):
    @app.middleware("http")
    async def profile_request(request: Request, call_next):
        profiler.enable()
        response = await call_next(request)
        profiler.disable()
        return response

    @app.on_event("shutdown")
    async def save_profile():
        stats.dump_stats("combined_profile.pstat")
        stats.sort_stats(pstats.SortKey.TIME).print_stats(20)

    return app

# 在create_app中应用中间件
def create_app():
    app = FastAPI()
    app = profile_middleware(app)
    # 其他业务逻辑
    return app

注意:单worker模式下效果最佳,多worker时需要每个worker单独导出结果后手动合并。

3. 异步场景优化py-spy采样

针对py-spy在异步工作负载中采样不足的问题,调整采样参数提升追踪质量:

  • 启动时指定异步追踪模式和更高采样频率:
    py-spy record -f async -r 1000 -o profile.svg -- python main.py
    
    • -f async:启用异步任务追踪,捕获协程执行时间
    • -r 1000:设置每秒采样1000次,提升采样密度
  • 多worker场景下,先用ps aux | grep hypercorn找到worker进程的PID,再单独attach追踪:
    py-spy record -f async -p <WORKER_PID> -o worker_profile.svg
    

4. 调试阶段简化:单worker模式分析

如果暂时不需要多worker支撑并发,将hypercorn_config.workers = 1,此时Hypercorn不会fork额外进程,大部分工具可以直接正常工作:

  • 用pyinstrument main.py启动服务,发送多请求后关闭服务器,自动生成聚合分析报告
  • 用scalene main.py进行CPU、内存、IO的全面分析,单worker模式下无追踪障碍

总结

  1. 多worker场景优先选择子进程初始化时启动分析工具,配合FastAPI的startup/shutdown事件完成追踪与结果导出;
  2. 多请求聚合分析优先用cProfile中间件或yappi的全量追踪;
  3. 异步场景下用py-spy的async模式提升采样质量;
  4. 调试阶段可以先用单worker模式简化分析流程。

内容的提问来源于stack exchange,提问作者Sanjit Jha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 07:06:04