• 一个软件架构师,热爱阅读,观察,思考和行动
  • A software architect who enjoys reading, observing, thinking and doing.

LLM推理优化技术(三)

5. 解码优化 5.1 Speculative Decoding 投机解码(推测解码)1是一种非常成熟的优化技术,并被吸收进各开源大模型推理引擎项目中。它的基本原理非常简单:由于decoding阶段是内存带宽瓶颈限制,LLM输入一个token前向推理一次花费的时间,和批量输入N个token前向推理一次的时间基本一致。那么就有个比较巧妙的办法是,通过某种方法让一次前向推理输出N个token作为草稿(draft),然后批量输入这N个draft token并行前向推理,比较draft token和对应位置的LLM输出token,如果一致则直接跳过推理,如果不支持则从这个位置重新开始下一轮推理。如果猜测的N个draft token准确率足够高,那么整个推理过程将会从每次推理1个token,提升到多个token,推理速度显著提升。 思想很简单,但到具体实现,需要考虑的是如何解决一次推理能猜测N个后续token,并保证一定的准确率。这里就有两种常见的方法: 通过一个小模型来快速预测后续N个token。这个小模型可以是一个参数量更小的LLM,例如Llama 7B作为Llama 70B的猜测模型,也可以是一个更加轻量的n-gram的统计模型,可以根据实际需求挑选。这种方法的好处是,不需要重新训练模型,只需要升级推理过程,即可享受解码加速。缺点是需要两个模型配合,可能会浪费一些内存和算力。 通过修改LLM网络结构,增加多个推测头(speculative head),使得模型一次推理可以同时输出多个token。典型有Medusa2和Multi-token Prediction3。这种方法的好处是比较节省算力和内存资源,缺点也很明显,需要后训练模型,增加网络训练参数。 一个经典的,通过小模型作为draft model的投机解码算法如下图所示: 在实际落地过程中,投机解码中的draft model,也可以是一个n-gram统计模型,这样可以极大减少draft model的开销,而且适应性更强,比较适合输入和输出有较多重复内容的场景,例如文本总结场景。 应用投机解码过程中,还有一个绕不开的话题是投机解码是否对模型效果无损。投机解码影响模型效果主要有三个方面: 浮点数精度影响。由于硬件对于浮点数运算过程中,还是有可能引入精度误差,最终导致结果差异 算法实现影响。不同推理引擎在实现投机解码时,对于sampling阶段的算法,如果无法保证和原模型的分布一致,则会产生结果的差异 随机因素影响。当批量大小不同,GPU数值计算精度将会有细微差别,这个在pytorch等深度学习框架都有类似问题。这个不稳定性累计下来会影响logprobs结果,最终导致输出的差异。 综合上述原因,投机解码还是会或多或少影响LLM的输出效果。需要实际应用时充分完成效果的评测,才能确保该优化是否可使用。 5.2 MoE(Mixture of Experts) LLM在训练时遵循scaling law的规律,即参数量越大,计算量越大,训练token量越大,模型的效果越好。但这种scaling law对推理并不友好。随着参数量的增长,推理的计算量也随之增长,导致推理成本的上升。在深度学习时代,MoE技术就已经被深入研究,可以有效解决训练和推理算力需求不平等问题。 MoE是一种稀疏模型结构。它通过在网络中增加一个门结构来路由多个子网络,在推理过程中动态选择部分子网络参与推理计算,而训练时则是将所有子网络都参与训练参数中。这样既可以增加训练时的模型参数量,也可以有效降低推理时参与计算的参数量。 MoE的典型网络结构,可以参考DeepSeek-V3的结构4,如下图所示: LLM MoE一般将FFN结构替换为多个子FFN网络,在Multi Head Attention结构之后,增加一个门算子,决定路由到哪几个子FFN网络进一步计算。一般子专家网络分为共享专家和路由专家,共享专家是每次推理都会参与计算的子网络,而路由专家则是根据门算子决定选择其中几路专家网络参与计算。 对于数百B量级的MoE大模型的推理部署,是天然适合多GPU并行部署的。例如著名的DeepSeek-V3模型4,每个MoE层有1个共享专家网络,256个子专家网络,每个token推理将会激活8路专家网络和1路共享专家网络。 在部署的时候,Prefilling集群最小部署套餐是4台节点32张H100。每个GPU卡部署8个专家网络,为了避免不同专家计算量的不平衡,实际的Prefilling集群部署的时候,每个GPU卡在8个专家网络之外,还额外部署了一个冗余专家,这样每个token推理激活9个专家网络。通过动态跨节点迁移这个冗余专家网络,来尽量实现计算量的动态平衡。 而V3模型的Decoding集群,最小部署套餐是40个节点320张H100,其中256个GPU卡,每卡部署1个专家网络,其余64个GPU卡,每卡部署冗余专家以及共享专家网络。所有卡间通过直接点到点的IB网络来通信。整个调度需要精巧的设计,来降低通信耗时。 MoE的网络结构天然只会部分激活参数,因此对于推理更加友好。特别是随着DeepSeek的大火,这种网络结构也受到越来越多的关注和重视。 5.3 算子融合 算子融合是一种常见的成熟模型推理优化技术。它将模型网络计算图中的多个算子或者多层,进行编译期融合,来降低中间数据交换,降低计算开销,从而达到提升推理性能的目标。 自从深度学习迅速发展以来,算子融合就已经成为推理优化方向成熟的技术。著名的TVM5开源项目,实现全自动化的编译期算子识别和融合,覆盖经典的深度学习模型。 然而进入大语言模型时代,特别是以decoding-only模式大行其道的今天,专门针对decoding网络的优化更加适合当前业界的需要。著名的FlashAttention6,FlashAttention27,FlashDecoding8,FlashDecoding++9算法,将attension的多步算子计算,融合为一个算子。这样极大减少了读写attention矩阵的开销,提升了LLM推理计算中最重要的算子的性能。 开源项目DeepSpeed-inference10提出了Deep-Fusion融合技术,实现了: QKV矩阵计算和输入层归一化的融合 转置和attention算子的融合 后attention层归一化和中间矩阵计算的融合 偏置叠加和残差网络的融合 xFormers11 提供了各种可以增强Transformer网络结构性能的融合内核。这些包括融合 softmax算子,融合线性层、融合层归一化,以及融合SwiGLU。 TensorRT-LLM 12 是NVidia开发的提供各种高性能融合内核的框架。它集成了一个强大的模式匹配算法,可以检测各种 LLM 中的潜在融合。由于TensorRT-LLM和NVidia GPU紧密结合,使之成为NVidia GPU上推理服务的重要优化工具,深受NVidia GPU开发者的喜爱。 5.3 MQA, GQA, MLA 解码优化的另一个方向,就是在多头注意力模型的attention结构中进行优化。这也需要和训练相配合,而不是纯粹的推理优化。 经典的多头注意力算法(Multiple Head Attention)13,如下图所示: 可以明显看出,QKV矩阵计算,每个头分别计算,但Keys和Values矩阵需要占用大量的显存存储KV Cache。一个自然的想法是,是否可以在多个Queries矩阵中共享一个K和V矩阵,这就是MQA(Multi-query Attention)14的基本思想,其结构如下所示: 然而,MQA虽然节省了KV Cache的存储开销,但影响了大语言模型的效果。为此,Google的研究人员综合了MHA和MQA两种方法,提出了中间方案GQA(Grouped-query Attention)15,即多个Queries矩阵共享一组K和V矩阵,结构如下图所示:...

