假设你是火锅店的老板,午市高峰涌进来八位单独吃饭的客人,每位都要一份蛋炒饭。厨师每做一份饭都要洗锅、开火、装盘,八份饭就是八次重复劳动。这时一位机灵的服务员提议:把客人两两拼桌,四份蛋炒饭一锅炒,洗一次锅、开一次火,却出四份餐。原本要忙半小时的活儿,十分钟就干完了。 AI 推理服务遇到的正是同样的苦恼。GPU 就像那位厨师,每一次推理启动都有固定的开销——加载算子、搬运数据、等内核就绪。ONNX Runtime 是那个机灵的服务员,它把短时间内到达的多个推理请求合并成一个批次(batch),一次性交给 GPU 处理。这中间有两个旋钮:一桌坐几个人(批次大小),以及服务员等多久开始下单(超时窗口)。本篇文章就用火锅店的故事,把动态批处理调优讲透。

岗位关联

掌握动态批处理调优,你就能在 GPU 资源不变的情况下,把推理服务的吞吐量提升数倍——这是 AI 部署工程师性价比最高的优化技能之一。

目录
  1. 可视化演示
  2. 批处理原理
  3. 请求到达模式
  4. 批次大小决策
  5. 超时窗口机制
  6. 吞吐量优化
  7. 延迟约束
  8. 监控调优
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

动态展示ONNX Runtime动态批处理的工作流、三类触发策略、吞吐量提升(3-10x)及vLLM连续批处理机制,帮助理解如何通过智能合并请求最大化GPU利用率。

本节配有交互动效(进入视口后自动加载,约 3KB)。

批处理原理

火锅店后厨有个铁锅,每次开火前都要烧热锅、倒油、洗锅铲,这套“准备动作”需要一分钟。单独炒一份蛋炒饭要三分钟,其中一分钟花在准备上。但如果是拼桌后一次炒四份,准备动作还是一分钟,炒饭本身四分钟,总共五分钟就出了四份饭。平均每份饭只要一分多钟——这就是拼桌的魔力。

在 AI 推理服务里,GPU 就是那口铁锅。每一次推理请求,ONNX Runtime 都要经历内核启动、数据搬运、算子加载等固定开销。如果来一个请求就推理一次,固定开销重复支付,GPU 大部分时间都在“热锅”而不是“炒菜”。动态批处理做的事情很简单:把短时间内到达的多个请求合并成一个批次,一次送进 GPU,让固定开销只付一次。

假设批处理一堆请求的总耗时由两部分组成:固定开销 $T_{overhead}$ 和每请求的计算时间 $T_{compute}$,则每请求平均耗时可用下面公式描述:

火锅店的铁锅不会因为一次炒四份饭就多烧一次锅,GPU 也不会因为一次推理四个请求就多启动四次内核。把固定开销摊薄,是动态批处理最本质的收益来源。理解了这一条,后面所有调优思路都是围绕它展开的。

Tavg=Toverhead+Tcompute×BB=ToverheadB+Tcompute\displaystyle T_{avg}=\frac{T_{overhead}+T_{compute}\times B}{B}=\frac{T_{overhead}}{B}+T_{compute}
公式说明:批次大小 B 越大,每个请求分摊到的固定开销就越少,平均耗时越接近单个请求纯粹的计算时间。
  1. 分子总耗时一批 B 个请求的总耗时 = 一次固定开销 + B 份计算耗时
    Toverhead+Tcompute×BT_{overhead}+T_{compute}\times B
  2. 均摊到每个请求把总耗时除以批次大小 B,得到每个请求的平均耗时
    Toverhead+Tcompute×BB\frac{T_{overhead}+T_{compute}\times B}{B}
  3. 拆开看摊薄效果固定开销被 B 分成 B 份,B 越大,每份越小
    ToverheadB+Tcompute\frac{T_{overhead}}{B}+T_{compute}

请求到达模式

火锅店的客流有鲜明的潮汐:午餐十二点涌进一波客流,下午三点门可罗雀。服务员拼桌的效果,完全取决于当下有多少客人在等位。客流汹涌时,不用招呼就能坐满一桌;客流稀疏时,等上半小时也凑不齐四个人。

