部署机器学习模型时,最主要的架构考量集中在一个基本矛盾上:最小化延迟与最大化吞吐量。这两个目标常常相互冲突,而最佳平衡完全取决于您的应用需求。专为即时欺诈检测设计的架构,与为夜间处理用户上传视频而构建的架构会截然不同。了解这种权衡是构建成功且经济的推理服务的第一步。区分延迟与吞吐量在我们分析架构模式之前,准确定义我们的性能指标非常必要。在模型服务中,这些术语具有特定的含义,指导着工程决策。延迟 是处理单个推理请求所需的时间。它通常从服务接收请求的那一刻起,到响应发送回去的那一刻为止进行测量。对于面向用户的应用,这通常受严格的服务水平目标(SLO)约束,例如99百分位延迟($p99$)低于100毫秒。这意味着100个请求中有99个必须比此阈值更快完成。我们还必须考虑冷启动延迟,这是第一个需要将模型载入内存或预热新计算实例的请求所产生的额外延迟。吞吐量 是服务在给定时间内可以处理的推理请求总数,通常以每秒请求数(RPS)或每秒推理数衡量。吞吐量是系统容量的衡量标准,与成本效益直接相关。高吞吐量系统能有效使用其底层硬件(如GPU),以相同的固定成本处理更多数据。核心难点在于,用于提高吞吐量的技术,例如以大批量处理请求,本身会增加任何单个请求的延迟。推理架构模式您的架构选择将介于纯粹的延迟优化和纯粹的吞吐量优化之间。延迟优化架构:在线、实时推理对于即时响应要求高的应用,例如交互式聊天机器人、实时竞价或驾驶辅助系统,延迟是主要的衡量指标。目标是尽可能快地处理每个传入请求,并使延迟最小化。在设计推理服务时,为了有效平衡延迟和吞吐量,通常会采用以下几种架构方法:单请求处理: 每个请求在到达后立即发送到模型进行推理。避免批量处理,以防止请求相互等待。横向扩展: 为处理并发用户,服务依赖于运行多个模型副本。负载均衡器将传入请求分发到这些副本上。合适规模的硬件: 该架构可能不使用单个大型GPU,而是使用多个更小、功率更低的GPU,甚至CPU。如果单个请求无法使大型GPU饱和,那么使用它就会造成浪费,甚至增加额外开销。这种方法的主要缺点是硬件利用率低。现代GPU是一种大规模并行处理器,旨在同时处理大量数据。发送批处理大小为1的单个请求会使大部分计算核心处于空闲状态,导致每次推理的成本很高。digraph G { rankdir=TB; splines=ortho; node [shape=box, style="rounded,filled", fontname="sans-serif", fillcolor="#a5d8ff"]; edge [fontname="sans-serif"]; subgraph cluster_client { label = "客户端"; bgcolor="#e9ecef"; c1 [label="请求 1"]; c2 [label="请求 2"]; c3 [label="请求 3"]; } lb [label="负载均衡器", shape=diamond, fillcolor="#96f2d7"]; subgraph cluster_replicas { label = "模型副本 (批处理大小 = 1)"; bgcolor="#e9ecef"; rank=same; r1 [label="副本 A\n(GPU/CPU)"]; r2 [label="副本 B\n(GPU/CPU)"]; r3 [label="副本 C\n(GPU/CPU)"]; } c1 -> lb; c2 -> lb; c3 -> lb; lb -> r1 [label=""]; lb -> r2 [label=""]; lb -> r3 [label=""]; }延迟优化架构将单个请求分发到多个模型副本,以确保即时处理。吞吐量优化架构:离线、批量推理对于不关注延迟的离线任务,目标转变为最大化效率和最小化总成本。例子包括为文档语料库生成嵌入、预先计算夜间用户推荐,或分析一批医学图像。这种架构的特点是:大型静态批处理: 请求在队列中收集,并以大型、预先确定的批次一起处理。批处理大小为64、128甚至更高都很常见。最大化硬件使用: 通过向GPU提供大批量数据,其并行处理能力得到充分发挥,从而大幅增加每秒执行的推理数量并降低每次推理的成本。基于作业的执行: 这些工作负载通常作为计划作业(例如,夜间cron作业)运行,这些作业会启动计算资源,处理队列,然后关闭。明显的权衡是极高的延迟。刚好在批处理开始后到达的请求,必须等待整个当前批处理完成并填满新批处理后才能被处理。混合方法:动态批处理大多数现代服务需要一种实际的平衡:它们需要响应迅速,但也要具有成本效益。这正是动态批处理发挥作用的地方。它提供了一个中间方案,在只带来最小、可控的延迟增加下提升吞吐量。该机制简单而有效:当请求到达时,推理服务器不会立即处理它。相反,它会等待一个非常短的、可配置的时间段(例如5-10毫秒),以查看是否有其他请求到达。所有在此时间窗口内到达的请求都会被分组(批处理)在一起,并发送到模型进行一次并行推理。digraph G { rankdir=TB; node [shape=box, style="rounded,filled", fontname="sans-serif"]; edge [fontname="sans-serif"]; subgraph cluster_incoming { label = "传入请求 (时间线)"; bgcolor="#e9ecef"; node[shape=none, image="https://raw.githubusercontent.com/FortAwesome/Font-Awesome/6.x/svgs/solid/envelope.svg", label="", width=0.3, height=0.3, fixedsize=true, imagescale=true]; r1; r2; r3; r4; r5; {rank=same; r1 -> r2 -> r3 -> r4 -> r5 [style=invis]}; } server [label="推理服务器\n(动态批处理器)", fillcolor="#ffc9c9", shape=box3d]; subgraph cluster_processing { label = "处理中"; bgcolor="#e9ecef"; batch1 [label="批处理 1\n{请求 1, 2, 3}", fillcolor="#b2f2bb"]; batch2 [label="批处理 2\n{请求 4, 5}", fillcolor="#b2f2bb"]; gpu [label="GPU 执行", shape=cylinder, fillcolor="#bac8ff"]; } r1 -> server; r2 -> server; r3 -> server; r4 -> server; r5 -> server; server -> batch1 [label="批处理\n窗口 1"]; server -> batch2 [label="批处理\n窗口 2"]; batch1 -> gpu; batch2 -> gpu; }动态批处理将时间上接近的请求分组,以轻微的延迟权衡来提高GPU利用率。这种技术是高性能服务的基本组成。通过牺牲几毫秒的延迟,您可以大幅提高服务的吞吐量。例如,您可能会将$p99$延迟从15毫秒变为25毫秒,但这样做可以将服务器的容量从100 RPS提高到400 RPS。这种权衡对于并非处于实时要求最前沿的服务几乎总是值得的。在延迟-吞吐量之间选择合适的点,为所有后续优化做好了准备。一旦您心中有了架构模式,接下来的步骤(我们将在后续章节中介绍)包括优化模型本身,并使用Triton等专用服务软件有效实现这些模式。