February 20, 2025 · Skyan

LLM推理优化技术(二)

4. 系统优化 4.1 Iteration-level Scheduling 传统上的模型推理批量计算都是将一批请求合并到一起,然后一起发送到GPU批量推理计算,完成后返回结果。但这会引起两个问题: 大语言模型请求到达时间不固定,这样先到的请求得等待后到的请求集齐一个批量,并且等待上一批量计算完成之后,才能一起发送到GPU进行批量推理计算,这对于先到的请求而言,浪费了等待时间 大语言模型每次输入和输出序列的长度不定,一般会采用padding的方法插入一些空白token用于对齐。这种批量计算对于较短的序列并不公平,它虽然已经完成,但仍然等待长序列完成之后才能返回,而且空白token用于padding计算也是浪费,GPU整体利用率也很低。 为此,2022年Orca论文1提出了iteration-level scheduling方法,将每次批量decoding作为一个迭代(iteration),每次批量decoding输出一批token之后,检查有没有完成输出的序列,如果有则从批量token序列中删除,而从排队的请求中挑选一个请求序列插入到空出的序列位置。 通过这种方法,可以有效节省等待时间,只要一个迭代中有完成的请求,就可以加入新请求继续批量decoding,而且还无需特别处理长度不一致导致的padding。这种方法将会极大提升GPU利用率,并在各个主流LLM引擎中得到了广泛应用,例如vLLM和Huggingface TGI的continuous batching,TensorRT-LLM的In-flight Batching,都是这种技术的实际应用。 4.2 Chunked Prefills Iteration-level scheduling可以极大提升GPU利用率,但还未能将GPU利用率压榨到极致。主要有两点问题: 如前所述,推理中的Prefill阶段是计算密集型,Decoding阶段是内存密集型。GPU执行prefill阶段任务时,由于大部分计算都是矩阵和矩阵乘法,因此GPU利用率可以达到最大。但当GPU在执行decoding计算时,由于大部分计算都是向量和矩阵乘法,计算受到内存带宽瓶颈限制,整体利用率很难达到最大化。 由于prefill和decoding阶段分别批量计算,存在两个阶段调度互相抢占问题。如果优先decoding,则会影响首token延迟(TTFT)。如果优先prefill,则会影响生成token间耗时(ITL)。这些调度方法都对整体的延迟长尾不友好。 Chunked prefill技术2通过将prefill拆分为多段,和decoding的序列拼在一起批量计算,来提升GPU吞吐和利用率,而且对整体服务的延迟长尾能有较好的提升。拆分主要有如下两种: 长prompt拆分为多段,调度到多次迭代批量计算中,只有最后一个段才真正使用生成的token 短prompt和decode token一起拼凑成一次批量计算,但是prompt和decode token在attention计算时分开计算。这种可以充分利用GPU算力。 Chunked Prefill的好处是将访存密集型的decode预算通过和prefill融合,从而转换为算力密集型。将decode效率得到较大提升。一些测试报告显示,chunked prefill调度相对vLLM的continuous batching调度,吞吐提升2.3倍,延迟降低50%。 该项技术已经成熟,已经被引入到vLLM,DeepSpeed等项目中,成为可以配置开启的调度类型。 4.3 PD分离 除了chunked prefill调度优化,另外一种提升GPU利用率的方法就是将prefill和decoding分别运行在不同的GPU集群3 4。这种优化可以针对算力瓶颈不同的两阶段,分配不同的GPU算力,甚至不同型号的GPU,来最大化整个集群的利用率。 典型的架构可以参考MoonCake4架构图,如下所示: 这种调度方法原理上比较直观。在prefill集群只运行prefill计算,将计算完成后得到的KV Cache传输到decoding集群,decoding集群自回归式计算输出token,完成文本生成。整个过程可以发生在一台机器中的多卡之间,通过NVLink完成卡间通讯。也可以发生在多机之间,通过IB或者RDMA网卡来通讯传输KV Cache数据。 为什么这种调度会提升GPU利用率,节省整体的GPU资源呢。可以这样模拟计算一下: 假设prefill的最大吞吐为5.6 RPS(request per second),decoding最大吞吐为10 RPS,那么如果放在一起,平均每卡吞吐最大为min(5.6, 10)/2=2.8 RPS。而将prefill阶段放在两张GPU运行,decoding阶段放在一张GPU,这样平均每卡吞吐最大为min(5.6 * 2, 10)/3=3.3 RPS,相对运行在一张卡,吞吐提升了18%。 更进一步,prefill和decoding可以采用不同的GPU型号,例如prefill集群采用算力较强的GPU,而decoding阶段采用内存带宽更大的型号,整体利用率还可以进一步提升。 PD分离带来的优势有: 可以分别优化首token延迟(TTFT)和token间延迟(ITL),由于PD分离,可以针对P和D分别采用不同的并行策略(TP或者PP),两种延迟之间不互相影响 有效控制ITL的长尾延迟。由于单卡调度Prefill和decoding,会导致prefill和decoding互相抢占GPU资源,导致最坏情况下,输出token发生严重的长尾延迟。 当然,PD分离带来的性能提升,也是建立在保证SLO目标基础上。如果不考虑SLO,PD分离带来的收益并不显著,相反,还会导致运维复杂性提高,P和D集群资源规划更加复杂。这些都是需要考虑的因素。目前PD分离也作为实验功能,进入vLLM等开源项目中。 4.4 多GPU并行推理 对于参数规模在数百B量级以上的大语言模型而言,单GPU的显存大小已经无法承载如此数量的参数和KV Cache了,这必然依赖多GPU共同加载模型,实现并行推理服务。Google在这个领域很早就开始布局5,PaLM的540B大模型就是成功部署在64块TPU v4芯片上。 多GPU并行推理,核心的难题是如何拆分模型,使得推理延迟,MFU(model FLOPS utilization),以及显存使用量同时达到帕累托最优。这个拆分还依赖模型结构和GPU的体系结构特点。例如MHA和MQA下需要考虑不同的分片方法。 模型网络每个阶段,可以采用不同的分片方法,例如在前馈神经网络层(feedforward layer)可以精巧的设计tensor的拆分,通过all-gather操作实现并行推理,在attention层则可以通过batch维度拆分,来降低显存消耗,同时降低卡间通信量,提升MFU。通过这类优化,PaLM 540B大模型的Prefill MFU可以达到76%,Decoding MFU可以达到33%。...

