并行计算基础:从单核到多核的思维跃迁

各位同学,欢迎来到《AI芯片多核并行计算实战》的第一章。

我是你们的讲师,一个在芯片和嵌入式领域摸爬滚打十几年的老工程师。今天咱们聊点最基础,但也最核心的东西——并行计算。

说实话,我刚入行那会儿,觉得“并行”就是个高大上的概念。直到后来做AI芯片,发现单核再怎么使劲也跑不动大模型,才真正体会到:并行不是选项,是刚需

核心观点:并行计算的本质,就是“把一个大活拆成多个小活,同时干”。但怎么拆、怎么干,这里面学问大了。

1.1 并行计算的概念

先问大家一个问题:你写一个for循环,处理100万张图片。单核跑要10秒,四核跑要多久?

理想情况是2.5秒。但现实往往打脸——可能只有3秒,甚至更慢。

为什么会这样?因为并行计算不是简单的“人多力量大”。它涉及任务拆分、数据同步、通信开销等一系列问题。

我个人习惯把并行计算分成两个层面:

  • 硬件层面:多个计算单元同时工作。比如多核CPU、GPU、AI芯片里的NPU核。
  • 软件层面:把算法拆成可以并行执行的部分。比如把一张大图切成四块,四个核各处理一块。

我在项目中遇到过最典型的坑:以为并行就是多开几个线程。结果线程开多了,光切换开销就占了50%的算力。嗯,这里要注意——并行不是线程越多越好。

我的经验:做并行优化前,先搞清楚你的瓶颈在哪。是计算密集?还是访存密集?不同的瓶颈,并行策略完全不同。

1.2 Flynn分类法:四种并行架构

说到并行计算的分类,就绕不开Flynn分类法。这是1972年Flynn提出的,虽然老,但至今仍是理解并行架构的基石。

Flynn从两个维度看问题:指令流数据流。每个维度分“单”和“多”,组合起来就是四种架构。

分类 指令流 数据流 典型代表 我眼中的它
SISD 普通单核CPU 老实人,一次只干一件事
SIMD GPU、ARM NEON、x86 SSE 包工头,一声令下全干活
MISD 容错系统(少见) 稀罕物,我几乎没用过
MIMD 多核CPU、集群 自由人,各干各的

SISD:单指令单数据

说白了就是最传统的冯·诺依曼架构。一条指令处理一个数据。你写个a = b + c,CPU就老老实实加一次。

这种架构简单、好理解,但效率低。我刚开始写嵌入式程序时,全是这种思维——串行、顺序、一步一个脚印。

SIMD:单指令多数据

这是AI芯片最常用的模式。一条指令,同时对多个数据执行相同操作。

举个例子:你要给1000个像素点每个加10。SIMD模式下,一条指令就能搞定。而在SISD下,你得循环1000次。

我在做神经网络加速器时,SIMD是核心武器。卷积运算里,一个乘加指令可以同时处理16个甚至32个数据。你想想看,这速度能不快吗?

// 伪代码:SIMD vs SISD
// SISD方式
for (int i = 0; i < 1000; i++) {
    c[i] = a[i] + b[i];
}

// SIMD方式(假设一次处理4个)
simd_add(c, a, b, 1000);  // 一条指令搞定

避坑指南:我曾经在某个项目中,为了用SIMD把数据对齐搞错了,结果跑出来的结果全是错的。后来查了两天才发现——SIMD要求数据在内存中对齐,否则会出大问题。

MISD:多指令单数据

这个分类比较冷门。多个指令处理同一个数据。说白了就是多个计算单元对同一份数据做不同操作。

实际应用中很少见。我做了十几年芯片,只在一些航天级的容错系统里见过——三个CPU同时算同一个结果,然后投票表决。

嗯,这个大家了解就行,面试可能会考,但工作中基本用不到。

MIMD:多指令多数据

这是现代多核CPU和AI芯片的主流架构。每个核可以执行不同的指令,处理不同的数据。

比如一个四核CPU:核1在跑视频解码,核2在跑音频处理,核3在跑网络协议,核4在跑用户界面。各干各的,互不干扰。

