Skip to content

Commit

Permalink
fix typos (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
luweizheng authored May 16, 2024
1 parent 90b817e commit 18ba51c
Show file tree
Hide file tree
Showing 21 changed files with 323 additions and 324 deletions.
2 changes: 1 addition & 1 deletion ch-mpi-large-model/data-parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ $$
}
$$

对这些分布在不同 GPU 上的梯度同步时,可以使用 MPI 提供的 `AllReduce` 原语。MPI 的 `AllReduce` 将每块 GPU 上分别计算得到的梯度收集起来,计算平均后,再将更新后的梯度重新分发给各块 GPU。
同步不同 GPU 上的梯度,可以使用 MPI 提供的 `AllReduce` 原语。MPI 的 `AllReduce` 将每块 GPU 上分别计算得到的梯度收集起来,计算平均后,再将更新后的梯度重新分发给各块 GPU。

如 {numref}`fig-data-parallel-all-reduce` 所示,梯度同步阶段,MPI 的 `AllReduce` 原语将各 GPU 上的梯度进行同步。

Expand Down
2 changes: 1 addition & 1 deletion ch-mpi-large-model/pipeline-parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ name: fig-pipeline-parallel-distributed

## 流水线并行 + 数据并行

流水线并行与数据并行是相互正交的,两者可以结合起来同时使用。由于两种并行是正交的,互不干扰,为避免数据传输错乱,应使用 MPI 的 Communicator 来做隔离。在 {numref}`sec-mpi-hello-world` 中我们曾经提到,Communicator 可以被理解为 MPI 中的组,同一个 GPU 可以在不同的 Communicator 中。如 {numref}`fig-pipeline-parallel-data-parallel` 所示,我们创建了两类 Communicator:红色为流水线并行的 Communicator,蓝色为数据并行的 Communicator。同一个 GPU 既属于红色,也属于蓝色:既要实现流水线并行中模型层之间的通信,也要实现数据并行的梯度的同步
流水线并行与数据并行是相互正交的,两者可以结合起来同时使用。由于两种并行是正交的,互不干扰,为避免数据传输错乱,应使用 MPI 的 Communicator 来做隔离。在 {numref}`sec-mpi-hello-world` 中我们曾经提到,Communicator 可以被理解为 MPI 中的组,同一个 GPU 可以在不同的 Communicator 中。如 {numref}`fig-pipeline-parallel-data-parallel` 所示,我们创建了两类 Communicator:红色为流水线并行的 Communicator,蓝色为数据并行的 Communicator。同一个 GPU 既属于红色,也属于蓝色:既要实现流水线并行中模型层之间的通信,也要实现数据并行的梯度同步