February 18, 2025 · Skyan

DeepSeek V3/R1 训练笔记

1. 概述 最近学习了下DeepSeek-V31和R12的论文,整个训练流程虽然复杂,但描述还是比较清楚的。也从中对大模型最新的训练流程,管中窥豹,可见一斑。 2. DeepSeek-V3 训练流程 首先DeepSeek-V3的训练比较直观,就是Pretrain→SFT→RL这种经典的方式,如下图所示: flowchart TD A{{"14.8T High-Quality Dataset"}} --> B(["Pretrain"]) B --> C["DeepSeek-V3-Base Model"] C --> D(["Supervised Fine-tuning \(2 epochs\)"]) E["DeepSeek-R1 Model"] --> F{{"Reasoning Dataset"}} F --> D G["DeepSeek-V2.5 Model"] --> H{{"Non-Reasoning Dataset"}} H --> D D --> I[DeepSeek-V3-SFT] I --> J([Reinforcement Learning]) J --> K[DeepSeek-V3] I --SFT--> L[Reward Model] L1[Rule] --> J L --> J style B fill:#424242,color:#FFFFFF style D fill:#424242,color:#FFFFFF style J fill:#424242,color:#FFFFFF 需要注意的是SFT这个阶段所使用的语料,采用了自我进化的思想,即推理数据集由DeepSeek-R1生成,而非推理类数据集,由DeepSeek-V2.5生成。...

