深度学习框架(例如 mxnet 和 pytorch)在后端自动构建计算图。使用计算图,系统了解所有依赖关系,并可以选择性地并行执行多个非相互依赖的任务以提高速度。例如,第 13.2 节中的图 13.2.2 独立地初始化了两个变量。因此,系统可以选择并行执行它们。
通常,单个运算符将使用所有 cpu 或单个 gpu 上的所有计算资源。例如,dot算子将使用所有 cpu 上的所有内核(和线程),即使在一台机器上有多个 cpu 处理器。这同样适用于单个 gpu。因此,并行化对于单设备计算机不是很有用。有了多个设备,事情就更重要了。虽然并行化通常在多个 gpu 之间最相关,但添加本地 cpu 会略微提高性能。例如,参见 hadjis等人。( 2016 年)专注于训练结合 gpu 和 cpu 的计算机视觉模型。借助自动并行化框架的便利,我们可以在几行 python 代码中实现相同的目标。更广泛地说,我们对自动并行计算的讨论集中在使用 cpu 和 gpu 的并行计算,以及计算和通信的并行化。
请注意,我们至少需要两个 gpu 才能运行本节中的实验。
import torchfrom d2l import torch as d2l
from mxnet import np, npxfrom d2l import mxnet as d2lnpx.set_np()
13.3.1。gpu 上的并行计算
让我们首先定义一个要测试的参考工作负载:run 下面的函数使用分配到两个变量中的数据在我们选择的设备上执行 10 次矩阵-矩阵乘法:x_gpu1和 x_gpu2。
devices = d2l.try_all_gpus()def run(x): return [x.mm(x) for _ in range(50)]x_gpu1 = torch.rand(size=(4000, 4000), device=devices[0])x_gpu2 = torch.rand(size=(4000, 4000), device=devices[1])
现在我们将函数应用于数据。为了确保缓存不会在结果中发挥作用,我们通过在测量之前对其中任何一个执行单次传递来预热设备。torch.cuda.synchronize() 等待 cuda 设备上所有流中的所有内核完成。它接受一个device参数,即我们需要同步的设备。current_device()如果设备参数为(默认),则它使用由 给出的当前设备none。
run(x_gpu1)run(x_gpu2) # warm-up all devicestorch.cuda.synchronize(devices[0])torch.cuda.synchronize(devices[1])with d2l.benchmark('gpu1 time'): run(x_gpu1) torch.cuda.synchronize(devices[0])with d2l.benchmark('gpu2 time'): run(x_gpu2) torch.cuda.synchronize(devices[1])
gpu1 time: 0.4967 secgpu2 time: 0.5151 sec
如果我们删除synchronize两个任务之间的语句,系统就可以自由地自动在两个设备上并行计算。
with d2l.benchmark('gpu1 & gpu2'): run(x_gpu1) run(x_gpu2) torch.cuda.synchronize()
gpu1 & gpu2: 0.5000 sec
devices = d2l.try_all_gpus()def run(x): return [x.dot(x) for _ in range(50)]x_gpu1 = np.random.uniform(size=(4000, 4000), ctx=devices[0])x_gpu2 = np.random.uniform(size=(4000, 4000), ctx=devices[1])
now we apply the function to the data. to ensure that caching does not play a role in the results we warm up the devices by performing a single pass on either of them prior to measuring.
run(x_gpu1) # warm-up both devicesrun(x_gpu2)npx.waitall()with d2l.benchmark('gpu1 time'): run(x_gpu1) npx.waitall()with d2l.benchmark('gpu2 time'): run(x_gpu2) npx.waitall()
gpu1 time: 0.5233 secgpu2 time: 0.5158 sec
if we remove the waitall statement between both tasks the system is free to parallelize computation on both devices automatically.
with d2l.benchmark('gpu1 & gpu2'): run(x_gpu1) run(x_gpu2) npx.waitall()
gpu1 & gpu2: 0.5214 sec
在上述情况下,总执行时间小于其各部分的总和,因为深度学习框架会自动安排两个 gpu 设备上的计算,而不需要代表用户编写复杂的代码。
13.3.2。并行计算与通信
在许多情况下,我们需要在不同设备之间移动数据,比如在 cpu 和 gpu 之间,或者在不同 gpu 之间。例如,当我们想要执行分布式优化时会发生这种情况,我们需要在多个加速器卡上聚合梯度。让我们通过在 gpu 上计算然后将结果复制回 cpu 来对此进行模拟。
def copy_to_cpu(x, non_blocking=false): return [y.to('cpu', non_blocking=non_blocking) for y in x]with d2l.benchmark('run on gpu1'): y = run(x_gpu1) torch.cuda.synchronize()with d2l.benchmark('copy to cpu'): y_cpu = copy_to_cpu(y) torch.cuda.synchronize()
run on gpu1: 0.5019 seccopy to cpu: 2.7168 sec
这有点低效。请注意,我们可能已经开始将 的部分内容复制y到 cpu,而列表的其余部分仍在计算中。这种情况会发生,例如,当我们计算小批量的(反向传播)梯度时。一些参数的梯度将比其他参数更早可用。因此,在 gpu 仍在运行时开始使用 pci-express 总线带宽对我们有利。在 pytorch 中,几个函数(例如to()和)copy_()承认一个显式non_blocking参数,它允许调用者在不需要时绕过同步。设置non_blocking=true 允许我们模拟这种情况。
with d2l.benchmark('run on gpu1 and copy to cpu'): y = run(x_gpu1) y_cpu = copy_to_cpu(y, true) torch.cuda.synchronize()
run on gpu1 and copy to cpu: 2.4682 sec
def copy_to_cpu(x): return [y.copyto(npx.cpu()) for y in x]with d2l.benchmark('run on gpu1'): y = run(x_gpu1) npx.waitall()with d2l.benchmark('copy to cpu'): y_cpu = copy_to_cpu(y) npx.waitall()
run on gpu1: 0.5796 seccopy to cpu: 3.0989 sec
this is somewhat inefficient. note that we could already start copying parts of y to the cpu while the remainder of the list is still being computed. this situation occurs, e.g., when we compute the gradient on a minibatch. the gradients of some of the parameters will be available earlier than that of others. hence it works to our advantage to start using pci-express bus bandwidth while the gpu is still running. removing waitall between both parts allows us to simulate this scenario.
with d2l.benchmark('run on gpu1 and copy to cpu'): y = run(x_gpu1) y_cpu = copy_to_cpu(y) npx.waitall()
run on gpu1 and copy to cpu: 3.3488 sec
两个操作所需的总时间(正如预期的那样)小于它们各部分的总和。请注意,此任务不同于并行计算,因为它使用不同的资源:cpu 和 gpu 之间的总线。事实上,我们可以同时在两个设备上进行计算和通信。如上所述,计算和通信之间存在依赖关系:y[i]必须在将其复制到 cpu 之前进行计算。幸运的是,系统可以y[i-1]边计算边 复制y[i],以减少总运行时间。
我们以在一个 cpu 和两个 gpu 上进行训练时简单的两层 mlp 的计算图及其依赖关系的图示作为结尾,如图13.3.1所示。手动安排由此产生的并行程序将非常痛苦。这就是拥有基于图形的计算后端进行优化的优势所在。
图 13.3.1两层 mlp 在一个 cpu 和两个 gpu 上的计算图及其依赖关系。
13.3.3。概括
现代系统具有多种设备,例如多个 gpu 和 cpu。它们可以并行、异步使用。
现代系统还具有多种通信资源,例如 pci express、存储(通常是固态驱动器或通过网络)和网络带宽。它们可以并联使用以达到最高效率。
后端可以通过自动并行计算和通信来提高性能。
13.3.4。练习
run在本节定义的函数中执行了八个操作。它们之间没有依赖关系。设计一个实验,看看深度学习框架是否会自动并行执行它们。
当单个操作员的工作量足够小时,并行化甚至可以在单个 cpu 或 gpu 上提供帮助。设计一个实验来验证这一点。
设计一个实验,在 cpu、gpu 上使用并行计算,并在两个设备之间进行通信。
使用 nvidia 的nsight等调试器 来验证您的代码是否有效。
设计包含更复杂数据依赖关系的计算任务,并运行实验以查看是否可以在提高性能的同时获得正确的结果。
探宝机器人:助人类探寻地下蕴藏着的无限价值
选择可靠的光伏发电厂家的方法有哪些?
车载电源的额定功率
一文了解移动互联网和互联网的区别
电信网通TD-SCDMA设备招标开始
PyTorch教程-13.3. 自动并行
GPT-4 遭投诉要求禁用,OpenAI 为何成为众矢之的?
为什么三星Note7召回不含中国
FPGA:大幅渗透医疗电子领域,逐步SoC化
让人工智能做自己的理财顾问,可靠吗?
日光灯低压直流驱动电路图解析
iphone8什么时候上市?iphone8最新消息:iphone8外形终确定,狂产一亿台?余承东为P10强力甩锅
0-5V/0-10V转rs485远程I/O数据采集Modbus
10年内物联网智能装置数量将超过120亿
配电网智能一体化保护控制成套设备
拉绳位移传感器SPD-4-3数据采集系统
局部放电测试的测试原理介绍
友尚集团推出SAMSUNG低功耗的Multimedia Rich全方位Cortex Table
串行通信SPI总线的详解分析
华为百度积极发力智能电动汽车