FastAPI+Uvicorn部署下线程CPU使用率被限约65%的问题
问题分析与解决方案
以下是导致CPU使用率无法拉满的常见原因及对应解决方法:
1. Docker容器CPU资源受限
Docker默认可能对容器CPU使用设置配额,若容器可用CPU核心数少于推理库所需的8核,会导致线程无法跑满。比如主机有8核,但容器仅被分配5核,8个线程的总使用率就会卡在约62.5%左右,和你遇到的65%接近。
解决方法:
- 启动容器时显式指定CPU资源:
- 使用
--cpus=8允许容器使用全部8核CPU(需主机有对应核心数):docker run --cpus=8 ... - 或用
--cpuset-cpus=0-7绑定所有8个核心,确保线程能调度到每个核心:docker run --cpuset-cpus=0-7 ...
- 使用
- 检查容器的Cgroup CPU限制:进入容器后查看
/sys/fs/cgroup/cpu/cpu.cfs_quota_us和/sys/fs/cgroup/cpu/cpu.cfs_period_us,若cpu.cfs_quota_us值小于cpu.cfs_period_us * 8,说明有配额限制,可将其设置为-1解除限制:echo -1 > /sys/fs/cgroup/cpu/cpu.cfs_quota_us
2. CPU亲和性绑定限制
若容器内的进程被绑定到部分CPU核心,推理库的8个线程只能在有限核心上运行,无法充分利用所有资源。
解决方法:
- 查看当前进程的CPU亲和性:找到uvicorn主进程的PID,执行
taskset -p <PID>,输出的掩码若不是全核心(8核对应掩码0xff),则说明有绑定限制。 - 调整亲和性绑定所有核心:
taskset -p 0xff <PID> - 启动容器时直接指定
--cpuset-cpus=0-7,从根源上确保容器能使用全部核心。
3. Uvicorn工作模型与资源竞争
Uvicorn默认使用单进程异步模型,若你的推理函数是同步阻塞型,且未正确配置uvicorn的进程/线程数,可能导致事件循环阻塞,间接影响推理线程的CPU调度。
解决方法:
- 启动uvicorn时增加工作进程数,配合线程数设置,让资源分配更合理:
注:uvicorn main:app --host 0.0.0.0 --port 8080 --workers 2 --threads 4--workers建议设置为CPU核心数的1-2倍,--threads根据实际情况调整,避免过度竞争。 - 若你的接口是异步函数,调用同步推理函数时需用
asyncio.to_thread包裹,避免阻塞事件循环:from fastapi import FastAPI import asyncio from your_inference_lib import infer app = FastAPI() @app.get("/infer") async def inference(): for _ in range(10): # 用to_thread包裹同步推理函数,避免阻塞事件循环 result = await asyncio.to_thread(infer) return {"status": "done"}
4. 进程优先级问题
Uvicorn进程的优先级可能低于直接运行Python脚本时的优先级,导致推理线程无法获得足够的CPU时间片。
解决方法:
- 调整uvicorn进程的优先级,降低nice值(数值越小优先级越高,需root权限):
renice -n -10 <UVICORN_PID> - 启动uvicorn时直接设置优先级:
nice -n -10 uvicorn main:app --host 0.0.0.0 --port 8080
内容的提问来源于stack exchange,提问作者cyrusbehr
相关产品推荐
相关产品推荐

