并行计算基础:Flynn分类法、共享内存与分布式内存模型

聊并行计算,咱们得先搞清楚一个根本问题:到底什么是“并行”?

我刚开始接触高性能计算那会儿,总觉得并行就是“多核一起干活”。后来踩了不少坑才明白——并行计算的本质,是如何把一个大任务拆成小块,再让多个处理单元同时处理。拆得好,效率翻倍;拆不好,比串行还慢。

这一节,咱们就先把并行计算的两块基石打牢:Flynn分类法(告诉你计算机有哪几种并行方式)和内存模型(告诉你数据怎么共享)。

1. Flynn分类法:四种并行架构

1980年代,Michael Flynn提出了一个至今仍在用的分类方法。他看问题的角度很简单:指令流数据流,各分“单”和“多”。

两两组合,就得到四种架构:

指令流 数据流 架构名称 典型代表
SISD 普通单核CPU
SIMD GPU、向量机
MISD 容错系统(少见)
MIMD 多核CPU、集群

重点记住:我们做HPC和GPU编程,打交道最多的就是SIMDMIMD。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_SendMPI_Recv来交换数据。

优点很明显:

  • 可扩展性好:加节点就是加内存和算力,没有共享总线的瓶颈。
  • 容错性强:一个节点挂了,不影响其他节点。

缺点也扎心:

  • 编程复杂:你得手动管理数据分布和通信。我刚开始写MPI时,经常因为忘记同步导致死锁。
  • 通信延迟高:网络传输比内存访问慢几个数量级。

4. 两种模型的对比

特性 共享内存 分布式内存
地址空间 单一 多个
通信方式 隐式(读写) 显式(消息)
可扩展性 差(受限于总线) 好(可到数万节点)
编程难度
典型代表 OpenMP、Pthreads MPI、NVSHMEM

5. 知识体系图

下面这张图,帮你理清本章的核心逻辑:

并行计算基础:核心知识体系 Flynn分类法 SISD(单核CPU) SIMD(GPU) MISD(容错系统) MIMD(集群) 内存模型 共享内存(OpenMP) 隐式通信 · 缓存一致性 分布式内存(MPI/NVSHMEM) 显式通信 · 消息传递 NVSHMEM = 共享内存的编程体验 + 分布式内存的可扩展性

6. 我的个人体会

做了这么多年并行计算,我最大的感受是:没有银弹

共享内存模型写起来爽,但扩展性受限。分布式内存模型能跑大规模,但代码复杂度高。NVSHMEM的出现,其实是在两者之间找平衡——它给你共享内存的编程体验,底层却走的是分布式通信。

我个人习惯是:小规模(单机多核)用OpenMP,大规模(多节点)用MPI,需要极致性能且节点间延迟敏感时,上NVSHMEM

你想想看,如果能把共享内存的简单和分布式内存的扩展结合起来,是不是很香?这就是NVSHMEM要做的事。后面几章,咱们就深入进去看看它到底怎么做到的。

一个小建议:学并行计算,别急着上手写代码。先把Flynn分类法和内存模型搞透。我见过太多人,连SIMD和MIMD都分不清,就开始调MPI参数——那是在浪费时间。


公众号:蓝海资料掘金营,微信deep3321