February 13, 2025 · Skyan

LLM推理优化技术(一)

1. 前言 随着2022年底以ChatGPT为代表的LLM(大语言模型)爆发式发展,LLM成为智能时代新的增长引擎,受到学术和工业界广泛的关注。业界对于LLM的深度研究极其火热,从训练、推理、可解释性、数据集、评估方法,甚至神经科学的交叉方向都有丰富的研究成果。 作为大模型应用领域最为关心的话题,还是如何能进一步降低大模型的成本,降低响应延迟,在不影响模型效果的前提下,提升模型推理性能。这方面的工作自从2023年以来,已有诸多新技术新思想的出现,而且已经迅速成熟,成为「开箱即用」的开源组件。本文主要介绍一些工业界成熟落地的推理优化技术,作为LLM推理技术的阶段性总结。 2. LLM推理技术原理 现代主流的大语言模型都是基于decoder-only的自回归transformer模型结构。这种模型预测方式是:已知一个token序列,预测下一个token的概率。整个模型的计算非常简洁,只需前向执行多层堆叠的transformer结构,经过抽样(sampling)计算,输出下一个token。而且现代服务一般采用GPU等专用AI芯片用于推理加速计算。 模型执行一次推理分为两个阶段: 第一阶段称为prefill,将输入的文本token序列执行一遍前向计算,填充内部每一个transformer层的key-value cache,实现初始化内部KV Cache的目的,准备下一阶段的计算。此时KV Cache缓存在GPU的HBM高速缓存中。 第二阶段称为decoding,这是一个循环计算,模型使用prefill阶段填充好的KV Cache,预测下一个token,将下一个token加入输入序列的尾部,重新执行一遍前向计算并更新KV Cache,再预测下一个token,更新KV Cache,再将新生成的token加入输入序列的尾部,如此循环反复,直到遇到结尾token<EOS>或者最大输出长度限制停止,此时将生成的文本序列返回。 整个流程如下图所示: graph LR subgraph Prefill Phase A1["'世界'"] --> A2([Iterator 1]) A2 --> A3["'人民'"] end subgraph Decoding Phase B1([Iteration 2]) --> B2["'大'"] B2 --> B3([Iteration 3]) B3 --> B4["'团结'"] B4 --> B5([Iteration 4]) B5 --> B6["&lt;EOS&gt;"] end subgraph KV Cache KV[KV-Cache] end A3 --> B1 %% KV-Cache as a shared block A2 --> |Update| KV KV -->|Shared| B1 B1 --> |Update| KV KV -->|Shared| B3 B3 --> |Update| KV KV -->|Shared| B5 B5 --> |Update| KV 从上面的推理流程可以看出,prefill阶段和decoding阶段是完全不同的两个阶段。prefill阶段主要是批量计算,由于整个序列可以一次输入计算,并行度更高,所需要的GPU算力也更高,所以属于计算密集型,GPU的算力(FLOPS)越高,运算越快,模型的首token耗时(TTFT)也就越低。...

January 13, 2025 · Skyan

llama2.c 源码阅读