推理服务的请求到达同样有波峰波谷。波峰时,请求像潮水一样涌来,队列瞬间堆满,批次很快就攒够了;波谷时,半天来一个请求,批次一直凑不满。请求到达的密集程度,在数学上用一个量来刻画——到达率 $\lambda$,表示单位时间内平均到达的请求个数。到达率直接影响攒批的快慢,也决定了服务中积压的请求数量。

排队论里的 Little's Law 描述了积压量、到达率与等待时间之间的稳定关系。当系统处于稳定状态时,积压的请求数等于到达率乘以平均等待时间:

火锅店老板一看等位区的人越来越多,就知道要么客人来得太密,要么上菜太慢。对应到推理服务,队列积压上涨时,你需要判断是到达率飙升了,还是攒批等待把 $W$ 拖长了。请求到达模式决定了你在调参时的发力方向,波峰期要防积压,波谷期要防饿着 GPU。

L=λ×W\displaystyle L=\lambda \times W
公式说明:到达率越高或等待时间越长,系统中积压的请求就越多。这个公式帮助我们在调参前先判断系统的积压压力。
  1. 到达率$\lambda$ 表示平均每秒钟有多少新请求进入服务系统
    λ\lambda
  2. 平均等待时间$W$ 表示一个请求从进入队列到开始被处理平均要等多久
    WW
  3. 相乘得积压数两者相乘得到系统中平均积压的请求总数
    λ×W\lambda \times W

批次大小决策

火锅店的桌子有四种规格:两人桌、四人桌、六人桌和大圆桌。服务员拼桌时,如果只有两位客人却坐六人桌,桌面空间浪费;如果硬塞十个人到六人桌,客人挤得难受,厨师一次也炒不了十人份。桌子大小,就是餐厅的“批次大小”。

在 ONNX Runtime 中,max_batch_size 决定了一次推理最多合并多少个请求。这个值不是拍脑袋定的:首先,GPU 显存是硬约束——批次越大,中间张量占用的显存越多,超出显存直接报错;其次,模型本身的延迟特性也有限制——有些模型批次增大后单请求耗时暴涨,收益急剧缩水。

批次大小与吞吐量的关系并非线性增长,而是遵循收益递减规律。设固定开销为 $a$,每个请求的计算时间为 $b$,则吞吐量 $S(B)$ 随批次大小 $B$ 的变化为:

火锅店老板发现,四人桌翻台效率最高,换成六人桌后上菜时间变长,客人满意度反而下降。调批次大小也是这个道理:找到吞吐量曲线的“拐点”,在显存允许的范围内选择一个让吞吐提升明显、延迟增加可接受的批次大小。记住,不是桌子越大越好,合适才是最好。

S(B)=Ba+bB,lim⁡B→∞S(B)=1b\displaystyle S(B)=\frac{B}{a+bB},\qquad \lim_{B\to\infty}S(B)=\frac{1}{b}
公式说明:批次无限增大时,吞吐量趋近于 $1/b$ 这个渐近上限。超过某个点后,再增大批次只是让延迟白白上升。
  1. 分母总耗时$a+bB$ 表示处理一批请求的总时间,$a$ 是固定开销,$bB$ 是 B 个请求的计算时间
    a+bBa+bB
  2. 求吞吐量用请求数 B 除以总耗时,得到单位时间能处理的请求数
    Ba+bB\frac{B}{a+bB}
  3. 取极限当 B 趋于无穷大时,$a$ 的影响消失,吞吐量逼近 $1/b$
    lim⁡B→∞S(B)=1b\lim_{B\to\infty}S(B)=\frac{1}{b}

超时窗口机制

服务员小李有个苦恼:刚把两位客人领到四人桌,是立刻下单,还是等等看有没有新客人来拼桌?立刻下单,两位客人不用久等,但可能错过大单;一直等,客人可能不耐烦起身走人。餐厅定了个规矩:凑不齐四人,最多等三分钟,三分钟一到立刻下单。

这个“三分钟”就是动态批处理里的超时窗口(timeout)。在 ONNX Runtime 中,即使请求没有攒够 max_batch_size,只要达到超时窗口,也必须把现有请求作为一批送出去。超时窗口是吞吐量和延迟之间的调节旋钮:窗口越大,攒够批次的机会越多,但每个请求的等待时间也越长。

