并行计算基础:Flynn分类法、共享内存与分布式内存模型
聊并行计算,咱们得先搞清楚一个根本问题:到底什么是“并行”?
我刚开始接触高性能计算那会儿,总觉得并行就是“多核一起干活”。后来踩了不少坑才明白——并行计算的本质,是如何把一个大任务拆成小块,再让多个处理单元同时处理。拆得好,效率翻倍;拆不好,比串行还慢。
这一节,咱们就先把并行计算的两块基石打牢:Flynn分类法(告诉你计算机有哪几种并行方式)和内存模型(告诉你数据怎么共享)。
1. Flynn分类法:四种并行架构
1980年代,Michael Flynn提出了一个至今仍在用的分类方法。他看问题的角度很简单:指令流和数据流,各分“单”和“多”。
两两组合,就得到四种架构:
| 指令流 | 数据流 | 架构名称 | 典型代表 |
|---|---|---|---|
| 单 | 单 | SISD | 普通单核CPU |
| 单 | 多 | SIMD | GPU、向量机 |
| 多 | 单 | MISD | 容错系统(少见) |
| 多 | 多 | MIMD | 多核CPU、集群 |
重点记住:我们做HPC和GPU编程,打交道最多的就是SIMD和MIMD。SIMD是GPU的看家本领,MIMD是MPI和NVSHMEM的底层基础。
1.1 SISD:单指令单数据
这就是你桌面上的普通CPU。一条指令处理一个数据。说白了,就是“一个人干一件事”。
1.2 SIMD:单指令多数据
一条指令,同时对多个数据执行相同操作。GPU就是典型的SIMD架构——一个线程束(warp)里的32个线程,执行同一条指令,但处理各自的数据。
我在项目中遇到过一个问题:写CUDA代码时,如果线程束内出现分支(if-else),性能会急剧下降。为什么?因为SIMD要求所有线程走同一条路,分支意味着部分线程要等待。这就是所谓的线程束发散。
避坑指南:我曾经在写向量加法时,没注意数据对齐,导致SIMD向量化没生效。后来用编译器报告一看,性能差了5倍。记住:数据对齐是SIMD优化的第一步。
1.3 MISD:多指令单数据
这个架构比较冷门。多个指令处理同一个数据,主要用于容错系统。比如航天器上的三模冗余——三个处理器同时算同一个结果,然后投票。你想想看,日常开发基本碰不到。
1.4 MIMD:多指令多数据
每个处理单元执行自己的指令,处理自己的数据。多核CPU、分布式集群都属于MIMD。MPI和NVSHMEM就是为MIMD设计的通信库。
嗯,这里要注意:MIMD虽然灵活,但同步和通信是最大的挑战。你让1000个核各自为政,怎么保证它们协同工作?这就是后面要讲的内容。
2. 共享内存模型
共享内存,说白了就是所有处理器都能直接访问同一块物理内存。你写一个变量,我马上就能读到。
这种模型的好处是:编程简单。你不用操心数据在哪,直接读写就行。OpenMP就是典型的共享内存编程模型。
但问题也很明显:
- 可扩展性差:处理器一多,内存总线就成了瓶颈。我见过一个项目,32核时性能还行,加到64核反而变慢了——全在抢内存带宽。
- 缓存一致性:每个核都有自己的缓存,你改了数据,我得知道。这个协议(如MESI)开销不小。
- 同步开销:用锁、原子操作来保护共享数据,一不小心就死锁。
注意:共享内存模型不等于“没有通信”。实际上,缓存一致性协议本身就是一种隐式通信。只不过这种通信由硬件帮你做了,你感觉不到。
3. 分布式内存模型
分布式内存模型里,每个处理器有自己的私有内存。你想访问别人的数据?必须显式发送消息。
这就是MPI的用武之地。每个进程有自己的地址空间,通过MPI_Send和MPI_Recv来交换数据。
优点很明显:
- 可扩展性好:加节点就是加内存和算力,没有共享总线的瓶颈。
- 容错性强:一个节点挂了,不影响其他节点。
缺点也扎心:
- 编程复杂:你得手动管理数据分布和通信。我刚开始写MPI时,经常因为忘记同步导致死锁。
- 通信延迟高:网络传输比内存访问慢几个数量级。
4. 两种模型的对比
| 特性 | 共享内存 | 分布式内存 |
|---|---|---|
| 地址空间 | 单一 | 多个 |
| 通信方式 | 隐式(读写) | 显式(消息) |
| 可扩展性 | 差(受限于总线) | 好(可到数万节点) |
| 编程难度 | 低 | 高 |
| 典型代表 | OpenMP、Pthreads | MPI、NVSHMEM |
5. 知识体系图
下面这张图,帮你理清本章的核心逻辑:
6. 我的个人体会
做了这么多年并行计算,我最大的感受是:没有银弹。
共享内存模型写起来爽,但扩展性受限。分布式内存模型能跑大规模,但代码复杂度高。NVSHMEM的出现,其实是在两者之间找平衡——它给你共享内存的编程体验,底层却走的是分布式通信。
我个人习惯是:小规模(单机多核)用OpenMP,大规模(多节点)用MPI,需要极致性能且节点间延迟敏感时,上NVSHMEM。
你想想看,如果能把共享内存的简单和分布式内存的扩展结合起来,是不是很香?这就是NVSHMEM要做的事。后面几章,咱们就深入进去看看它到底怎么做到的。
一个小建议:学并行计算,别急着上手写代码。先把Flynn分类法和内存模型搞透。我见过太多人,连SIMD和MIMD都分不清,就开始调MPI参数——那是在浪费时间。
公众号:蓝海资料掘金营,微信deep3321