解密LLM大模型推理优化本质
一、LLM推理的本质以及考量点
LLM推理聚焦Transformer架构的Decoder以生成文本。过程分两步:首先,模型初始化并加载输入文本;接着,进入解码阶段,模型自回归地生成文本,直至满足停止条件或序列长度上限。这一流程高效且精确,为文本生成提供了强大支持。
自回归模型是什么?
自回归模型专注于单一变量的历史数据,无需外部变量,仅基于该变量过往表现进行预测。它广泛应用于时间序列数据分析,如股票价格、气象数据等,未来值与历史值紧密相关,展现精准预测力。
KV缓存的应用与挑战
KV缓存是突破LLM推理中计算瓶颈的关键,通过存储键值对,大幅减少重复计算,将注意力层复杂度从二次方降至线性。然而,它也带来了内存与数据传输成本的提升,需权衡性能与资源消耗。
硬件利用与性能优化
在LLM推理中,高效利用硬件资源至关重要。借助算术强度和屋顶模型,我们精准匹配硬件运算能力与内存带宽,优化模型延迟、吞吐量和成本效益。此外,通过量化和fused kernels等策略,模型性能再获提升,为高效推理提供强大支撑。
Serving frameworks 的选择
部署LLM推理解决方案时,serving frameworks至关重要。其高效管理硬件资源与请求,实现端到端性能的最优化。深入理解其工作原理,有助于精准配置与优化LLM部署,确保系统高效运行。
二、LLM响应背后的两阶段过程
以序列为单位(批量大小为1),下图展现了基于Transformer的标准解码器核心层,它负责从输入令牌序列中生成输出令牌。
解码器模型结构
输入数据为多组数字(如3923、1933),每组代表不同信息,如编码后的单词或字符,实现精准数据输入。
2.嵌入层(Embedding Layer)
在解码器模型的开始部分,通常会有一个嵌入层。这一层的作用是将输入的数字(通常是单词或字符的索引)转换为一个更高维度的向量表示。这些向量能够更好地捕捉和表示单词之间的复杂关系,是处理自然语言的关键步骤。
维度扩展:嵌入层将每个输入的索引(如3923、1933等)映射到一个高维空间的向量。这些向量通常包含更多的信息和上下文,有助于模型理解和处理语言。信息丰富:通过这种方式,模型能够学习到单词之间的相似性和差异性,例如同义词可能会被映射到彼此接近的向量。
3. 位置编码(Positional Encoding):
位置编码融入输入数据,增强模型对单词句内位置的感知。这在语言处理中至关重要,因为单词意义常与其在句中的位置紧密相关。
3.解码器块(Decoder Blocks):
数据流经解码器块,逐层处理、提取并转化信息,内含注意力机制和神经网络层,显著提升模型对上下文的理解能力。
4.线性层(Linear Layer):
5.Softmax层:
6.Logits到Target:
Softmax层将概率分布(Logits)转化为最终输出,明确指向特定单词或字符。在此例中,输出数字578,对应某特定单词或字符,确保结果精准明确。
解码器终端,语言模型头部(LM头部)作用关键。它依托前期生成的概率分布(Logits),择取最高概率输出为结果。此步骤将模型内数值转化为直观文本,如单词或字符,使模型输出更直观、实用,提升用户体验。
接下来介绍几种解码实现。
1. Greedy Decoding(贪婪解码)
在单词选择游戏中,贪婪解码策略让你总是挑选当前最可能的单词,如同餐馆里的热门菜品。但为求独特与趣味,你或许会避免重复选择,这就是“重复惩罚”的魅力。通过微调单词的对数概率,使常用词失去吸引力,打破常规,构建更富创意的句子。
2. Sampling Decoding(采样解码)
想象一下,一个装满彩球的罐子,每球代表一词,大小象征选择概率。采样解码即闭眼随机抽取,赋予句子生成无限随机与多样性。通过调整彩球大小,你能巧妙操控游戏,让乐趣倍增。
- Top-k:只保留最大的k个彩球,其他的全部拿走。
- Top-p(nucleus sampling):精选最大概率彩球,累积至超过p即止,其余舍弃,精准筛选,提升生成质量。
3. 更复杂的解码策略
- 束搜索(Beam Search)打破传统,不再逐一选取,而是并行抽取多个(根据束宽确定),并基于这些选择逐步构建多个潜在句子,最终挑选出整体最佳的一句,高效且精准。
- 对比解码,一种卓越策略,它超越了仅考量词语概率的范畴,深度聚焦整体句子或文本段的质量,力求创造出独特且质量上乘的结果,与众多潜在选项鲜明对比。
为了简单起见,我们将假设解码策略是模型的一部分。
如何生成多个令牌?基于Transformer的解码器可从输入文本序列(即prompt)高效生成文本(即completions),主要步骤涵盖从prompt中解码并生成文本。
- 将模型权重加载到GPU
- 在CPU上对提示进行Tokenizing 并将标记张量转移到GPU
通过运行tokenized prompt,神经网络高效生成完成文本的首个标记,实现精准内容预测,提升文本生成效率与准确性。
将生成的令牌连续附加至输入序列,循环此步骤以生成补全的下一个令牌,直至遇到停止序列(如EOS令牌)或满足设定的最大序列长度限制。
多步骤生成过程涵盖生成、解码、自回归和增量阶段。最终,提取completion令牌至CPU,经反分词处理,高效生成精准文本。
启动阶段与解码阶段的比较
- 相似性:
- 这个过程看起来像是一个while循环,其中启动阶段可以被看作是循环的初始化步骤。
- 区别感知:
性能和优化
- 随着序列长度增长,计算量呈二次方激增。文本越长,每增一令牌,数据处理量与计算复杂度均显著攀升,考验着系统的处理能力。
- 计算冗余显著,新令牌生成时,大量先前计算被重复执行,而结果早已得出。优化流程,避免不必要的计算,提升效率至关重要。
解决方法:KV缓存
- 缓存优化:引入KV缓存技术,高效存储文本生成中可重用的计算结果,大幅减少重复计算,显著提升性能,确保高效运行。
结论
启动与解码阶段虽操作相似,但借助KV缓存等优化技术,我们能高效应对计算需求,显著提升性能。敬请期待后续文章,深入解析这些技术的具体应用之道。
三、 解释KV缓存
自然语言处理中,单词向量化包含两步:转换为令牌ID,再将ID转为密集向量。简化而言,就是单词到ID,再到向量的转换过程。
步骤1: 单词到令牌ID
- 词汇表构建关键:基于训练数据集,构建包含所有唯一单词的词汇表,确保每个单词拥有独特索引,为数据处理奠定坚实基础。
步骤2: 令牌ID到向量
- 嵌入层是模型的关键部分,负责将令牌ID转化为密集向量。它包含一个预设大小的嵌入矩阵,每行映射一个令牌ID的向量表示,确保信息的高效转化与存储。
- 输入令牌ID,嵌入层迅速将其转化为精准密集的向量表示。例如,ID为1的令牌,将直接映射至其专属的向量数据,实现高效且准确的转换。
例子
- 单词 "apple" 被转换为令牌ID 1。
- 令牌ID 1在嵌入层映射为[0.32, -1.22, 0.66]向量。此两步标准化处理文本数据,并捕捉单词的语义和语法特性。这些学习向量助力模型训练和推理,执行分类、翻译或文本生成等任务。
为什么需要这样做的几个关键原因:
- 标准化处理——将单词转为唯一令牌ID,简化数据处理,实现词汇的高效索引与管理,确保大量词汇有序且易于操作。
- 内存优化:避免重复生成单词向量,通过令牌ID在嵌入矩阵中快速查找并重用相同向量表示,既节省内存又加速计算,显著提升数据处理效率。
- 模型凭借令牌ID与嵌入层设计,展现卓越灵活性与可扩展性。轻松调整嵌入矩阵大小,平衡复杂性与性能;实时更新矩阵以纳新词汇,无需重构整体模型,高效便捷。
- 预训练词嵌入为NLP任务提供强大助力:通过映射ID至向量,模型能汲取大规模数据集的语言智慧,显著提升特定任务的性能。将单词转化为令牌ID,进而映射至向量,不仅技术可行,且在实际应用中高效且效果显著。此方法已成为处理文本数据的行业标准,广泛应用于各类NLP模型与系统中。
在机器学习与自然语言处理中,密集向量是核心概念。与稀疏向量相反,密集向量大部分或全部元素非零,常用于数据点与特征表示,每个元素均承载有用信息,是数据处理和分析的重要工具。
计算挑战
- 针对生成步骤中的计算冗余,实施缓存策略,预先存储计算结果以减少重复计算。此举有效将计算需求从二次方增长降为与序列长度线性增长,显著提升效率。
Transformer注意力层的工作原理
- 向量表示:在处理过程中,输入序列中的每个令牌都被转换成一个密集向量。
- 注意力机制:
- 输入向量生成三个低维密集向量:查询(Q)、键(K)和值(V),分别对应查询、匹配和提取关键信息的高效机制。
- 注意力分数精准调控输出向量,它是值向量的线性组合,组合系数直接由注意力分数决定,实现值的注意力加权平均。
在自回归解码中,为维持文本连贯与逻辑,模型计算注意力时禁用后续令牌信息。通过掩蔽技术,将非当前查询相关值向量的注意力分数归零,确保文本生成的连贯与准确。
最终,每个注意力头的输出被连接起来,并通过最后一次线性变换产生最终输出。
为了简化说明,我们假设只处理一个长度为t的单一序列(即批次大小为1):在整个处理过程中,输入序列(prompt部分)中的每个令牌都被表示为一个密集向量(如图1中的浅黄色所示)。注意力层接收的输入是一系列密集向量,每个输入令牌对应一个,这些向量由前一个解码块产生。对于每个输入向量,注意力层将生成一个具有相同维度的单一密集向量(如图1中的浅蓝色所示)。
现在,让我们关注单个注意力头的工作机制:首先,我们对每个输入向量使用三种不同的投影方式生成三个低维密集向量,分别对应于查询(Query)、键(Key)和值(Value)(如图1中最左边的浅灰色向量所示)。总体上,我们将获得t个查询向量、t个键向量和t个值向量。
对于每个查询向量,我们生成的输出向量是值向量的线性组合,而这个组合的系数就是注意力分数。换言之,每个查询向量对应的输出向量是根据注意力分数加权平均后的值向量。
具体来说,给定查询的注意力分数是通过该查询向量与每个键向量的点积计算得出的。通过这种方式,我们为序列中的每个令牌生成了一个包含其他令牌信息的表示,形成了每个令牌的上下文表示。
然而,在自回归解码的情景下,我们无法使用所有可能的值向量来构建给定查询的输出表示。实际上,在计算与特定令牌相关的查询的输出时,我们不能使用序列中后续令牌的值向量。这种限制是通过一种称为掩蔽的技术来实现的,该技术基本上将不允许使用的值向量的注意力分数设置为零。
最终,每个注意力头的输出被合并,并通过一个最后的线性变换来生成最终的输出结果。
注意力计算的二次方扩展
在注意力机制及Transformer架构中,d_head定义每个注意力头处理的特征/嵌入向量的维度。其中,"d"代表"维度","head"指注意力"头",精确刻画了模型处理信息的精细程度。
注意力头和d_head的作用:
- 多头注意力机制:
- Transformer模型引入革新的多头注意力机制,突破传统限制。该机制将Query、Key和Value向量拆分为多个“头”,每个头独立处理输入数据的不同“视角”,显著提升模型捕捉复杂依赖关系的能力,展现卓越性能。
- 在多头注意力机制中,若特征维度(d_model)为512,且采用8个注意力头,则每头处理的特征维度d_head为64(即512/8)。这种方式确保每个头独立作用于64维的子空间,提高了模型处理复杂特征的能力。
- 计算效益和表达力:
- 通过分割特征空间为多个子空间,每个由独立“头”处理,模型能并行学习数据的多维特征,显著提升表达力和计算效率。各“头”精准捕捉数据模式,整合输出,综合呈现最终结果。
- 具体应用:
- 在进行注意力计算时,每个头会独立地生成其对应的Query、Key和Value向量。然后,每个头使用其 d_head 维的Query和Key向量计算注意力分数,进而使用这些分数来加权其Value向量。最终,所有头的输出被聚合(通常是拼接后再进行线性变换),形成最终的输出。
总的来说,d_head 是Transformer架构中定义每个注意力头处理数据的维度大小的参数,这个参数对于多头注意力的实现至关重要,影响着模型的表达能力和计算效率。
- 在进行注意力计算时,每个头会独立地生成其对应的Query、Key和Value向量。然后,每个头使用其 d_head 维的Query和Key向量计算注意力分数,进而使用这些分数来加权其Value向量。最终,所有头的输出被聚合(通常是拼接后再进行线性变换),形成最终的输出。
在注意力机制中,涉及的矩阵乘法是使用Query (Q) 和Key (K) 张量:
- 计算注意力分数:
我们计算“注意力分数”或“权重”,即Query (Q) 矩阵与Key (K) 矩阵转置的乘积,此法精准评估序列元素间的关联性,为信息筛选与聚焦提供核心依据。- 当处理Query矩阵Q(t, d_head)与Key矩阵K(t, d_head)时,计算注意力分数需用到K的转置K.T(d_head, t)。Q与K.T的矩阵乘法将生成一个(t, t)形状的新矩阵,其中每个元素精准地反映了序列中元素间的注意力权重。这一步骤不仅确保了计算的准确性,还高效捕捉了序列间的依赖关系。
- 使用注意力权重处理Value (V) 矩阵:
- 注意力分数矩阵经计算后,直接用于加权Value (V) 矩阵中的元素。通过矩阵乘法操作,结合注意力分数矩阵与Value (V) 矩阵(通常形状为(t, d_head)),实现信息的有效整合。
- 注意力分数矩阵的形状为 (t, t),Value矩阵的形状为 (t, d_head),所以通过矩阵乘法(注意力分数矩阵 (t, t) 乘以 V (t, d_head))将得到一个新的形状为 (t, d_head) 的矩阵,其中包含了根据注意力权重调整过的值。
综上所述,在注意力机制中,KVQ并不直接代表矩阵乘法的过程。实际上,K和Q用于生成注意力分数,然后这些注意力分数用于通过矩阵乘法调整或加权V矩阵中的值。这是Transformer模型中注意力机制的核心运作方式。
让我们看看计算注意力分数所需的浮点运算次数(FLOPs)。对于一个给定head,对于batch of size为b、total length为t(包括prompt和生成的completions)的每个序列,通过将查询张量的形状为(t, d_head)与转置的键张量的形状为(d_head, t)相乘来创建注意力分数矩阵。
单次矩阵乘法需要多少FLOPs?乘以一个形状为(n, p)的矩阵和另一个大小为(n, m)的矩阵大约涉及2.m.n.p次操作。在我们的情况下,单头单序列注意力分数计算因此大约需要2.d_head.t^2 FLOPs。总的来说,注意力分数计算需要2.b.n_layers.n_head.d_head.t^2=2.b.n_layers.d_model.t^2 FLOPs。t的二次方扩展现在显而易见。
考虑实际数字,例如Meta的Llama2–7B,n_layers=32和d_model=4096。
关于模型权重矩阵乘法的注意
分析表明,该模型的计算复杂性为O(b.n_layers.d_model^2.t),即随序列长度t线性增长。以实例为证,生成第1001个令牌相较于第101个,计算量激增100倍,这种二次方扩展导致的计算指数增长,将迅速成为制约因素。
在序列处理中,特别是Transformer模型的自注意力机制下,每个令牌表示均基于序列内其他令牌信息计算得出。引入Masking技术后,模型可逐步处理输入序列,每步聚焦一个令牌,并充分利用已处理令牌信息。
以“What color is the sky? The sky is”为例,模型将逐一令牌处理,实现高效且精准的序列分析。
- 处理“What”时,由于它是第一个令牌,其表示只与自身有关。
- 处理“color”时,其表示可以利用“What”的信息。
- 处理“is”时,其表示可以利用“What”和“color”的信息。
- 依此类推,每个后续令牌在计算其表示时,都会考虑到它之前的所有令牌的信息。
到了处理“The sky is”部分时,前面部分“What color is the sky?”的每个令牌的表示已经在先前的步骤中计算过了。因此,当再次到达这些令牌时,如果令牌和上下文没有改变,其计算结果也会相同,从而造成了冗余计算。最后的令牌“is”是新出现的,因此它的表示还没有被计算过。
这种冗余计算的问题可以通过保存先前计算的结果来缓解,这种方法称为KV缓存。这样,每个令牌只需要在首次出现时计算一次,之后可以直接使用缓存的结果,避免重复计算。这种方法大大提高了计算效率,尤其是在处理长序列时更为明显。
拥有宛如巨型桌面般广阔的GPU内存和带宽,你可一展身手,瞬间拼接碎片并参考记录,数据处理之迅速超乎想象。但现实是资源有限,若GPU内存如小桌般局促,难以容纳拼图全貌和详尽记录。此时,你需抉择:是优化策略以高效利用有限空间,还是寻求创新方案以拓展处理能力?明智的选择将引领你突破局限,实现数据处理的新高度。
- 扩展桌子能力即增强GPU内存与带宽,实现信息存储倍增,加速处理流程。犹如升级桌面空间,容纳更多拼图与记录,但请注意,此提升将伴随成本增加。
- 经常清理桌子:这意味着你不扩展内存,但需要经常清理不再需要的信息,以腾出空间给新的拼图部件。这种方法会减少资源消耗,但每次清理和重新计算需要的位置时,你的拼图速度会减慢,因为你需要花时间去重新查找和确认那些之前已经放置好的部件的位置。这种计算上的花销就像是每次都需要重新计算拼图部件的信息,而不是直接从记录中获取。
所以,这就是权衡的所在:你是选择增加资源以保存更多信息,从而快速处理(但成本较高),还是选择减少资源使用,接受更慢的处理速度(但更节约成本)。在实际应用中,这需要根据具体的需求和资源情况来做出合理的选择。这就是为什么设计一个高效的缓存逐出策略是挑战性的,因为它需要在内存使用和计算效率之间找到一个平衡点。
四、推理的四种性能瓶颈
如果想提升推理性能,首先要做的就是确定性能瓶颈的类型。
性能瓶颈分四类,三类源于硬件限制,一类与软件相关。聚焦硬件瓶颈,每种瓶颈均有独特操作模式,亟待解析优化。
计算受限模式显著特点在于执行算术运算占据大部分处理时间。其成本主要源于计算,却因此凸显成本效益优势。鉴于此,我们应将优化目标聚焦于这一模式,以实现更高效能。
计算受限过程图(计算和数据传输时间分别用黄色和蓝色表示)
- 内存带宽受限模式显著影响性能,处理时间大量消耗在数据传输上,特别是在芯片内存与处理器间,如权重矩阵和中间计算结果的传输,凸显数据传输为性能瓶颈的关键因素。
内存带宽受限过程图(计算和数据传输时间分别用黄色和蓝色表示)
- 通信受限模式特指多芯片间计算与数据分布时,处理时间多用于芯片间网络数据传输(见图3)。这一模式揭示了在跨芯片计算与数据传输中,网络通信成为性能的主要瓶颈。
通信受限过程图(计算、数据传输和网络通信时间分别用黄色、蓝色和绿色表示)
出现重叠数据传输的通信受限过程
计算开销受限模式,这主要与软件引起的限制有关。在这种模式下,绝大多数的处理时间花在了调度工作和将任务提交给硬件上。换言之,我们花更多的时间去规划和准备工作,而不是直接在硬件上执行操作。这种情况在使用灵活性极高的编程语言(例如Python)或框架(例如PyTorch)时尤其常见,因为这些工具在运行时不需要明确指定所有所需信息(如张量数据类型、目标设备、调用的kernel等)。
缺少的信息必须在运行时进行推导,这会消耗CPU周期,称为计算开销。随着现代加速硬件的速度远超CPU,计算开销可能导致硬件利用率下降,从而影响成本效益——基本上,硬件有时会处于闲置状态,等待软件提交下一个工作项。
计算开销受限过程图(计算、数据传输和软件开销时间分别用黄色、蓝色和紫色表示)
执行模型的正向传播或反向传播时,会涉及到多个kernel的执行,这些kernel类似于在GPU上的函数调用。由于不是所有的kernel都以相同的模式运行,因此关键在于识别出大部分执行时间花费在哪种模式上。一旦确定了主要的性能瓶颈,优先事项应是针对这些瓶颈进行优化。之后,应逐步识别并解决下一个最重要的性能瓶颈,依此类推。准确地识别出瓶颈的类型至关重要,因为不同类型的问题需要不同的解决方案。如果判断错误,可能不仅会浪费大量时间,而且即使进行了某种优化并取得了一定的效果,最终的结果可能仍然不尽如人意。
判断限制因素
当评估程序的性能瓶颈时,一个关键的观察点是:如果程序主要受到计算开销的限制(瑞案件优化问题),那么即使增加计算量或数据传输量,程序的运行时间也不会相应增加。这表明,如果你增加了计算或数据传输的容量,但运行时间没有明显变化,那么程序很可能受到计算开销的限制。相反,如果运行时间因此延长,那么可能是硬件性能受到了限制。
为了区分这些不同的性能瓶颈,如计算限制与内存带宽限制,可以通过监测如FLOP计数和数据传输量等性能指标来实现。这通常需要借助性能分析工具来完成。
在讨论长语言模型(LLM)的上下文中,这一点尤其重要。在模型训练和推理的初始化阶段,性能通常受到计算限制。而在推理解码阶段,性能瓶颈多数情况下是内存带宽限制。因此,那些主要用于训练阶段的优化措施,如使用低精度矩阵乘法,如果应用于减少解码阶段的推理延迟,可能效果不佳,因为这些措施并不解决内存带宽的限制问题。
针对不同瓶颈类型的优化策略如下:
1. 计算受限模式:
- 升级硬件,选择性能卓越的芯片是关键。NVIDIA H100 PCIe凭借51 TFLOPS的CUDA核心峰值性能与高达378 TFLOPS的Tensor Cores计算力,为您提供更强大、更高效的计算体验。
- 采用低精度计算策略,如8位Tensor Cores,能显著提升FLOPS性能至1,513 TFLOPS,是16位的两倍、32位的四倍。但需注意,需对输入数据(权重矩阵、激活函数等)进行量化,并配备专用低精度kernel。此外,内存带宽受限模式亦是一种有效策略。
- 升级硬件:使用具有更高内存带宽的更强大、更昂贵的芯片。
- 精简数据大小:利用模型压缩技术如量化、剪枝或知识蒸馏,显著减少数据搬运量。针对长语言模型(LLM),采用权重量化技术(如GTPQ、AWQ)与KV Cache量化,实现高效压缩策略。
- 减少内存操作次数:通过kernel融合技术减少。这包括将多个操作合并到单个kernel调用中执行,从而减少对内存的读写次数。算子融合可以通过编译器自动完成,或通过手动编写复杂的kernel来实现。特别是在处理Transformer模型的注意力层时,开发高效的融合kernel是一个活跃的研究领域,其中许多优化的kernel基于FlashAttention算法。
以上策略有助于在遇到计算受限或内存带宽受限的情况时优化模型的执行效率,从而提高整体性能。
3. 通信受限模式:
- 升级硬件:选择具有更高网络带宽的更昂贵、更强大的芯片。
- 优化通信策略,采用高效分区和集合通信,减少通信量。张量并行策略提升通信时间扩展性,避免瓶颈。实践中,通过“权重聚合”分区策略,固定激活分片并移动权重分片,优化通信,尤其适用于大批量、大序列数据处理。同时,引入计算开销受限模式,提升性能。
- 使用更高效的编程语言:如C++,以换取更少的计算开销。
- 合并kernel提交:分组提交多个kernel,减轻跨kernel计算负担,尤其适用于需多次提交相同短命kernel组的迭代工作负载,显著提升效率。
- PyTorch 1.10起集成CUDA Graph,通过捕获代码块触发的GPU活动构建有向图,实现一次性提交,大幅减轻计算负载,提升性能。
- 优化计算图(AOT)为可部署工件,如利用PyTorch的torch.jit.trace追踪并打包为TorchScript,再经模型编译器优化。但请注意,此法牺牲灵活性,需确保输入参数(张量大小、类型等)为静态,以确保部署的顺畅与高效。
- JIT编译器赋能动态优化,灵活应对动态张量形状与控制流挑战。PyTorch 2.x的TorchDynamo JIT编译器,在维持Python开发便捷性的同时,有效降低计算成本。这些策略在通信受限或计算资源紧张时尤为关键,有效提升模型执行效率,强化整体性能。接下来,我们聚焦计算和内存带宽限制,探讨如何通过提升吞吐量,即单位时间内的请求处理能力,来进一步优化性能。
瓶颈性能由硬件与运算强度共同决定,简化公式:Bottleneck = f(Hardware, Arithmetic Intensity)。
瓶颈 = f(硬件,算术强度)
处理同输入与算法时,性能瓶颈源于计算或内存带宽,取决于硬件配置。算法算术强度,即每内存访问字节的算术操作数,决定适用模式。高算术强度带来更佳吞吐量和成本效益,但可能增加时延。在追求性能时,需权衡时延与吞吐量,确保系统处于成本效益最优的计算受限模式。
算术强度精准反映应用或算法特性,非直接评估硬件性能。它量化每次内存读写时,程序能执行的浮点运算量(FLOPs),即每字节传输数据伴随的FLOPs数量。此指标为应用程序性能优化提供关键依据,揭示内存访问与浮点运算效率的关联。
算术强度是理解应用程序在特定硬件上性能瓶颈的关键。通过它,我们精准识别计算受限或内存带宽受限的瓶颈,为优化提供明确方向。
算术强度虽关键,但掌握特定硬件如GPU的计算能力(FLOPS)和内存带宽更为重要。这些硬件特性直接决定应用性能上限。通过对比应用的算术强度与硬件的峰值FLOPS、内存带宽,我们能预见性能瓶颈,并据此精准优化,提升运行效率。
算术强度是衡量应用或算法的关键指标,紧密关联于硬件计算力与内存带宽,非硬件直接评价。掌握算术强度有助于针对特定硬件优化应用性能。
设定以下参数以更好地理解这一概念:
- b:每次执行从内存传输的数据字节数。
- p:每次执行所进行的浮点运算次数(FLOPs)。
- BW_mem(以TB/s表示):硬件的内存带宽。
- t_mem:移动数据字节所花费的时间。
- t_math代表算术运算耗时。当系统处于计算受限模式,t_math超过数据传输时间t_mem,表明提升系统性能关键在于增加计算复杂性或优化计算过程,而非提升内存带宽。针对特定算法与硬件,优化算术强度将推动系统向高效计算受限模式发展,实现吞吐量与时延的最佳平衡。
计算受限与内存带宽受限模式对比图(计算和数据传输时间分别用黄色和蓝色表示)
因此,当以下条件满足时,我们处于计算受限状态:
算术强度和硬件的关系
算术强度反映了处理每字节数据所需的计算量,高强度意味着更多计算。峰值FLOPS代表硬件计算速度的上限,而内存带宽则体现数据获取的速度。这两大指标共同构成了评估硬件性能的核心标准,直接关联到计算速度与数据处理效率。
为什么要比较算术强度和峰值FLOPS与内存带宽的比率?
执行计算任务时,若内存带宽如小水桶般有限,而算术强度高需求大,大部分时间将耗于数据获取而非实际计算。此时,瓶颈在于内存带宽。反之,若内存带宽如大水桶般充裕,但计算需求小,则主要时间将用于计算而非数据获取,此时限制在于计算能力。因此,在优化计算性能时,需综合考虑内存带宽与计算能力之间的平衡。
- 若您面临高强度算术需求,但峰值FLOPS与内存带宽比揭示计算能力不足以支撑数据处理,则您正面临计算受限的挑战。
- 算术强度低时,虽计算能力强大,但数据处理速度受限,这通常源于内存带宽的瓶颈。优化内存带宽,提升数据处理效率。
深入了解算术强度与硬件FLOPS峰值、内存带宽间的联系,能精准优化程序性能。计算密集型任务宜增强计算能力(提升FLOPS峰值),而内存带宽受限任务则应优化内存性能。精准施策,实现程序高效运行。
内存带宽 / 计算受限边界
以NVIDIA A10为判断算法是计算受限还是内存带宽受限。
- NVIDIA A10带宽比高达208,意味着其数据传输效率极高,每传输1字节数据的时间,相当于执行了208次浮点运算,彰显强大性能。
- 算术强度判定:NVIDIA A10上运行的算法,若每字节传输少于208次FLOPs,则可能未充分利用GPU算力。因GPU在完成计算后需等待更多数据,内存带宽或成性能瓶颈。但若算法设计本身不需密集计算,则非带宽受限,而是算法计算需求低。准确评估算法与硬件匹配度,优化性能至关重要。
- 长语言模型(LLM)在推理解码阶段算术强度低,导致其在多数硬件上内存带宽受限,成为性能瓶颈,需优化提升算术强度以改善性能。
- NVIDIA H200展现卓越优势,针对低算术强度工作负载提供更高带宽比率。NVIDIA将其定位为“生成式AI推理加速器”,凸显其处理内存带宽受限场景的高效能力,彰显硬件选择的重要性。
现在我们将算术强度与时延和吞吐量联系起来:
屋顶线模型图
图中解释的关键点:
- 横轴(算术强度op/byte)直观呈现算术强度,即每字节数据传输伴随的操作数(FLOPs)。随着横轴数值攀升,算术强度同步增强,凸显数据处理的高效与精准。
- 纵轴(Throughput in TFLOPS)代表吞吐量,衡量硬件每秒执行万亿次浮点运算的能力,是评估计算能力的重要指标。
- 内存带宽斜率(Slope = BW_mem)揭示了在带宽限制下,算术强度对吞吐量的影响,直观反映内存性能与计算强度的关系。
- 在Memory Bandwidth Bound区域,当算术强度较低时,吞吐量受限于内存带宽,即图左侧斜线部分。这表明计算资源未能被充分利用,性能瓶颈主要源于数据移动,需优化数据访问以释放计算潜力。
- Compute bound区域指算术强度超出阈值后,吞吐量不再增长,稳定在最大值。此时,受限于计算能力,增加算术强度无法提升吞吐量,已达硬件计算极限,表现为图中右侧水平线部分。
情景1:内存带宽受限,但吞吐量线性增长
内存带宽受限意味着算法执行时,数据传输速度成为瓶颈,即无法迅速从内存读取或写入数据。这并非内存带宽绝对不足,而是算法设计及工作负载需求超过内存带宽能力。简而言之,算法需求的数据传输速度超出了系统供给。
算术强度提升有限:尽管尝试在每次数据传输后增加计算量以提升算法效率,但若增幅不足,算法性能仍受限于内存带宽。
吞吐量实现线性增长:尽管内存带宽有所限制,但算法吞吐量随算术强度适度提升而增长。算法通过优化计算效率,高效利用每次数据传输,实现性能与算术强度正比增长,呈现出显著的线性提升趋势。
情景2:转换到计算受限模式
算法算术强度显著提升,可能导致性能瓶颈由内存带宽转向计算能力,凸显处理器计算速度成为当前算法的关键制约因素。
在吞吐量达到峰值时,算法高效利用硬件资源,实现硬件支持的最大处理能力。显著增强算术强度,充分释放硬件计算潜能,实现性能最优化。
情景3:已处于计算受限状态
算术强度增强不再提升吞吐量:算法已发挥硬件计算极限,进一步增加算术强度无法带来性能增益,吞吐量保持不变。
如何增加算术强度?
优化计算效率,可运用增大批处理量、操作融合和数据量化等技术,显著减少数据传输,提升算术强度,实现更高效的运算性能。
次优资源使用的影响
算法实际性能受限于实现效率,难以100%利用硬件资源。性能提升潜力巨大,通过增强算术强度或优化算法实现,能有效提升硬件资源利用率,进而提升整体性能。
图11:具有次优资源利用的屋顶线模型
图表部分展示了计算吞吐量(TFLOPS)与算术强度(op/byte)之间的关系。图中标出了“Memory bandwidth bound”(内存带宽限制)和“Compute bound”(计算限制)两个区域,说明了在不同的算术强度下,系统性能受限于内存带宽或计算能力。图中的实线表示实际达到的带宽(BW_achieved)和实际吞吐量(Actual throughput),而虚线表示理论上的带宽需求(BW_math)和理论上可能的更高吞吐量(Higher BW_achieved_math)。图中的点显示了当前的实际性能状态,位于内存带宽限制区域,意味着提升性能的瓶颈在于内存带宽而非计算能力。
FlashAttention 2.2前版本推理解码阶段表现欠佳,主要问题凸显于解码流程的效率低下和准确性不足。
-
- 内存带宽利用低:在解码时,数据加载(即从内存中读取数据到处理单元)的效率很低。
- 长序列处理面临内存与带宽双重挑战,尤其数据序列庞大时更为显著。FlashAttention团队为此推出“FlashDecoding”技术,其核心优势在于有效解决上述难题,展现卓越性能。
- 并行加载数据:通过在数据加载时采用并行处理的方式,提高了内存带宽的利用率。
- 针对长序列优化:这种改进特别针对长序列的处理,显著减少了处理这些序列的时间延迟。
-对此,您有什么看法见解?-
-欢迎在评论区留言探讨和分享。-