```{figure} ../img/ch-mpi-large-model/pipeline-parallel-data-parallel.svg
---
Expand Down
16 changes: 8 additions & 8 deletions ch-mpi/collective.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"\n",
"## 同步\n",
"\n",
"MPI 的计算分布在多个进程,每个进程的计算速度有快有慢。`Comm.Barrier` 对 Communicator 里所有进程都执行同步等待,就像 Barrier 的英文名一样,设置一个屏障,等待所有进程都执行完。计算比较快的进程们达到 `Comm.Barrier`,无法执行 `Comm.Barrier` 之后的计算逻辑,必须等待其他所有进程都到达这里才行\n",
"MPI 的计算分布在多个进程,每个进程的计算速度有快有慢。`Comm.Barrier` 对 Communicator 里所有进程都执行同步等待,就像 Barrier 的英文名一样,设置一个屏障,等待所有进程都执行完。计算比较快的进程们达到 `Comm.Barrier`,无法执行 `Comm.Barrier` 之后的计算逻辑,必须等待其他所有进程都运行到 `Comm.Barrier` 才行\n",
"\n",
"## 数据移动\n",
"\n",
Expand All @@ -34,9 +34,9 @@
"---\n",
"Broadcast\n",
"```\n",
"### 案例1:广播\n",
"### 案例:广播\n",
"\n",
"{numref}`code-mpi-broadcast-py` 对如何将一个 NumPy 数组广播到所有的进程中进行了演示\n",
"{numref}`code-mpi-broadcast-py` 演示了如何将一个 NumPy 数组广播到所有的进程:\n",
"\n",
"```{code-block} python\n",
":caption: broadcast.py\n",
Expand Down Expand Up @@ -89,10 +89,10 @@
"source": [
"### Scatter 和 Gather\n",
"\n",
"`Comm.Scatter` 和 `Comm.Gather` 是一组相对应的操作,如\n",
"`Comm.Scatter` 和 `Comm.Gather` 是一组相对应的操作\n",
"\n",
"`Comm.Scatter` 将数据从一个进程分散到组中的所有进程,一个进程将数据分散成多个块,每个块发送给对应的进程。其他进程接收并存储各自的块。Scatter 操作适用于将一个较大的数据集分割成多个小块。\n",
"`Comm.Gather` 与 `Comm.Scatter` 相反,将组里所有进程的小数据块归集到一个进程上。\n",
"* `Comm.Scatter` 将数据从一个进程分散到组中的所有进程,一个进程将数据分散成多个块,每个块发送给对应的进程。其他进程接收并存储各自的块。Scatter 操作适用于将一个较大的数据集分割成多个小块。\n",
"* `Comm.Gather` 与 `Comm.Scatter` 相反,将组里所有进程的小数据块归集到一个进程上。\n",
"\n",
"```{figure} ../img/ch-mpi/scatter-gather.svg\n",
"---\n",
Expand All @@ -102,7 +102,7 @@
"Scatter 与 Gather\n",
"```\n",
"\n",
"### 案例2:Scatter\n",
"### 案例:Scatter\n",
"\n",
"{numref}`code-mpi-scatter` 演示了如何使用 Scatter 将数据分散到所有进程。\n",
"\n",
Expand Down Expand Up @@ -184,7 +184,7 @@
"\n",
"## 集合计算\n",
"\n",
"集合计算指的是在将散落在不同进程的数据聚合在一起的时候,对数据进行计算,比如 `Comm.Reduce` 和 [`Intracomm`](https://mpi4py.readthedocs.io/en/stable/reference/mpi4py.MPI.Intracomm.html) 等。如 {numref}`fig-mpi-reduce` 和 {numref}`fig-mpi-scan` 所示,数据归集到某个进程时,还执行了聚合函数 `f`,常用的聚合函数有求和 [`MPI.SUM`](https://mpi4py.readthedocs.io/en/stable/reference/mpi4py.MPI.SUM.html) 等。\n",
"集合计算是散落在不同进程的数据聚合在一起的同时对数据进行计算,比如 `Comm.Reduce`。如 {numref}`fig-mpi-reduce` 和 {numref}`fig-mpi-scan` 所示,数据归集到某个进程时,还执行了聚合函数 `f`,常用的聚合函数有求和 [`MPI.SUM`](https://mpi4py.readthedocs.io/en/stable/reference/mpi4py.MPI.SUM.html) 等。\n",
"\n",
"```{figure} ../img/ch-mpi/reduce.svg\n",
"---\n",
Expand Down
12 changes: 6 additions & 6 deletions ch-mpi/mpi-hello-world.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
"\n",
"## World 和 Rank\n",
"\n",
"在进行 MPI 编程时,进程之间要互相通信,我们首先要解决两个问题:在 MPI 的众多进程中,“我是谁?”,除了我,“还有谁?”。MPI 标准中, [`MPI_Comm_rank`](https://learn.microsoft.com/en-us/message-passing-interface/mpi-comm-rank-function) 定义了我是谁。[`MPI_COMM_WORLD`](https://learn.microsoft.com/en-us/message-passing-interface/mpi-comm-size-function) 定义了还有谁的问题。 开始一个 MPI 程序时,要先定义一个 World,这个世界里有 `size` 个进程,每个进程有一个识别自己的号码,这个号码被称为 Rank,Rank 是 0 到 `size - 1` 的整数。更严肃地阐述:\n",
"在进行 MPI 编程时,进程之间要互相通信,我们首先要解决两个问题:在 MPI 的众多进程中,“我是谁?”,除了我,“还有谁?”。MPI 标准中, [`MPI_Comm_rank`](https://learn.microsoft.com/en-us/message-passing-interface/mpi-comm-rank-function) 定义了我是谁。[`MPI_COMM_WORLD`](https://learn.microsoft.com/en-us/message-passing-interface/mpi-comm-size-function) 定义了还有谁。 开始一个 MPI 程序时,要先定义一个世界(World),这个 World 有 `size` 个进程,每个进程有一个识别自己的号码,这个号码被称为 Rank,Rank 是 0 到 `size - 1` 的整数。更严肃地阐述:\n",
"\n",
"* MPI 中的 World 是指所有参与并行计算的进程的总集合。在一个 MPI 程序中,所有的进程都属于一个默认的通信域,这个通信域就被称为 `MPI_COMM_WORLD`。所有在这个通信域中的进程都可以进行通信。\n",
"* World 中的每个进程都有一个唯一的 Rank,Rank 用来标识进程在通信域中的位置。由于每个进程有自己的 Rank 号码,那在编程时,可以控制,使得 Rank 为 0 的进程发送数据给 Rank 为 1 的进程。\n",
"* World 中的每个进程都有一个唯一的 Rank,Rank 用来标识进程在通信域中的位置。由于每个进程有自己的 Rank 号码,程序员可以控制 Rank 为 0 的进程发送数据给 Rank 为 1 的进程。\n",
"\n",
"## 案例:Hello World\n",
"\n",
Expand All @@ -46,7 +46,7 @@
"comm.Barrier()\n",
"```\n",
"\n",
"在这段程序中,`print` 是上单个进程内执行的,打印出当前进程的 Rank 和主机名。[`comm.Barrier()`](https://mpi4py.readthedocs.io/en/stable/reference/mpi4py.MPI.Comm.html#mpi4py.MPI.Comm.Barrier) 将每个进程做了阻断,直到所有进程执行完毕后,才会进行下面的操作。本例中,`comm.Barrier()` 后无其他操作,程序将退出。\n",
"在这段程序中,`print()` 是在单个进程内执行的,打印出当前进程的 Rank 和主机名。[`comm.Barrier()`](https://mpi4py.readthedocs.io/en/stable/reference/mpi4py.MPI.Comm.html#mpi4py.MPI.Comm.Barrier) 将每个进程做了阻断,直到所有进程都到达 `comm.Barrier()` 时,才会进行 `comm.Barrier()` 之后操作。本例中,`comm.Barrier()` 后无其他操作,程序将退出。\n",
"\n",
"如果在个人电脑上,启动 8 个进程,在命令行中执行:"
]
Expand Down Expand Up @@ -81,7 +81,7 @@
"source": [
"不同厂商的 `mpiexec` 的参数会有一些区别。相比 C/C++ 或 Fortran,mpi4py 的方便之处在于不需要使用 `mpicc` 编译器进行编译,直接执行即可。\n",
"\n",
"如果有一个集群,且集群挂载了一个共享的文件系统,即集群上每个节点上的特定目录的内容是一样的,`hello.py` `mpiexec` 是一模一样的。可以这样拉起\n",
"如果有一个集群,且集群挂载了一个共享的文件系统,即集群上每个节点上的特定目录的内容是一样的,源代码 `hello.py` 和安装的 MPI `mpiexec` 是一模一样的。可以这样拉起多个 MPI 进程\n",
"\n",
"```bash\n",
"mpiexec –hosts h1:4,h2:4,h3:4,h4:4 –n 16 python hello.py\n",
Expand All @@ -102,9 +102,9 @@
"\n",
"## Communicator\n",
"\n",
"刚才我们提到了 World 的概念,并使用了 `MPI_COMM_WORLD`,更准确地说,`MPI_COMM_WORLD` 是一个 Communicator。MPI 将进程划分到不同的组(Group)中,每个 Group 有不同的 Color,Group 和 Color 共同组成了 Communicator,或者说 Communicator 是 Group + Color 的名字,一个默认的 Communicator 就是 `MPI_COMM_WORLD`。\n",
"刚才我们提到了 World 的概念,并使用了 `MPI_COMM_WORLD`,更准确地说,`MPI_COMM_WORLD` 是一个 Communicator。MPI 将进程划分到不同的组(Group)中,每个 Group 有不同的 Color,Group 和 Color 共同组成了 Communicator,或者说 Communicator 是 Group + Color,一个默认的 Communicator 就是 `MPI_COMM_WORLD`。\n",
"\n",
"对于一个进程,它可能在不同的 Communicator 中,因此它在不同 Communicator 中的 Rank 可能也不一样。{numref}`fig-mpi-communicatitor` (a) 、(b) 和(c)为三个 Communicator,圆圈为进程。当我们启动一个 MPI 程序时,就创建了默认的 Communicator(`MPI_COMM_WORLD`),如 {numref}`fig-mpi-communicatitor` (a) 所示。每个进程在 Communicator 内被分配了一个 Rank 号码,图中圆圈上的数字是进程在这个 Communicator 中的 Rank。同样的进程可以被划归到不同的 Communicator 中,且相同的进程在不同 Communicator 的 Rank 可以不一样,如 {numref}`fig-mpi-communicatitor` (b)和(c)所示。每个 Communicator 内进程通信是相互独立的。大部分简单的程序,`MPI_COMM_WORLD` 就足够了。\n",
"对于一个进程,它可能在不同的 Communicator 中,因此它在不同 Communicator 中的 Rank 可能也不一样。{numref}`fig-mpi-communicatitor` (a) 、(b) 和(c)为三个 Communicator,圆圈为进程。当我们启动一个 MPI 程序时,就创建了默认的 Communicator(`MPI_COMM_WORLD`),如 {numref}`fig-mpi-communicatitor` (a) 所示。每个进程在 Communicator 内被分配了一个 Rank 号码,图中圆圈上的数字是进程在这个 Communicator 中的 Rank。同样的进程可以被划归到不同的 Communicator 中,且相同的进程在不同 Communicator 的 Rank 可以不一样,如 {numref}`fig-mpi-communicatitor` (b)和(c)所示。每个 Communicator 内进程通信是相互独立的。大部分简单的程序,使用默认的 `MPI_COMM_WORLD` 就足够了。\n",
"\n",
"```{figure} ../img/ch-mpi/communicator.svg\n",
"---\n",
Expand Down
16 changes: 6 additions & 10 deletions ch-mpi/mpi-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Message Passing Interface(MPI)是个经典的并行计算工具,由于它

## 历史

MPI 的发展可以追溯到20世纪80年代末和90年代初,彼时已经开始出现了超级计算机,主要用于科学和工程计算,包括气象模拟、核能研究、分子建模、流体动力学等领域。与现在的大数据集群一样,超级计算机主要是一组高性能计算机组成的集群。为了完成上述科学和工程计算问题,使得程序得以在多台计算机上并行运行。MPI 出现之前,多个研究小组和机构开始独立开发并推广自己的通信库,但这导致了互操作性和可移植性的问题。因此,社区迫切需要一种标准化的方法来编写并行应用程序。
MPI 的发展可以追溯到20世纪80年代末和90年代初,彼时已经开始出现了超级计算机,主要用于科学和工程计算,包括气象模拟、核能研究、分子建模、流体动力学等领域。超级计算机主要是一组高性能计算机组成的集群,服务科学和工程计算问题,使得程序得以在多台计算机上并行运行。MPI 出现之前,多个研究小组和机构开始独立开发并推广自己的通信库,但这导致了互操作性和可移植性的问题。因此,社区迫切需要一种标准化的方法来编写并行应用程序。

1992年,图灵奖得主 Jack Dongarra 联合几位学者提出了并行计算第一个草案:MPI1。第一个标准版本 MPI 1.0 最终于 1994 年发布。之后,MPI-2、MPI-3 接连发布,来自学术界和工业界的多位专家共同参与修订,不断根据最新的并行计算需求修改 MPI 标准。

Expand All @@ -22,32 +22,28 @@ MPI 标准定义了:

## 高速网络

如果进行多机并行,机器之间需要有高速互联网络。如果你的集群已经部署了这些硬件,并安装了某个 MPI 实现,MPI 可以充分利用这些高速网络的高带宽、低延迟的特性。这些网络大部分拥有超过 100Gbps 的带宽,但其价格也非常昂贵,通常在面向高性能计算的场景上才会配置这些网络设备。

数据中心经常部署的万兆网络,带宽在 10Gbps 量级,也可以使用 MPI,并且会有一定加速效果。

MPI 也可以在单机上运行,即:利用单台节点上的多个计算核心。
如果进行多机并行,机器之间需要有高速互联网络。如果你的集群已经部署了这些硬件,并安装了某个 MPI 实现,MPI 可以充分利用这些高速网络的高带宽、低延迟的特性。这些网络大部分拥有超过 100Gbps 的带宽,但其价格也非常昂贵,通常在面向高性能计算的场景上才会配置这些网络设备。数据中心经常部署的普通万兆网络,带宽在 10Gbps 量级。在万兆网络上也可以使用 MPI,并且会有一定加速效果。MPI 也可以在单机上运行,即:利用单台节点上的多个计算核心。

## 安装

刚才提到,MPI 有不同的实现,即不同的 MPI 厂商一般会提供:

* 编译器 `mpicc``mpicxx``mpifort`,分别用来编译 C、C++、Fortran 语言编写的源代码,源代码中一部分是多机通讯,一部分是单机计算,这些编译器通常将多机通讯与单机计算的代码一起编译,并生成可执行文件。
* 在多台节点上将并行程序拉起的 `mpirun``mpiexec`比如,在多少台节点上拉起多少进程等,都是通过 `mpiexec` 来完成的
* 编译器 `mpicc``mpicxx``mpifort`,分别用来编译 C、C++、Fortran 语言源代码,源代码中一部分是多机通讯,一部分是单机计算,这些编译器通常将多机通讯与单机计算的代码一起编译,并生成可执行文件。
* 执行并行程序的 `mpirun``mpiexec``mpiexec` 可以在多台节点拉起多个 MPI 进程

:::{note}
在很多 MPI 实现中,`mpirun``mpiexec` 的功能几乎相同,它们都可以将并行程序拉起。有些 MPI 实现的 `mpirun``mpiexec` 背后是同一个程序。但严禁地讲,MPI 标准中只定义了 `mpiexec`,并没有定义 `mpirun`,因此,`mpiexec` 应该更通用。
:::

如果使用 C/C++ 或 Fortran 这样的编译语言编写代码,一般的流程是:使用 `mpicc` 编译源代码,得到可执行文件比如,将可执行文件命名为 `parallel.o`;使用 `mpiexec` 在多台节点上将 `parallel.o` 并行程序拉起。mpi4py 将上述的编译环节封装
如果使用 C/C++ 或 Fortran 这样的编译语言编写代码,一般的流程是:使用 `mpicc` 编译源代码,得到可执行文件比如,将可执行文件命名为 `parallel.o`;使用 `mpiexec` 在多台节点上启动并运行 `parallel.o`。Python 是一种解释型语言,不是编译型语言,mpi4py 用户也不需要编译源代码

如果你的集群环境已经安装了 MPI,可以先将 MPI 加载到环境变量里,然后使用 `pip` 安装:

```bash
pip install mpi4py
```

如果你的集群环境没有 MPI,而又对编译这些流程不熟悉,可以直接用 `conda` 安装。 使用 `conda` 安装的软件已经完成了编译过程
如果你的集群环境没有 MPI,而又对编译这些流程不熟悉,可以直接用 `conda` 安装 MPICH。 `conda` 安装的是开源社区预编译好的软件

```bash
conda install -c conda-forge mpich
Expand Down
Loading

0 comments on commit 18ba51c

Please sign in to comment.