AI芯片里,MIMD通常和SIMD结合使用。比如一个芯片有16个核,每个核内部是SIMD,核之间是MIMD。这种混合架构,说白了就是既要灵活性又要效率。

1.3 并行计算模型:三种主流模式

搞清楚了硬件架构,接下来看软件层面怎么组织并行。我总结三种最常用的模型:数据并行任务并行流水线并行

下面这张图是我自己画的,帮你快速理解三者的区别:

三种并行计算模型对比 数据并行 大数组 ┌─ 核1: 处理块1 ├─ 核2: 处理块2 ├─ 核3: 处理块3 └─ 核4: 处理块4 特点: • 相同操作 • 不同数据 • 适合矩阵运算 任务并行 多个独立任务 核1: 视频解码 核2: 音频处理 核3: 网络协议 核4: 用户界面 特点: • 不同操作 • 不同数据 • 适合异构计算 流水线并行 数据流 → 核1: 读取 → 核2: 预处理 → 核3: 计算 → 核4: 输出 特点: • 分阶段处理 • 数据流驱动 • 适合流式处理

数据并行

这是AI芯片里最常用的模式。把数据切成多块,每个核处理一块,执行相同的操作。

比如你要做1000x1000的矩阵乘法。可以切成4块500x500,四个核同时算。最后再把结果拼起来。

我建议你在做AI芯片编程时,优先考虑数据并行。因为它最容易实现,也最容易获得线性加速比。

// 数据并行示例:向量加法
// 假设有4个核,每个核处理1/4的数据
void vector_add_parallel(float *a, float *b, float *c, int n) {
    int chunk = n / 4;
    // 核0处理 [0, chunk)
    // 核1处理 [chunk, 2*chunk)
    // 核2处理 [2*chunk, 3*chunk)
    // 核3处理 [3*chunk, n)
    // 每个核独立执行相同的加法操作
}

我的经验:数据并行最怕的是“负载不均”。如果数据分布不均匀,有的核早早干完,有的核还在苦干。我曾经遇到过这种情况,四个核的利用率分别是100%、80%、30%、10%。后来改用动态任务分配才解决。

任务并行

任务并行是把不同的任务分配给不同的核。每个核干的事不一样。

举个例子:一个自动驾驶系统。核1做目标检测,核2做路径规划,核3做传感器融合,核4做控制输出。各干各的,最后汇总。

任务并行比数据并行难搞。因为任务之间可能有依赖关系。比如路径规划必须等目标检测的结果。这就涉及同步和通信。

我个人习惯在任务并行时,先画一个依赖图。搞清楚哪些任务可以并行,哪些必须串行。然后才分配核资源。

流水线并行

流水线并行有点像工厂里的流水线。数据流经过多个阶段,每个阶段由一个核处理。

比如视频处理:核1读帧 → 核2解码 → 核3增强 → 核4编码输出。每个核只干自己那一摊事。

流水线并行的关键是平衡各阶段的时间。如果某个阶段特别慢,整个流水线都得等它。这就是所谓的“木桶效应”。

我曾经在一个图像处理项目里,流水线的第三阶段比别的阶段慢两倍。结果其他核大部分时间都在空转。后来我把第三阶段拆成两个子阶段,才把流水线跑满。

避坑指南:流水线并行有个隐藏坑——启动时间和排空时间。刚开始时,流水线是空的,需要一段时间才能填满。结束时,也需要时间排空。如果处理的数据量不大,这两个时间占比会很高,反而得不偿失。

小结

这一章我们聊了并行计算的基础。从Flynn分类法到三种并行模型,都是后续课程的地基。

记住几个关键点:

  • SIMD是AI芯片的杀手锏,数据并行是最常用的模式
  • MIMD提供灵活性,但需要处理好同步和通信
  • 流水线并行适合流式处理,但要小心负载均衡

下一章,我们会深入AI芯片的硬件架构。看看这些并行模型在真实的芯片上是怎么落地的。


专注资料整理