超时窗口的期望等待时间可以用一个简洁的概率表达式刻画:当批次在超时前未凑满时,请求需要等到超时时刻才能被处理;若批次提前凑满,则请求可以更早出发。因此平均等待时间近似等于超时窗口长度乘以“没凑满”的概率:

火锅店的“三分钟规则”本质上是在赌:三分钟内来新客人的概率高,拼桌成功,餐厅多赚钱;概率低,直接下单,至少保住眼前客人。超时窗口何尝不是一场赌博,赌的是请求到达的节律。调参时,观察业务的请求到达间隔分布,如果大多数请求都在几百毫秒内成群到达,窗口就不必设得太长。

E[wait]=Ttimeout×P(batch not full)\displaystyle E[\text{wait}]=T_{timeout}\times P(\text{batch not full})
公式说明:如果请求总是提前凑满批次,$P(\text{batch not full})$ 很小,等待时间远低于超时窗口;如果请求稀疏,等待时间几乎就等于超时窗口本身。
  1. 超时窗口$T_{timeout}$ 是 ONNX Runtime 中配置的最大攒批等待时长
    TtimeoutT_{timeout}
  2. 未凑满概率$P(\text{batch not full})$ 表示在超时前批次大小未达到上限的概率,它由请求到达模式决定
    P(batch not full)P(\text{batch not full})
  3. 期望相乘两者相乘得到请求平均等待时间的估计值
    Ttimeout×P(batch not full)T_{timeout}\times P(\text{batch not full})

吞吐量优化

火锅店的运营指标里有一个“翻台率”:一张桌子一晚上接待了几波客人。拼桌之所以让老板高兴,不是因为单桌客人多,而是因为同样的灶台、同样的桌位数,一晚上能服务更多客人。翻台率,就是餐厅的“吞吐量”。

在推理服务里,吞吐量指单位时间内成功完成的推理请求数。吞吐量的上限很大程度上取决于 GPU 在“炒菜”还是在“做准备工作”。如果 GPU 空转等待请求,或者反复做固定开销动作,它的有效工作时间占比就低。动态批处理正是让 GPU 忙碌起来的核心手段。

衡量 GPU 忙得有没有价值,要看有效计算时间占总时间的比例。批次大小为 B 时,GPU 利用率可以写为:

火锅店老板发现,只要铁锅一直不冷,翻台率自然就高。GPU 也一样,它的理想状态是“灶台从来不等菜”——请求到达后立刻攒批、立刻处理,处理完立刻接下一批。让 GPU 始终处于有效计算状态,吞吐量的提升是立竿见影的。调优的每一天,你都在回答同一个问题:GPU 有多少时间真正在炒菜?

U=B×TcompToverhead+B×Tcomp\displaystyle U=\frac{B\times T_{comp}}{T_{overhead}+B\times T_{comp}}
公式说明:分子是真正做矩阵运算的时间,分母是固定开销加上计算时间。批次越大,利用率越接近 100%。
  1. 有效计算时间$B\times T_{comp}$ 是 B 个请求真正做计算所花的时间
    B×TcompB\times T_{comp}
  2. 总占用时间$T_{overhead}+B\times T_{comp}$ 包含固定开销和全部计算时间
    Toverhead+B×TcompT_{overhead}+B\times T_{comp}
  3. 求比值有效计算时间除以总占用时间,得到 GPU 利用率
    B×TcompToverhead+B×Tcomp\frac{B\times T_{comp}}{T_{overhead}+B\times T_{comp}}

延迟约束

火锅店里总有几位急躁的客人,落座后盯着手机看时间,三分钟不上菜就要发火。服务员心里有一根红线:不管怎么拼桌,不能让客人等超过五分钟。这根红线,就是服务等级协议(SLA)。

推理服务也有类似的红线,通常用 P95 或 P99 延迟来表达——95% 或 99% 的请求必须在某个时间上限内返回。动态批处理虽然提升吞吐,但也给请求增加了“等拼桌”的时间。所以调优不是一味地增大批次和超时窗口,而是要在 SLA 红线内做文章。

端到端延迟由排队等待、批处理计算和网络传输三部分组成。设延迟预算为 $T_{SLA}$,排队等待为 $T_{queue}$,网络传输为 $T_{network}$,则批次大小上限可以由 SLA 倒推出来:

