diff --git a/.translate/state/need_for_speed.md.yml b/.translate/state/need_for_speed.md.yml index c9970c5..8cd13c4 100644 --- a/.translate/state/need_for_speed.md.yml +++ b/.translate/state/need_for_speed.md.yml @@ -1,5 +1,5 @@ -source-sha: 21f1ea0669031ccc0ee0194878439a87de5d248d -synced-at: "2026-04-10" +source-sha: c0a74f003dd77fc52558b7b402b0f058b6b31bf7 +synced-at: "2026-04-12" model: claude-sonnet-4-6 mode: UPDATE section-count: 5 diff --git a/lectures/need_for_speed.md b/lectures/need_for_speed.md index 99186c9..ddfa453 100644 --- a/lectures/need_for_speed.md +++ b/lectures/need_for_speed.md @@ -14,31 +14,25 @@ translation: Major Scientific Libraries: 主要科学库 Major Scientific Libraries::Why do we need them?: 为什么需要它们? Major Scientific Libraries::Python's Scientific Ecosystem: Python 的科学生态系统 - Pure Python is slow: 纯 Python 速度慢 - Pure Python is slow::High vs low level code: 高级语言与低级语言 - Pure Python is slow::Where are the bottlenecks?: 瓶颈在哪里? - Pure Python is slow::Where are the bottlenecks?::Dynamic typing: 动态类型 - Pure Python is slow::Where are the bottlenecks?::Static types: 静态类型 - Pure Python is slow::Data Access: 数据访问 - Pure Python is slow::Data Access::Summing with Compiled Code: 使用编译代码求和 - Pure Python is slow::Data Access::Summing in Pure Python: 在纯 Python 中求和 - Pure Python is slow::Summary: 总结 + Why is Pure Python Slow?: 为什么纯 Python 速度较慢? + Why is Pure Python Slow?::Type Checking: 类型检查 + Why is Pure Python Slow?::Type Checking::Dynamic typing: 动态类型 + Why is Pure Python Slow?::Type Checking::Static types: 静态类型 + Why is Pure Python Slow?::Data Access: 数据访问 + Why is Pure Python Slow?::Data Access::Summing with Compiled Code: 使用编译代码求和 + Why is Pure Python Slow?::Data Access::Summing in Pure Python: 用纯 Python 求和 + Why is Pure Python Slow?::Summary: 总结 Accelerating Python: 加速 Python Accelerating Python::Vectorization: 向量化 - Accelerating Python::Vectorization vs for pure Python loops: 向量化 vs 纯 Python 循环 + Accelerating Python::Vectorization vs pure Python loops: 向量化 vs 纯 Python 循环 Accelerating Python::JIT compilers: JIT 编译器 Parallelization: 并行化 Parallelization::Parallelization on CPUs: CPU 上的并行化 - Parallelization::Parallelization on CPUs::Multiprocessing: 多进程 Parallelization::Parallelization on CPUs::Multithreading: 多线程 - Parallelization::Parallelization on CPUs::Advantages and Disadvantages: 优缺点 + Parallelization::Parallelization on CPUs::Multiprocessing: 多进程 + Parallelization::Parallelization on CPUs::Which Should We Use?: 应该选择哪种方式? Parallelization::Hardware Accelerators: 硬件加速器 - Parallelization::Hardware Accelerators::GPUs and TPUs: GPU 和 TPU - Parallelization::Hardware Accelerators::Why TPUs/GPUs Matter: 为何 TPU/GPU 至关重要 - Parallelization::Single GPUs vs GPU Servers: 单 GPU 与 GPU 服务器 - Parallelization::Single GPUs vs GPU Servers::Single GPU Systems: 单 GPU 系统 - Parallelization::Single GPUs vs GPU Servers::Multi-GPU Servers: 多 GPU 服务器 - Parallelization::Summary: 总结 + Parallelization::Accessing GPU Resources: 访问 GPU 资源 --- (speed)= @@ -153,60 +147,44 @@ import random * Pandas 提供用于操作数据的类型和函数。 * Numba 提供一个即时编译器,与 NumPy 配合良好,有助于加速 Python 代码。 -我们将在本系列讲座中详细讨论所有这些库。 - -## 纯 Python 速度慢 - -如上所述,科学库的一大吸引力在于更快的执行速度。 - -我们将讨论科学库如何帮助我们加速代码。 - -对于这个主题,如果我们理解是什么导致了执行速度慢,将会很有帮助。 - -### 高级语言与低级语言 - -像 Python 这样的高级语言是为人类优化的。 - -这意味着程序员可以将许多细节留给运行时环境处理,例如: +我们将在本系列讲座中广泛讨论所有这些库。 -* 指定变量类型 -* 内存分配与释放 -* 等等。 +## 为什么纯 Python 速度较慢? -此外,纯 Python 由一个[解释器](https://en.wikipedia.org/wiki/Interpreter_(computing))运行,该解释器逐条语句地执行代码。 +如上所述,用纯 Python 编写的数值计算代码相对较慢。 -这使得 Python 灵活、交互性强、易于编写、易于阅读,并且相对容易调试。 +让我们尝试理解是什么导致了执行速度缓慢。 -另一方面,Python 的标准实现(称为 CPython)无法与 C 或 Fortran 等编译语言的速度相媲美。 +### 类型检查 -### 瓶颈在哪里? +纯 Python 操作中的一个开销来源是类型检查。 -为什么会这样呢? +让我们尝试理解其中的问题。 #### 动态类型 ```{index} single: Dynamic Typing ``` -考虑这个 Python 操作: +考虑以下 Python 操作 ```{code-cell} python3 a, b = 10, 10 a + b ``` -即使对于这个简单的操作,Python 解释器也有相当多的工作要做。 +即使对于这个简单的操作,Python 解释器也需要做相当多的工作。 -例如,在语句 `a + b` 中,解释器必须知道调用哪个操作。 +例如,在语句 `a + b` 中,解释器必须知道应该调用哪种操作。 -如果 `a` 和 `b` 是字符串,那么 `a + b` 需要字符串连接: +如果 `a` 和 `b` 是字符串,那么 `a + b` 需要执行字符串拼接 ```{code-cell} python3 a, b = 'foo', 'bar' a + b ``` -如果 `a` 和 `b` 是列表,那么 `a + b` 需要列表连接: +如果 `a` 和 `b` 是列表,那么 `a + b` 需要执行列表拼接 ```{code-cell} python3 a, b = ['foo'], ['bar'] @@ -224,9 +202,9 @@ a + b ```{index} single: Static Types ``` -编译语言通过显式的静态类型来避免这些开销。 +编译型语言通过显式的静态类型来避免这些开销。 -例如,考虑以下 C 代码,它对从 1 到 10 的整数求和: +例如,考虑以下 C 代码,它计算从 1 到 10 的整数之和 ```{code-block} c :class: no-execute @@ -244,58 +222,58 @@ int main(void) { } ``` -变量 `i` 和 `sum` 被明确声明为整数。 +变量 `i` 和 `sum` 被显式声明为整数类型。 -此外,当我们写出 `int i` 这样的语句时,我们是在向编译器承诺,在程序执行的整个过程中,`i` 将*始终*是一个整数。 +此外,当我们写下 `int i` 这样的语句时,我们是在向编译器承诺:在整个程序执行过程中,`i` 将*始终*是一个整数。 因此,表达式 `sum + i` 中加法的含义是完全明确的。 -无需类型检查,因此没有额外开销。 +无需进行类型检查,也就不存在额外开销。 ### 数据访问 -高级语言速度慢的另一个原因是数据访问。 +高级语言速度较慢的另一个原因是数据访问方式。 -为了说明这一点,让我们考虑对一些数据求和的问题——比如,一组整数。 +为了说明这一点,让我们考虑对某些数据求和的问题——比如,一组整数的集合。 #### 使用编译代码求和 -在 C 或 Fortran 中,整数数组存储在单个连续的内存块中: +在 C 或 Fortran 中,整数数组存储在一块连续的内存空间中 -* 例如,一个 64 位整数存储在 8 字节的内存中。 -* $n$ 个这样的整数组成的数组占据 $8n$ 个*连续*的内存槽。 +* 例如,一个 64 位整数占用 8 个字节的内存。 +* 由 $n$ 个这样的整数组成的数组占用 $8n$ 个连续字节。 此外,数据类型在编译时是已知的。 因此,每个连续的数据点都可以通过在内存空间中向前移动一个已知且固定的量来访问。 -#### 在纯 Python 中求和 +#### 用纯 Python 求和 -Python 在一定程度上试图复制这些思想。 +Python 在一定程度上尝试复现这些思路。 例如,在标准 Python 实现(CPython)中,列表元素被放置在某种意义上连续的内存位置。 -然而,这些列表元素更像是指向数据的指针,而非实际数据。 +然而,这些列表元素更像是指向数据的指针,而不是实际的数据本身。 -因此,访问数据值本身仍然存在开销。 +因此,访问数据值本身仍然存在额外开销。 -这种开销是导致执行缓慢的主要因素。 +这种开销是导致执行速度缓慢的主要原因之一。 ### 总结 -上面的讨论是否意味着我们应该对所有事情都切换到 C 或 Fortran? +上述讨论是否意味着我们应该将所有工作都切换到 C 或 Fortran? 答案是:绝对不是! -对于任何给定的程序,相对而言只有少数几行代码对时间要求严苛。 +对于任何给定的程序,真正对时间敏感的代码行数相对较少。 -因此,用 Python 这样的高生产力语言编写大部分代码要高效得多。 +因此,用 Python 这样的高生产力语言编写大部分代码效率要高得多。 -此外,即使对于那些*确实*对时间要求严苛的代码行,我们现在也可以通过使用 Python 的科学库,达到甚至超过从 C 或 Fortran 编译的二进制文件的速度。 +此外,即使对于那些*确实*对时间敏感的代码行,我们现在也可以通过使用 Python 的科学计算库来达到甚至超越由 C 或 Fortran 编译的二进制文件的性能。 -在这一点上,我们强调,在过去几年中,加速代码基本上已经与并行化同义。 +在这一点上,我们强调,在过去几年中,代码加速在本质上已经与并行化画上了等号。 -这个任务最好留给专门的编译器! +这项任务最好留给专门的编译器来完成! ## 加速 Python @@ -335,7 +313,7 @@ Python 在一定程度上试图复制这些思想。 ```{figure} /_static/lecture_specific/need_for_speed/matlab.png ``` -NumPy 使用类似的模型,灵感来自 MATLAB。 +NumPy 使用类似的模型,灵感来源于 MATLAB ### 向量化 vs 纯 Python 循环 @@ -404,7 +382,7 @@ with qe.Timer(): 1. 增加每台机器中嵌入的 CPU 数量 1. 连接 GPU 和 TPU 等硬件加速器 -对于程序员来说,挑战在于利用这些硬件并行运行多个进程。 +对于程序员来说,挑战在于充分利用这些硬件,同时运行多个进程(即并行)。 下面我们讨论科学计算中的并行化,重点关注: @@ -423,38 +401,35 @@ with qe.Timer(): 在多进程中,*每个进程都有自己的内存空间*,尽管物理内存芯片可能是共享的。 -#### 多线程 +所有线程共享同一内存空间,因此它们可以在不复制数据的情况下对同一数组进行读写。 -多线程与多进程类似,不同之处在于,在执行期间,所有线程 *共享同一内存空间*。 +例如,当对大型数组的数值操作在现代笔记本电脑上运行时,工作负载可以分配到机器的多个 CPU 核心上,每个核心处理数组的一部分。 +```{note} 由于一些[遗留的设计特性](https://wiki.python.org/moin/GlobalInterpreterLock),原生 Python 难以实现多线程。 - 但这对于 NumPy 和 Numba 等科学库来说并不是限制。 - 从这些库导入的函数和经过 JIT 编译的代码在低级执行环境中运行,Python 的遗留限制在那里并不适用。 +``` -#### 优缺点 - -多线程更加轻量,因为大多数系统资源和内存资源由线程共享。 +#### 多进程 -此外,多个线程访问共享内存池这一事实对于数值编程来说极为方便。 +多进程是指运行多个独立进程,每个进程都有自己独立的内存空间。 -另一方面,多进程更灵活,可以分布在集群中。 +由于内存不共享,进程之间通过相互传递数据进行通信。 -对于我们在这些讲座中所做的绝大多数工作,多线程就足够了。 +多进程可以在单台机器上运行,也可以分布在通过网络连接的多台机器(集群)上。 -### 硬件加速器 +#### 应该选择哪种方式? -虽然拥有多核的 CPU 已成为并行计算的标准,但随着专用硬件加速器的兴起,发生了更为深刻的变化。 +对于单台机器上的数值工作,通常首选多线程——它更加轻量,且共享内存模型非常便利。 -这些加速器专门为科学计算、机器学习和数据科学中出现的高度并行计算而设计。 +当需要扩展到单台机器之外时,多进程就变得重要了。 -#### GPU 和 TPU +对于我们在这些讲座中所做的绝大多数工作,多线程就足够了。 -两种最重要的硬件加速器类型是: +### 硬件加速器 -* **GPU**(图形处理单元)和 -* **TPU**(张量处理单元)。 +并行化的一个更为重要的来源来自专用硬件加速器,尤其是 **GPU**(图形处理单元)。 GPU 最初是为渲染图形而设计的,这需要同时对许多像素执行相同的操作。 @@ -462,53 +437,28 @@ GPU 最初是为渲染图形而设计的,这需要同时对许多像素执行 :scale: 40 ``` -科学家和工程师意识到,这种架构——许多简单的处理器并行工作——非常适合科学计算任务。 - -TPU 是较新的发展,由 Google 专门为机器学习工作负载设计。 - -与 GPU 一样,TPU 擅长并行执行大量矩阵运算。 - -#### 为何 TPU/GPU 至关重要 - -使用硬件加速器带来的性能提升可能是惊人的。 - -例如,一块现代 GPU 可以包含数千个小型处理核心,而 CPU 中通常只有 8 到 64 个核心。 - -当一个问题可以表示为对数据数组进行许多独立操作时,GPU 的速度可以比 CPU 快几个数量级。 - -这对科学计算尤为重要,因为许多算法天然地映射到 GPU 的并行架构上。 - -### 单 GPU 与 GPU 服务器 +这种架构——数千个简单核心对不同数据点执行相同指令——恰好非常适合科学计算。 -访问 GPU 资源有两种常见方式: +```{note} +**核心**是芯片中的一个独立处理单元——一种可以自主执行指令的电路。CPU 通常拥有少量强大的核心,每个核心都能处理复杂的操作序列。而 GPU 则集成了数千个更小、更简单的核心,每个核心专门执行基本的算术运算。GPU 的强大之处在于让所有这些核心同时处理同一问题的不同部分。 +``` -#### 单 GPU 系统 +当一个计算可以表示为对大型数据数组进行独立操作时,GPU 的速度可以比 CPU 快几个数量级。 -许多工作站和笔记本电脑现在配备了性能强劲的 GPU,或者可以安装 GPU。 +由 Google 为机器学习设计的 **TPU**(张量处理单元)遵循类似的理念,专门针对大规模并行矩阵运算进行优化。 -单块现代 GPU 可以显著加速许多科学计算任务。 +### 访问 GPU 资源 -对于个人研究人员和小型项目,单块 GPU 通常就足够了。 +许多工作站和笔记本电脑现在都配备了性能强劲的 GPU,对于个人研究项目来说,单块现代 GPU 通常就足够了。 现代 Python 库(如本系列讲座中广泛讨论的 JAX)可以以最少的代码改动自动检测并使用可用的 GPU。 -#### 多 GPU 服务器 - -对于规模更大的问题,包含多个 GPU(通常每台服务器 4-8 个 GPU)的服务器越来越普遍。 +对于规模更大的问题,包含多个 GPU(通常每台机器 4--8 个 GPU)的服务器越来越普遍。 ```{figure} /_static/lecture_specific/need_for_speed/dgx.png :scale: 40 ``` - -借助适当的软件,计算可以分布在多个 GPU 上,无论是在单台服务器内还是跨多台服务器。 - -这使研究人员能够处理在单个 GPU 或 CPU 上不可行的问题。 - -### 总结 - -GPU 计算正变得越来越容易获取,尤其是在 Python 中。 - -一些 Python 科学库(如 JAX)现在支持 GPU 加速,对现有代码的改动极少。 +借助适当的软件,计算可以分布在多个 GPU 上,无论是在单台服务器内还是跨集群。 我们将在后续讲座中更详细地探讨 GPU 计算,并将其应用于一系列经济学应用。