1. 概述 前OpenAI著名工程师Andrej Kapathy开源了llama2.c项目,该项目是llama2模型推理代码的C语言实现,用大概970行C代码实现了LLama2模型的推理算法。整个项目代码简洁高效,值得深度阅读。对掌握大模型推理算法的细节有极大的帮助。 2. 源码阅读 2.1 基础算法 RMS归一化公式是: $$ o_i = w_i \times x_i \times \frac {1}{\sqrt{\frac{1}{n}\sum_{j=0}^{n-1} x_j^2 + \epsilon}} $$ 其中,\(\epsilon\) 为防止分母为0的数值。还有RMS因子是对x的归一化,w变量是gain变量,重新缩放标准化后的输入向量。 // ---------------------------------------------------------------------------- // neural net blocks; the dynamics of the Transformer void rmsnorm(float* o, float* x, float* weight, int size) { // calculate sum of squares float ss = 0.0f; for (int j = 0; j < size; j++) { ss += x[j] * x[j]; } ss /= size; ss += 1e-5f; ss = 1....

July 6, 2024 · Skyan

X下云

背景 2022 年 10 月 27 日马斯克以 440 亿美元完成 Twitter私有化交易并改名X.com,整整一年过去后,X的工程架构也发生巨大的变化,今年10月27日X工程团队发表了一个推文,总结一年的巨大变化,我觉得这个帖子很有代表性,给所有做engineering的同学新的启发。 原文 https://twitter.com/XEng/status/1717754398410240018 This has been a year full of engineering excellence that sometimes can go unnoticed. Besides all the visible changes you see on our app, here are some of the most important improvements we have made under the hood. Consolidated the tech stacks for For you, Following, Search, Profiles, Lists, Communities and Explore around a singular product framework. Completely rebuilt the For you serving and ranking systems from the ground up, resulting in a decrease 90% reduction in lines of code from 700K to 70K, a 50% decrease in our compute footprint, and an 80% increase in the throughput of posts scored per request....

November 22, 2023 · Skyan

大语言模型如何扩充上下文长度?

我们在应用大语言模型遇到的最典型的限制就是输入文本的上下文长度。开源的模型的上下文长度限制从2K到32K不等,商业模型最大上下文限制从2K到100K范围。上下文长度对应用大语言模型有着非常关键的影响,包括知识增强、记忆等Agents完成的工作,都是为了解决大语言模型上下文长度限制而设计的。大语言模型为什么会有上下文长度限制?是否有方法能扩充长度到几倍甚至十几倍?这几个问题困扰我很久。最近一段时间经过调研之后,我发现这些问题已经有了令人兴奋的进展,我也收获一些心得,记录于此。 先说结论: LLM的训练和计算都是没有上下文长度限制的,限制的只有计算资源和模型效果 头部公司和开源社区都有了阶段性的成果,最新的transformers,llama.cpp等开源项目已经内置了扩充上下文长度方法 如何在扩充上下文长度的同时,降低训练成本,保证模型效果,是一个还在不断探索的话题 LLM的上下文长度限制之谜 实际上,目前以Transformer为核心的LLM,理论上而言是没有上下文长度限制的。唯一限制上下文长度的,只有训练时的资源消耗,以及预测时的输出效果。如果不考虑这两点,LLM完全可以支持任意长度的上下文,这里本质原因是,Transformer的核心:Attention算法和上下文长度无关。 为说明这个本质原因,我们回顾下Attention的计算,参考「Attention is all your need」经典论文1 : 定义n为输入的token数量,vocab_size为token词典大小,d为文本embedding的维度大小,k为Query和Key向量的维度大小。那么整个Attention计算过程如下: Inputs是n个token序列,查找(vocab_size, d)大小的Embedding词典,转化为(n, d)的输入矩阵X 类似的,将输入的token位置i经过位置向量计算(查表或者实时计算),转化为(n, d)的词典,和上面的X词典相加,获得带上位置向量的X作为输入。注意位置向量的计算有两种方法,一种是通过查表的方式,即查找一个(pos_size, d)大小的Embedding词典,另外一种是实时计算,根据token的位置i,通过位置embedding算法,计算出对应的位置向量。这两种方法各有优缺点,这将是突破上下文长度限制的重点。 将X乘以\(W^Q,W^K和W^V\)三个Q,K,V权重矩阵,获得Q,K,V值矩阵。其中\(W^Q\)形状为(d, k), \(W^K\)形状为(d,k), \(W^V\)形状为(d, v),注意着三个权重矩阵都和输入长度无关,获得的Q和K矩阵大小是(n, k),V矩阵大小是(n, v) 如下计算attention: $$Attention(Q,K,V) =softmax(\frac {QK^T}{\sqrt{k}})V$$ 其中\(QK^T\)计算结果为(n, n)矩阵,再乘以V的,输出结果为(n,v)矩阵。注意这些计算都是实时计算,计算复杂度和输入长度有关。 5. 在Multi-Head Attention算法中,上述4个步骤所有矩阵变成了张量,增加了h个header,输入矩阵X变成(h, n, d)大小,\(W_q\)大小为(h, d, k), \(W_k\)大小为(h, d, k), \(W_v\)大小为(h, d, v)。Q, K, V矩阵分别大小为(h, n, k), (h, n, k), (h, n, v)。通过将多头concat,输出(n, hv)大小的结果\(Attention(Q,K,V)\),再经过一次线性变化,最终Attention结果为: $$MultiHead(Q, K, V) = Concat(Attention(Q, K, V))W^O$$ \(W^O\)大小为(hv, d),所以最终Attention结果\(MultiHead\)维度为(n, d)。 从上面的计算可见,整个模型的参数,只有位置向量需要查表的时候,是和上下文长度有关,而其他的所有权重矩阵,都和上下文长度无关。如果位置向量实时计算时,attention算法所依赖的所有参数都和上下文长度无关。 那么限制到底在哪里呢?上面已经提到,限制就在: 计算资源 模型效果 先说计算资源的限制,可以证明(过程略),上述第三步计算Q,K,V矩阵的计算复杂度是\(O(nd^2)\),第四步计算attention的计算复杂度是\(O(n^2d)\),所以计算attention总体的计算复杂度是\(O(nd^2+n^2d)\),如果d > n,则\(O(nd^2)\)占计算复杂度的大头,例如LLaMa1模型的n为2048,d为4096,所以可以估计训练复杂度和训练输入的上下文长度呈线性关系。以LLaMa1模型举例,训练一次的计算成本约为300万美元,如果将输入长度扩大到8倍(即16K),那么训练成本将增长到2400万美元。因此如果要在预训练阶段用更长的上下文长度训练,这个成本将变得难以接受。...