火锅店宁可少拼一桌,也绝不让客人等超过五分钟。推理服务同样如此——一个突破了 P95 红线的模型,无论吞吐多漂亮,都是失败的部署。调优时先问自己:SLA 是多少?排队和网络占了多少?剩下的时间能支撑多大的批次?把预算算清楚,再回头调批次大小和超时窗口。

Bmax=TSLA−Tqueue−TnetworkTcomp\displaystyle B_{max}=\frac{T_{SLA}-T_{queue}-T_{network}}{T_{comp}}
公式说明:从总延迟预算中扣除排队和网络开销,剩下的时间除以单请求计算耗时,就是批次大小的上限。超过这个值,P95 延迟就会越线。
  1. 可分配时间$T_{SLA}-T_{queue}-T_{network}$ 是在排队和网络之外,留给 GPU 计算的时间
    TSLA−Tqueue−TnetworkT_{SLA}-T_{queue}-T_{network}
  2. 除以单请求耗时$T_{comp}$ 是单个请求在 GPU 上的计算耗时
    TcompT_{comp}
  3. 得批次上限相除得到在 SLA 约束下的最大批次大小
    TSLA−Tqueue−TnetworkTcomp\frac{T_{SLA}-T_{queue}-T_{network}}{T_{comp}}

监控调优

火锅店打烊后,老板会掏出账本复盘:今晚翻台率多少?平均上菜时间几分钟?有没有客人等太久给差评?哪张桌子空置最久?这些数据决定了明天的拼桌策略。没有账本,一切调整都是拍脑袋。

推理服务同样需要“账本”来支撑调参决策。核心监控指标有四个:吞吐量(每秒处理请求数)、P95 延迟(SLA 是否越线)、GPU 利用率(资源是否吃饱)、队列积压(攒批是否过度)。这些指标实时跳动,直接观察容易被噪声干扰。ONNX Runtime 在实际调优中常采用指数移动平均(EMA)对监控数据做平滑处理:

火锅店老板根据账本发现:周末翻台率低是因为拼桌等待太久,于是把三分钟规则改成两分钟,果然差评少了。调优动态批处理也是一样的循环:观察监控指标,发现瓶颈,调整批次大小或超时窗口,再观察指标反馈。日拱一卒,滚动优化,才能让批处理策略始终贴合业务的真实流量节律。

x^t=α x^t−1+(1−α) xt\displaystyle \hat{x}_{t}=\alpha\,\hat{x}_{t-1}+(1-\alpha)\,x_t
公式说明:新估计值由上一时刻的平滑值和新观测值加权融合,$\alpha$ 越大,曲线越平滑但反应越迟缓。
  1. 平滑系数$\alpha$ 决定历史数据的权重,通常取 0.8~0.99
    α\alpha
  2. 上一时刻平滑值$\hat{x}_{t-1}$ 是前一步的平滑结果,代表历史趋势
    x^t−1\hat{x}_{t-1}
  3. 加权融合历史趋势乘 $\alpha$,新观测乘 $(1-\alpha)$,相加得到最新平滑值
    α x^t−1+(1−α) xt\alpha\,\hat{x}_{t-1}+(1-\alpha)\,x_t

AI 应用场景

大语言模型在线推理服务

阶段:模型推理部署与性能优化

解决的问题:大语言模型推理时显存占用极高,单请求独占 GPU 资源利用率极低。大量用户同时提问时,若一个一个处理,GPU 大部分时间浪费在反复加载权重上。ONNX Runtime 动态批处理把并发用户的提示词合并成批次一起推理,显著提升吞吐。

具体案例:某智能客服平台接入 LLM 后,高峰期每秒收到 30 个用户请求。开启动态批处理,设置批次大小 8、超时窗口 300ms 后,GPU 利用率从 25% 提升到 78%,单卡每秒处理的请求数提高近三倍,P95 延迟仍稳定在 2 秒内。

智慧城市视觉检测服务

阶段:端到端推理服务搭建

解决的问题:城市摄像头每小时产生海量视频帧,目标检测模型需要逐帧分析。每帧的推理时间很短,但启动开销占比高,逐个处理浪费严重。动态批处理将多个摄像头传来的帧攒成批次,一次推理完成多帧检测。

