EN

同步 vs 异步 LLM 调用:并排对比

并发 LLM 操作的性能对比——比较 asyncio 和 httpx 的吞吐量

返回教程列表🌐 Read in English
进阶9 分钟

同步 vs 异步 LLM 调用:并排对比

并发 LLM 操作的性能对比——比较 asyncio 和 httpx 的吞吐量

同步 vs 异步 LLM 调用对比(2026):单次调用用同步、并发多请求用 async——100 个请求 400s 变 40s。含 Semaphore 限流、事件循环阻塞等常见坑、决策表,以及与 Batch API/流式的关系。

同步 vs 异步 LLM 调用:并排对比

规则一句话:一次只调一个 → 同步没问题;同时调多个 → 异步完胜,差距巨大。 LLM 请求是数秒纯 I/O 等待。同步代码白白消耗这些时间;异步代码利用它们运行其他请求。100 次调用每次约 4 秒,顺序同步约 400 秒,并发数为 10 的异步只需约 40 秒。API 账单相同,但挂钟时间缩短 10 倍。

这里不讨论:让单次调用更快。异步对单次调用的延迟毫无帮助——模型耗时该多久还是多久。

同一任务,两种方式

python

同步——适用于脚本、CLI、一次一个的流程

from openai import OpenAI

client = OpenAI()

def summarize(text: str) -> str: resp = client.chat.completions.create( model='gpt-4o-mini', messages=[{'role': 'user', 'content': f'Summarize in one line: {text}'}] ) return resp.choices[0].message.content

results = [summarize(doc) for doc in documents] # N * 延迟,顺序执行

python

异步——相同工作,并发执行

import asyncio from openai import AsyncOpenAI

client = AsyncOpenAI() sem = asyncio.Semaphore(10) # 并发上限——见下文

async def summarize(text: str) -> str: async with sem: resp = await client.chat.completions.create( model='gpt-4o-mini', messages=[{'role': 'user', 'content': f'Summarize in one line: {text}'}] ) return resp.choices[0].message.content

async def main(): return await asyncio.gather(*(summarize(d) for d in documents))

results = asyncio.run(main()) # ~N/10 * 延迟

每个主流 SDK 都提供两个客户端(OpenAI/AsyncOpenAIAnthropic/AsyncAnthropic);在 Node.js 中一切默认异步,所以这个决策主要存在于 Python 中。

信号量不是可选项

asyncio.gather 对 5000 个协程会同时发起 5000 个请求——你会立刻触发 429 速率限制,甚至可能触发滥用检测。信号量限制在途请求数。根据你的速率限制来设定大小:大致为 (每分钟请求限制 / 60) × 平均请求秒数,然后在此基础上保守一些。对于仍然漏过的 429 错误,添加带退避的重试(SDK 会自动重试;调整 max_retries 参数)。

决策表

场景选择原因

CLI 工具、笔记本、每次用户操作一个调用同步简单至上;没有重叠的必要 处理并发用户的 Web 后端异步一个同步 LLM 调用会阻塞工作线程数秒——在 FastAPI 中使用 async def 路由 + 异步客户端 立即批量处理 N 个文档异步 + 信号量上面提到的 10 倍挂钟时间优势 可以等待数小时的批量处理两者都不用——Batch API便宜 50%;参见 OpenAI Batch vs 标准 API 顺序依赖工具调用的 Agent同步(或等待的异步)第 k+1 步需要第 k 步的输出——无法提取并发性 扇出:相同提示给多个模型,或对分块进行映射异步经典的并行模式

混合同步和异步(常见错误)

  • 在异步 Web 处理器内部调用同步代码——阻塞整个事件循环;服务器上的每个其他请求都会在期间停滞。这是 #1 生产环境错误。使用异步客户端,或者将遗留同步代码包装在 asyncio.to_thread() 中。
  • 在正在运行的事件循环中调用 asyncio.run()(Jupyter、FastAPI)——引发 RuntimeError。在笔记本中直接 await main()
  • 线程作为替代方案:使用同步客户端的 ThreadPoolExecutor 也能获得并发性,在遗留代码库中是一个合理的过渡方案——但线程消耗更多内存,且池大小有限制。对于 LLM 规模的 I/O 并发,asyncio 是更干净的选择。
  • 流式传输是正交的

    同步 vs 异步是关于*有多少请求重叠*;流式传输是关于*逐步接收一个响应*。它们可以组合——异步处理器可以在其他请求进行的同时将令牌流式传输到浏览器。关于传输机制,请参见 LLM 流式传输 vs 轮询SSE 实现指南

    常见问题

    异步能降低成本吗? 不能——相同的令牌,相同的账单。它减少挂钟时间,让一台服务器处理更多并发用户(这可以降低*基础设施*成本)。

    并发数可以设多高? 取决于你的提供商速率限制层级。超过约 50-100 个并发请求时,还要注意 HTTP 客户端的连接池限制。

    asyncio 与 multiprocessing 对比? LLM 调用是 I/O 密集型,不是 CPU 密集型——多进程没有好处,反而带来序列化开销。将进程保留给真正的 CPU 密集型预处理/后处理。


    *最后更新:2026 年 6 月。*

    相关工具

    asynciopython