August 4, 2023 · Skyan

大模型时代,我们如何重构产品

近期厂内关于AI云原生思维的讨论非常热烈,尤其是AI大模型时代,产品如何重做重构的话题异常火热。关于这个话题,我倒是联想到一句经典的谚语:“It is like teenage sex: Everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims”. 但说说容易,我们应该如何思考这个话题呢?在我看来,需要引入一些模型化思维来拆解这个问题,这样很多现象就能得到很好的解释,关于如何重构产品也就能逐步看到清晰的路线了。 首先,我们可以观察到,一切复杂系统,包括人体,组织,社会,甚至互联网,都是由三个子系统组成: 信息系统:数据,文章,网页,图片,视频等等一切我们能看,能读,能听,能感受到的东西,这个大千世界充满了信息,信息可以是数字化的,例如互联网上的数据,也可以是模拟的,例如线下的交流对话等 模型系统:通过各种推理,拆解,演绎,总结等手段,将信息转化为知识沉淀下来,例如人的大脑内部就存在一个抽象的世界模型,这个模型赋予了我们认知的能力,让我们可以产生意识,理解这个世界 行动系统:当模型处理完信息之后,它需要执行一定的操作和这个环境互动,这就是行动系统的作用。例如我开车过程中看到了一只小狗,我的大脑获取这个信息后,交给认知模型进行分析和处理,认知模型决策需要避开,我的行动系统的手和脚操作方向盘和刹车,完美避开相撞的风险。 当把互联网这样的复杂系统拆解为这三个子系统之后,最近三十年互联网发生的事情,就能用这三个系统的变化得到如下分类: 信息时代:从1995年开始,我们进入了信息时代。这个时代的互联网产品,本质上都是在做信息的搬运工。这其中还细分多个阶段,例如90年代开始由论坛,新闻门户网站组成的Web1.0阶段,初步形成信息的数字化。典型的代表有雅虎,新浪,搜狐和网易这些老牌门户网站。从2000开始,随着Web2.0的兴起,我们有了更高效的搜索引擎,社交网络,电子商务平台,实现了信息更高效的运转,真正将获取信息的边际成本降为了0。这个阶段出现了很多伟大的公司,典型的代表有Google,Amazon,百度,腾讯,阿里等。正是有了这些平台,我们才真正实现了获取信息效率的极大提升。到了2010年以后逐步兴起的移动互联网,又诞生了一批新的平台公司,实现了移动设备上的信息生产和消费,让用户可以随时随地地获取信息,使用信息 模型时代:其实从2010年前后,互联网平台产品就已经开始具备了模型能力,但这种模型还是“小”模型,并不具备转化为知识的能力。例如在搜索引擎中,我们有LTR模型来优化排序,在推荐引擎中也有稀疏大模型来优化推荐的列表。但这些模型都专属于某一个垂直领域,无法通用化使用。一个搜索引擎的模型是无法用在推荐系统中的,模型应用的边际成本依然很高。直到2022年开始,以OpenAI的ChatGPT为代表的大模型真正登台之后,我们才真正步入了模型极大发展的阶段。代表特色就是这种模型具备通用的认知能力,将使用和获取知识的边际成本真正降为0。举个例子来说,在模型时代之前,我们想了解一种疾病如何治疗,需要去阅读,去理解各种专业知识,甚至花费数年专业的学习才能真正解决临床上的病症。但如果有一个经过医学专业知识训练好的大模型,通过大模型就直接获得了治疗方法,这就是将获取知识的成本降为了0。当前我们正处于这个模型时代刚开始的时期 下一个时代——行动时代:行动时代其实已经初露雏形了。典型的代表就是自动驾驶。自动驾驶是将我们驾驶行动的边际成本降为0,我们不需要付出太多获取信息-认知处理-操作汽车这一系列的成本,自动驾驶就自动替我们解决了。这个时代同样也处于早期阶段,但并不遥远 因此,在当前的模型时代,我们需要思考的是,如何用大模型来改造以及创造各种产品,将各行各业需要人来认知的过程的边际成本降为0,将是这个时代的主旋律。 以New Bing为例,为什么搜索是第一个大模型为先的应用及平台,从上面的分析可以获取答案。在信息时代,搜索产品本质上是人通过搜索query来找信息,找知识,完成任务,如下结构所示: 人 → 信息 但人在用搜索引擎找信息的过程中,还需要去理解网页信息,阅读内容,并将这些网页内容通过人自身的认知模型转换为知识,转换为下一步的行动。而且每一次搜索,都要重复这一过程,付出类似的成本,这就是人找信息过程中,需要付出的边际成本。 人 → 理解认知 → 信息 但大模型的出现,将中间需要人的认知模型来理解的步骤交给大模型来完成,整个流程变成了: 人 → 大模型 → 信息 这就意味着,大模型可以部分代替人的认知能力,把繁琐的总结,汇总,提取信息的边际成本都降为0,直接可以获得人想获得的答案。这就是经过大模型改造后的搜索引擎,其效率将比传统的搜索引擎更为高效,成本更低。 更进一步,人在找到信息之后,还会有下一步的行动。传统上这些行动还需要人去做判断,思考,决策才能执行这些行动。这个阶段的边际成本也可以交给大模型来降低,如下所示: 人 → 大模型 → 信息 → 大模型 → 人 → 行动 其实上面本质上就是一种所谓的端到端闭环。举一个例子,我希望买一个空调,在搜索引擎搜索后,大模型帮我筛选了若干最新款空调的信息,并总结展示给我供我选择,但我决定选某一款空调后,大模型帮我进一步规划去哪里买,什么价格最便宜,售后服务更完善,最终高效地完成了一次人找信息,再从信息到行动的闭环。 以上,就是New Bing在这次大模型时代对传统搜索引擎做出的重构:通过在人找信息这个环境中加入大模型,将人的理解和汇总的边际成本降为0,甚至实现信息到行动的0边际成本的延伸,真正实现一个高效率的搜索引擎。 如果这样推演,我们可以扩展到更多互联网产品和平台上么?完全是可能的。我们就以另一个Web2.0时代产物,推荐系统为例,尝试推演推荐系统如何应用大模型。...