具体案例:某安防平台接入 200 路摄像头,帧率 10 FPS。ONNX Runtime 配置批次大小 16、超时窗口 50ms 后,单张 GPU 从只能处理 60 路视频流提升到处理 180 路,硬件成本直接缩减三分之二。

电商推荐系统在线打分

阶段:在线推理的性能调优

解决的问题:推荐系统在用户刷新首页瞬间需要给数千个候选商品打分,请求呈明显潮汐式到达。大促期间请求暴增,闲时请求稀疏。动态批处理的超时窗口设计完美适配这种波动,繁忙时靠批次满发,空闲时靠超时兜底。

具体案例:某电商平台大促期间推荐调用量涨了 20 倍,通过 ONNX Runtime 动态批处理,将批次大小从 32 调至 128、超时窗口从 10ms 调至 50ms,在同样的 GPU 集群上扛住了流量峰值,整体吞吐提升 5 倍。

Python 演示

python83 行
import random  class DynamicBatcher:    """模拟 ONNX Runtime 动态批处理调度器。"""     def __init__(self, max_batch_size, timeout, process_time_per_item):        self.max_batch_size = max_batch_size        self.timeout = timeout        self.process_time_per_item = process_time_per_item        self.batch_count = 0        self.latency_sum = 0.0     def simulate(self, arrival_times):        """arrival_times 为升序排列的请求到达时间列表。"""        results = []        idx = 0        t = 0.0        pending = []        batch_open = None         while idx < len(arrival_times) or pending:            while idx < len(arrival_times) and arrival_times[idx] <= t:                pending.append(arrival_times[idx])                idx += 1             if not pending:                if idx < len(arrival_times):                    t = arrival_times[idx]                continue             if batch_open is None:                batch_open = t             if len(pending) >= self.max_batch_size:                t = self.dispatch(pending[:self.max_batch_size], t, results)                pending = pending[self.max_batch_size:]                batch_open = t            else:                deadline = batch_open + self.timeout                if idx < len(arrival_times) and arrival_times[idx] < deadline:                    t = arrival_times[idx]                else:                    t = deadline                    t = self.dispatch(pending, t, results)                    pending = []                    batch_open = t        return results     def dispatch(self, batch, start_time, results):        finish = start_time + len(batch) * self.process_time_per_item        for req in batch:            results.append((req, start_time, finish))            self.latency_sum += finish - req        self.batch_count += 1        return finish  def generate_arrivals(rate, duration):    """以给定速率生成泊松到达的请求时间序列。"""    times = []    t = 0.0    while t < duration:        t += random.expovariate(rate)        if t <= duration:            times.append(t)    return times  if __name__ == "__main__":    random.seed(42)    requests = generate_arrivals(rate=5.0, duration=60.0)    batcher = DynamicBatcher(max_batch_size=4, timeout=0.4,                             process_time_per_item=0.08)    result_list = batcher.simulate(requests)    count = len(result_list)    avg_latency = batcher.latency_sum / count if count else 0.0    end_time = max(f for _, _, f in result_list) if result_list else 0.0    throughput = count / end_time if end_time else 0.0    print("请求总数:", count)    print("合批次数:", batcher.batch_count)    print("平均延迟:", round(avg_latency, 3))    print("吞吐量:", round(throughput, 3))
目的:模拟一个动态批处理器在随机到达请求下的表现,输出批次数、平均延迟和吞吐量,直观感受批次大小和超时窗口对服务性能的影响。
  1. while idx < len(arrival_times) or pending:只要还有请求没到达或队列中还有请求,调度器就持续运转。
  2. while idx < len(arrival_times) and arrival_times[idx] <= t:把当前时刻之前到达的请求全部收入待处理队列,等价于服务员把新到客人领到等位区。
  3. batch_open = t记录当前批次开始攒批的时刻,超时窗口从这一刻开始倒计时。
  4. if len(pending) >= self.max_batch_size:队列里的请求攒够了批次上限,立即发批,不再等待超时。
  5. deadline = batch_open + self.timeout计算超时截止时间:从批次打开时刻加上超时窗口。
  6. if idx < len(arrival_times) and arrival_times[idx] < deadline:下一个请求会在超时前到达,那就推进时间到它到达的时刻,继续攒批。
  7. t = self.dispatch(pending, t, results)超时到期或批次已满,把当前攒的请求作为一批送进"GPU"处理。
  8. finish = start_time + len(batch) * self.process_time_per_item批次处理耗时等于请求数乘以单个请求计算时间,模拟批内顺序计算。
  9. req, start_time, finish每个请求记录到达时间、批次开始时间和完成时间,为后续延迟统计提供数据。
  10. random.expovariate(rate)生成符合泊松过程的时间间隔,模拟真实世界中“偶然成群”的请求到达模式。

