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

Python多线程调用API性能下降原因及优化方案咨询

问题描述

我有三个逻辑完全一致的Flask API(API1.py、API2.py、API3.py),仅端口号和加载的PyTorch模型文件路径不同:

  • 顺序调用API1三次:单请求平均耗时约0.84秒,总耗时4.1秒;
  • 用ThreadPoolExecutor多线程同时调用三个API:单请求耗时增至3.99-4.61秒,总耗时达4.73秒。

请问为何多线程调用反而导致执行时间增加?如何有效降低执行时间?


Flask API示例代码(API1.py)

from flask import Flask, request, jsonify
import torch
import random
import time

loaded_mdl = torch.load("/folder1/Tuned_Model_2.bin")

import spacy
nlp = spacy.load("en_core_web_sm")

import resource
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard))

app=Flask(__name__)

@app.route('/get_ans',methods=['GET','POST'])
def get_ans():
    quest = request.json
    question = quest['question']
    context = quest['context']
    try:
        query = str(question)
        context  = str(context)
        to_predict = [{
            "context": context,
            "qas": [{
                "question":query,
                "id": random.randint(1, 10000),
            }],
        }]
        try:
            start_predict_time = time.time()
            answers, _ = loaded_mdl.predict(to_predict)
            end_predict_time = time.time()- start_predict_time
        except Exception as e:
            # 自定义异常处理逻辑
            pass
        
        doc = nlp(context)
        ans_text = ""
        for sent in doc.sents:
            # 基于answers的处理逻辑
            ans_text = processed_answer  # 假设processed_answer是处理后的结果
            
        return str(end_predict_time)+"@#$"+ans_text
        
    except Exception as e:
        return "error"

if __name__ == "__main__":
    app.run(host='host1',port=port_1,threaded = True)

注:原代码补充了缺失的random、time导入,修正了已废弃的random.rand()方法,补充部分占位逻辑保证代码完整性。


顺序调用代码

import requests
import time

URL = 'http://host:port_1/get_ans'
json_ = {"question": "示例问题", "context": "示例上下文"}

def fetch(session, url):
    with session.post(url,json=json_) as response:
        print(response.text)

def main():
    with requests.Session() as session:
        for _ in range(3):
            fetch(session, URL)

start_time = time.time()
main()
print(f"Total time - {time.time() - start_time}")

多线程调用代码

from concurrent.futures import ThreadPoolExecutor
import requests
import time

URL1 = 'http://host:port_1/get_ans'
URL2 = 'http://host:port_2/get_ans'
URL3 = 'http://host:port_3/get_ans'

json_ = {"question": "示例问题", "context": "示例上下文"}

def fetch(session, url):
    with session.post(url,json=json_) as response:
        print(response.text)

def main():
    with ThreadPoolExecutor(max_workers=3) as executor:
        with requests.Session() as session:
            executor.map(fetch, [session] * 3, [URL1,URL2,URL3] )
            
start_time = time.time()
main()
print(f"Total time - {time.time() - start_time}")

问题解答

一、多线程调用变慢的核心原因

  1. CPU资源严重竞争
    PyTorch默认会用多线程执行CPU运算(比如矩阵乘法),每个API的模型预测都会抢占大量CPU核心;加上spaCy的模型处理也是CPU密集型任务,三个API同时运行时,CPU核心被瓜分,每个任务的实际可用算力大幅下降,导致单请求耗时飙升。

  2. 内存带宽瓶颈
    三个模型同时加载到内存并进行推理时,会频繁读取模型参数,导致内存带宽饱和,数据读取延迟增加,拖慢整体执行速度。

  3. Flask内置服务器局限性
    Flask默认的Werkzeug服务器是单进程多线程模型,并不适合高并发CPU密集型任务。多请求同时到达时,线程切换开销加上CPU资源不足,会让请求处理效率急剧降低。

二、有效优化方案

  1. 限制PyTorch CPU线程数
    在每个API代码开头添加以下配置,避免单个API占用过多核心:

    torch.set_num_threads(1)
    torch.set_num_interop_threads(1)
    

    让每个API仅使用1个CPU核心,三个API可以并行利用3个核心,减少资源竞争。

  2. 替换生产级服务器
    用Gunicorn或Uvicorn替代Werkzeug,采用多进程模式启动每个API:

    # 示例:用Gunicorn启动API1,单进程单线程
    gunicorn --workers 1 --threads 1 API1:app -b host1:port_1
    

    独立进程可以更好地利用多核CPU,避免进程内线程竞争。

  3. 批量处理请求
    合并多个请求为批量请求发送给单个API,利用模型的批量推理能力(通常批量推理的单样本耗时更低),减少多API并发的资源消耗。

  4. GPU加速推理
    如果条件允许,将模型部署到GPU上:PyTorch的GPU推理能大幅降低单请求耗时,且GPU的并行处理能力更适合多并发场景,从根源解决CPU资源竞争问题。

  5. 调整调用端线程池配置
    根据服务器CPU核心数调整max_workers,避免同时发起超出CPU承载能力的请求。比如CPU有4核,max_workers设置为3或4即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 06:50:21