July 6, 2023 · Skyan

Google abseil开源项目介绍

Google abseil是Google开源的优秀C++基础库,持续维护并且持续迭代。该库的代码质量和工程化水平属于业界顶级,值得我们在实际生产中使用和学习。不仅要善于使用abseil库,还要多看abseil的文档和代码,从中学习Google业界领先的C++经验。 这里先介绍几个abseil库的经典组件: 容器 Recommendation Prefer absl::flat_hash_map or absl::flat_hash_set in most new code (see above). Use absl::node_hash_map or absl::node_hash_set when pointer stability of both keys and values is required (rare), or for code migrations from other containers with this property. Note: Do not use popularity as a guide. You will see the “node” containers used a lot, but only because it was safe to migrate code to them from other containers....

June 1, 2023 · Skyan

FlameGraph火焰图原理

FlameGraph是世界知名计算机性能优化专家Brendan Gregg发明的一种性能数据可视化方法。通过不同色块可交互的展示,可动态展示系统运行时的性能热点。FlameGraph如下图所示: 按照作者介绍,FlameGraph可以用于分析CPU耗时,内存分配,非CPU耗时(线程等待,调度性能),混合运行时(CPU和非CPU运行混合),以及性能对比这5种性能分析的可视化。我们常用FlameGraph生成CPU耗时分析图,用来找到服务运行性能的热点,并专项优化以节省资源成本。其实,FlameGraph也有一套标准数据格式,根据这个格式,可以在任何适合的场景中生成所需要的FlameGraph,并可视化可交互地展示和分析。 FlameGraph适合用于有调用栈的数据可视化,通过一种类似柱子的布局,加上暖色调的颜色,很像火焰一样的图案,所以被称为火焰图。以大家常用的CPU耗时火焰图为例,它的组成元素如下: 一个函数调用栈用一列方框组成,每个方框代表一个函数 y轴代表函数调用的深度,从底向上逐层递进调用,最顶端的函数代表性能采集时刻正在运行的函数,下面是它的父函数,依次类推 x轴代表不同采集栈的集合。需要注意的是x轴并不代表时间顺序,同一层的函数按照字母序从左往右排列。这样如果两个同名函数在同一层,将会被合并成一个区块。 方框的宽度代表该函数在调用栈抽样中出现的次数,如果宽度越宽,代表这个函数在栈抽样中出现的越频繁,从CPU耗时的角度来说,也表示这个函数更耗时。 如果方框宽度足够则展示函数的全名,如果不够则展示部分或者不展示,鼠标放上去可以展示全名 每个方框的背景色其实并没有什么特别,其实就是选取了一组随机的暖色调。主要是为了方便眼睛能区分不同层的方框。 一个火焰图可以用于可视化单线程,多线程,多进程甚至多主机的性能数据。也可以为每个线程生成一份独立的火焰图用于更详细的分析。 方框的宽度不仅限于表示抽样次数,也可以表示其他指标。例如宽度可以表示线程阻塞的时长,这样的一个火焰图可以很清晰的看出哪些函数在阻塞线程,以及整个线程阻塞时函数的调用栈情况 以上即是一个火焰图静态的组成部分,更为有趣的是,Gregg也为火焰图增加了互动功能,使得用户体验更佳。 火焰图本身是一个SVG文件,配合Javascript,支持鼠标悬浮,点击放大,以及搜索功能: 鼠标悬浮:当鼠标光标放置到一个方框上方时,可以展示该方框的详细数据(函数名,采样次数,以及占比) 点击放大:当鼠标点击一个方框时,火焰图按照垂直方向放大,该方框以上的函数将被放大展示,其他部分浅色处理,方便聚焦一个函数的分析 搜索:可以用Ctrl/Command+F快捷键或者右上角搜索功能,按照函数名搜索。搜索到的函数会高亮显示,还是在右下角展示所有搜索到的函数出现次数占比 火焰图生成流程是怎样的呢?按照Gregg开源的flamegraph.pl程序,生成一个火焰图只需要3步: 从perf,dtrace,gperftools等程序中获取运行时的调用栈数据 转换为折叠栈格式(Fold stacks) 调用flamegraph.pl生成火焰图 原理非常简单。所以搞懂火焰图只需要明白折叠栈是怎么回事即可。首先一个函数的调用栈可能长这样: func_c func_b func_a start_thread clone func_d func_a start_thread clone func_d func_a start_thread clone 上面展示了三次性能抽样,每次抽样从底向上显示了一个调用栈,折叠栈格式如下: clone;start_thread;func_a;func_b;func_c 1 clone;start_thread;func_a;func_d 2 其实就是将三个栈做了一个汇总,将同样的栈汇总到一起,每个函数用’;‘分隔,最后加空格和打印出现次数。 前面还可以加上程序的名称,例如cpp,如下也是一个合法的folded stacks: cpp;clone;start_thread;func_a;func_b;func_c 1 cpp;clone;start_thread;func_a;func_d 2 有了折叠栈数据,即可利用flamegraph.pl生成对应的火焰图svg文件。该文件可以嵌入到任何网页中展示,简单方便。 进一步,我们可以利用火焰图原理,生成各种场景所需要的“火焰图”,用于性能分析,数据可视化展示等功能。我们以应用层算子调度框架的性能分析为例,展示如何利用火焰图生成算子耗时可视化图。 应用层算子调度框架常见于RPC服务端的应用层,主要思想是将策略逻辑封装成若干个函数式的算子,再通过调度框架,串行或者并行调度这些算子运行,最终产出RPC的返回结果。算子调度的拓扑图有叫Tree,也有叫DAG的,也有叫图的。下图所示就是一个典型的算子调度框架的运行时拓扑图。 针对这样的算子调度,经常需要分析的问题有:哪个算子耗时最高,最长路径在哪里,哪个算子可以优化调度路径等,我们通常通过打印日志和统计最长路径等方法来分析运行时性能情况。但同样,通过火焰图也可以将运行拓扑的耗时可视化起来。 生成火焰图的关键即为生成栈的汇总数据,假设每个算子的耗时情况如下: 运行阶段 解析请求 访问A服务 访问B服务 汇总结果 产生返回数据 开始运行时间 0 10 10 24 32 结束运行时间 10 20 24 32 34 我们想象有一个抽样程序,可以按照上面的运行时图,从左往右分别垂直抽样,可以看出在不同时间段有这样几种“栈”:...

December 5, 2022 · Skyan