常见误区

误区既然拼桌能提高效率,那批次大小设成 256、512,是不是吞吐量就能无限翻倍?反正显存够用就行。

正解批次增大带来的吞吐收益是递减的,吞吐量最终会逼近渐近线 $1/b$。超过拐点后,批次增大的唯一效果是延迟变长、显存吃紧,甚至因中间张量过大导致 OOM。火锅店的六人桌坐十个人,菜还没上齐客人就先吵起来了。

为何常见:直觉上“一次干更多活”一定更快,忽略了固定开销被摊薄到一定程度后不再是主要矛盾。

误区超时窗口设大一点,请求就有更多时间凑满批次,GPU 利用率一定更高,吞吐也一定更好。

正解超时窗口过长,请求在队列里干等,P95 延迟飙升,甚至超过 SLA 红线。而且当请求已经攒满批次时,超时窗口根本不起作用——批次满了立刻发,根本等不到超时。超时窗口只在请求稀疏时才是“主角”。

为何常见:把超时窗口误解为“攒批的保证”,忽略了它只是兜底机制,真正决定发批时机的是“批次满”和“超时到”哪个先发生。

误区动态批处理收益这么明显,不管什么业务,部署 ONNX Runtime 后直接把批处理打开就完事了。

正解动态批处理本质上是用延迟换吞吐。实时语音交互、自动驾驶等延迟极度敏感的场景,每增加 10ms 等待都不可接受,批处理反而有害。是否开启,取决于业务是“吞吐敏感型”还是“延迟敏感型”。

为何常见:只看到批处理提升吞吐的收益,忽略了它引入的额外等待时间,没有先做延迟预算分析。

误区上线前把批次大小和超时窗口调好,以后就一直用这套参数,不用再动。

正解业务的请求到达模式会随时间变化——白天高峰、深夜低谷、大促爆量。一套参数不可能在所有流量特征下都最优。正确的做法是持续监控吞吐量、P95 延迟和队列积压,根据趋势动态调整参数。

为何常见:把调优看作一次性的配置任务,而没有意识到流量环境本身是动态演变的。

面试题

Q1. 动态批处理的核心目标是什么?

单选题 · easy

  • A.合并请求提交GPU并行处理← 正确答案
  • B.拆分请求提升推理精度
  • C.减少模型权重文件大小
  • D.压缩特征向量降低开销

答案:A。动态批处理的核心是先把多个独立请求合并为一个批次,再一次性送入 GPU 计算,让固定开销只付一次。拆分请求、压缩权重都不是它的目标。就像火锅店把客人拼桌后一锅炒多份饭,而不是把一份饭分到几个锅里。

Q2. 增大批次大小后,每个请求的平均等待时间通常会?

单选题 · easy

  • A.批次内请求等待时间变长← 正确答案
  • B.GPU资源利用率持续下降
  • C.模型推理精度明显提升
  • D.服务吞吐量大幅减少

答案:A。批次越大,单个请求需要等整个批次的所有请求计算完成才能返回,所以平均等待时间变长。这是“用延迟换吞吐”的代价,也是调优时必须权衡的核心矛盾。

Q3. 关于动态批处理,下列说法正确的是?

单选题 · easy

  • A.批次越大延迟一定越低
  • B.超时越长响应一定越快
  • C.批次和超时共同权衡延迟← 正确答案
  • D.动态批处理导致精度损失

答案:C。动态批处理通过批次大小和超时窗口两个旋钮,在吞吐量与延迟之间寻求平衡。批次大小决定一次处理多少请求,超时窗口决定愿意等多久,两者共同影响端到端延迟。批处理不改变数值计算过程,不会损失精度。

