极客时间《Linux 性能优化实战》案例 git,以及相应的博客课程
Java 并发编程实战–王宝令
Java 性能调优实战
B站:AI System & AI Infra(zomi酱)
B站李沐老师论文精读
大模型推理并行技术
大模型的并行切分方式对通信和时延的要求:
切分方式 | 通信操作 | 通信量级(单卡) | 时延要求 | 集群部署方式要求 |
---|---|---|---|---|
Tensor并行 | AllReduce | 百GB | 秒级 | 节点内 |
流水线并行 | Send/Recv | MB | 秒级 | 节点间 |
数据并行 | AllReduce | GB | 十秒级 | 节点内/间 |
优化器并行 | Allgather/reduce scatter | 百GB | 秒级 | 节点内 |
专家并行 | All2All | 百GB | 秒级 | 高网络吞吐 |
数据并行和流水线并行对节点间的带宽要求较低;但专家并行(MOE),需要节点间较高的带宽才可以支持。
大模型LLM推理相关技术
- 投机推理
- KV cache
- Continuous batching
- Paged Attention (PA)
- Flash Attention (FA)
FA相关讲解
大模型技术栈
1、模型设计:将问题转化为对应的大模型,模型框架的选择与设计;
2、训练作业:GPU,NPU,学习率,batch size,初始化,优化器等;
3、学习策略:监督学习,无监督学习,强化学习等;
4、分布式框架;
5、SFT;
6、分布式优化、内存优化、算子加速、集群组网;
7、高性能存储、高可靠集群;
硬件通信方式
机器内通信:共享内存、PCIe(GPU与CPU之间)、NVLink(GPU与GPU之间);
机器间通信:TCP/IP网络、RDMA网络(AI框架常用的)
RDMA:远程内存直连,有以下3个重要特性
- CPU offload:无需CPU干预,远程主机CPU缓存cache不会被访问的内存内存所填充;
- kernel bypass:专有verbs interface,应用程序可以直接在用户态执行数据传输;
- zero copy:每个应用程序都能直接访问集群中的设备的虚拟内存;
软件通信协调
- MPI:定义了多个原语的消息传递接口,这一接口主要被用于多进程间的通信。MPI系统通信方式是建立在点对点通信之上。而集合通讯是建立在端到端通信的基础上,在一组进程内的通讯原语。
- NCCL/HCCL:NVIDIA和华为的
- Gloo
通信实现方式
点对点通信Send/Recv:TCP/IP,RDMA
集合式通信All Reduce:TCP/IP,NCCL
- 一对多:Scatter / Broadcast
Broadcast:某个节点想把自身的数据发送到其他节点,那么就可以使用广播机制。分布式机器学习中常用于网络参数的初始化
Scatter :将主节点的数据进行划分并散布至其他指定的节点 - 多对一:Gather / Reduce
Reduce:一系列简单运算操作的统称,细分可以包括sum、min、max、prod、lor等类型的规约操作;
Gather :将多个Sender上的数据收集到单节点上,可以理解为反向的Scatter - 多对多:All Reduce / All Gather
All Reduce:在所有的节点上都应用同样的reduce操作
All Gather:收集所有数据到所有节点上。发送多个数据到多个节点很有用,即在多对多通信模式的场景。
大模型性能调优工具:profiler
Tensor切分、变形等方法
- Tensor的链接操作
cat
torch.cat(tensors, dim = 0, out = None)
stack
torch.stack(inputs, dim=0)
- Tensor的切分操作
torch.chunk(input, chunks, dim=0)
torch.split(tensor, split_size_or_sections, dim=0)
torch.unbind(input, dim=0)
Pytorch矩阵转秩
在PyTorch中有两个函数,分别是permute()和transpose()可以用来实现矩阵的转秩,或者说交换不同维度的数据。比如在调整卷积层的尺寸、修改channel的顺序、变换全连接层的大小的时候,我们就要用到它们。
>>> x = torch.rand(2,3,5)
>>> x.shape
torch.Size([2, 3, 5])
>>> x = x.permute(2,1,0)
>>> x.shape
torch.Size([5, 3, 2])
>>> x.shape
torch.Size([2, 3, 4])
>>> x = x.transpose(1,0)
>>> x.shape
torch.Size([3, 2, 4])
需要注意的是,经过了transpose或者permute处理之后的数据,变得不再连续了
还是接着刚才的例子说,我们使用torch.rand(2,3,4)得到的tensor,在内存中是连续的,但是经过transpose或者permute之后呢,比如transpose(1,0),内存虽然没有变化,但是我们得到的数据“看上去”是第0和第1维的数据发生了交换,现在的第0维是原来的第1维,所以Tensor都会变得不再连续。
Pytorch形状变换
在PyTorch中有两种常用的改变形状的函数,分别是view和reshape。
>>> x = torch.randn(4, 4)
>>> x.shape
torch.Size([4, 4])
>>> x = x.view(2,8)
>>> x.shape
torch.Size([2, 8])
>>> x = x.permute(1,0)
>>> x.shape
torch.Size([8, 2])
>>> x.view(4, 4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
结合代码可以看到,利用permute,我们将第0和第1维度的数据进行了变换,得到了[8, 2]形状的Tensor,在这个新Tensor上进行view操作,忽然就报错了,为什么呢?其实就是因为view不能处理内存不连续Tensor的结构。
那这时候要怎么办呢?我们可以使用另一个函数,reshape:
>>> x = x.reshape(4, 4)
>>> x.shape
torch.Size([4, 4])
这样问题就迎刃而解了。其实reshape相当于进行了两步操作,先把Tensor在内存中捋顺了,然后再进行view操作。