Q4. 请求稀疏场景下,合理的超时窗口策略是?

单选题 · mid

  • A.设置较短超时及时响应
  • B.保持默认批处理参数
  • C.设置较长超时攒够批次← 正确答案
  • D.直接关闭动态批处理

答案:C。请求稀疏时,批次很难自然攒满,如果超时窗口太短,批次总是“几个人就发车”,固定开销摊薄效果差。如果延迟 SLA 允许,设置更长的超时窗口能攒到更多请求,提高 GPU 利用率。若 SLA 极严,则干脆不批处理。

Q5. 已知固定开销为 2ms,每请求计算耗时 1ms,批次大小为 8 时每个请求的均摊时间为?

单选题 · mid

  • A.每个请求均摊1.25毫秒← 正确答案
  • B.每个请求均摊2毫秒
  • C.每个请求均摊10毫秒
  • D.每个请求均摊8毫秒

答案:A。代入公式 $(2 + 8 \times 1) / 8 = 10 / 8 = 1.25$ 毫秒。固定开销 2ms 被 8 个请求摊薄,每个请求只需额外承担 0.25ms。这正体现了拼桌摊薄开火成本的数学原理。

Q6. GPU 利用率公式为 $U = \frac{B \times T_{comp}}{T_{overhead} + B \times T_{comp}}$,当批次大小趋向无穷时,利用率趋近于?

单选题 · mid

  • A.利用率趋近于零值
  • B.利用率趋近于1← 正确答案
  • C.利用率趋近于计算时间
  • D.利用率趋近于固定开销

答案:B。当 B 趋向无穷大时,分母中的固定开销 $T_{overhead}$ 相对于无限增大的有效计算时间可以忽略不计,分子分母都趋近于 $B \times T_{comp}$,比值趋近 1。这说明批次越大,GPU 越接近满负荷运转。

Q7. 延迟 SLA 为 50ms,排队耗时 10ms,网络耗时 5ms,单请求计算耗时 2ms,批次大小的最大合理值约为?

单选题 · mid

  • A.最大批次约为17个← 正确答案
  • B.最大批次约为35个
  • C.最大批次约为50个
  • D.最大批次约为25个

答案:A。由公式 $B_{max} = (T_{SLA} - T_{queue} - T_{network}) / T_{comp}$,代入得 $(50 - 10 - 5) / 2 = 35 / 2 = 17.5$,向下取整为 17。超过这个值,P95 延迟就会突破 SLA,相当于客人等菜时间超过了红线。

Q8. 动态批处理带来的收益包括哪些?

多选题 · easy

  • A.显著提高GPU使用率← 正确答案
  • B.提升系统整体吞吐量← 正确答案
  • C.保证响应延迟恒定不变
  • D.摊薄每批次的固定开销← 正确答案

答案:ABD。动态批处理把多个请求合并成批次,固定开销只付一次,GPU 有效计算占比提升,单位时间能处理的请求数增多。但延迟不是恒定的——攒批等待本身就会引入波动,因此 C 表述错误。

Q9. 以下哪些场景不适合开启动态批处理?

多选题 · mid

  • A.实时语音交互延迟敏感← 正确答案
  • B.请求稀疏且严格SLA约束← 正确答案
  • C.单笔业务不可等待合并← 正确答案
  • D.离线批量处理海量日志

答案:ABC。实时语音交互、严格 SLA、单笔业务等待合并的场景都以延迟为第一优先级,批处理引入的等待时间是致命的。离线批量日志处理恰恰是动态批处理的最佳战场——吞吐优先,延迟无所谓。

Q10. 动态批处理调优时需要考虑哪些约束条件?

多选题 · hard

  • A.显存容量约束批次上限← 正确答案
  • B.延迟SLA限制超时窗口← 正确答案
  • C.请求到达模式影响攒批← 正确答案
  • D.模型精度随批次动态变化

答案:ABC。显存决定批次能开多大,SLA 决定超时窗口能开多长,请求到达模式决定攒批的效率和等待策略。而模型精度只取决于权重和输入,与是否批处理无关,因此 D 不正确。调优就是在 A、B、C 三个约束下寻找最优平衡点。