Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
CoolBran
d2l-zh
提交
375a475e
D
d2l-zh
项目概览
CoolBran
/
d2l-zh
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
d2l-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
375a475e
编写于
4月 26, 2018
作者:
A
Aston Zhang
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
auto para done
上级
2119e41b
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
52 addition
and
46 deletion
+52
-46
README.md
README.md
+1
-1
chapter_gluon-advances/auto-parallelism.md
chapter_gluon-advances/auto-parallelism.md
+39
-35
chapter_gluon-advances/lazy-evaluation.md
chapter_gluon-advances/lazy-evaluation.md
+4
-2
chapter_gluon-basics/use-gpu.md
chapter_gluon-basics/use-gpu.md
+8
-8
未找到文件。
README.md
浏览文件 @
375a475e
...
...
@@ -130,7 +130,7 @@ metric,指标
normalization,归一化
operator,运算
子
operator,运算
符
optimizer,优化器
...
...
chapter_gluon-advances/auto-parallelism.md
浏览文件 @
375a475e
# 自动并行计算
在
[
“惰性计算”
](
./lazy-evaluation.md
)
一节里我们提到MXNet后端会自动构建计算图。通过计算图,系统可以知道所有计算的依赖关系,并可以选择将没有依赖关系的多个任务并行执行来获得性能的提升。
再次
以
[
“惰性计算”
](
./lazy-evaluation.md
)
一节中的计算图(图8.1)为例。其中
`a = nd.ones((1, 2))`
和
`b = nd.ones((1, 2))`
这两步计算之间并没有依赖关系。因此,系统可以选择并行执行它们。
在
[
“惰性计算”
](
./lazy-evaluation.md
)
一节里我们提到MXNet后端会自动构建计算图。通过计算图,系统可以知道所有计算的依赖关系,并可以选择将没有依赖关系的多个任务并行执行来获得性能的提升。以
[
“惰性计算”
](
./lazy-evaluation.md
)
一节中的计算图(图8.1)为例。其中
`a = nd.ones((1, 2))`
和
`b = nd.ones((1, 2))`
这两步计算之间并没有依赖关系。因此,系统可以选择并行执行它们。
通常一个运算符会用掉一个CPU/GPU上所有计算资源。例如,
`dot`
操作符会用到所有CPU(即使是有多个CPU)或单个GPU上所有线程。因此在单CPU/GPU上并行运行多个运算符可能效果并不明显。本节中探讨的自动并行计算主要关注多CPU/GPU的并行计算,以及计算和通讯的并行。
通常一个运算符,例如
`+`
或者
`dot`
,会用掉一个计算设备上所有计算资源。
`dot`
同样用到所有CPU的核(即使是有多个CPU)和单GPU上所有线程。因此在单设备上并行运行多个运算符可能效果并不明显。自动并行主要的用途是多设备的计算并行,和计算与通讯的并行
。
首先导入本节中实验所需的包。注意,我们需要至少一个GPU才能运行本节实验
。
【注意】本章需要至少一个GPU才能运行。
```
{.python .input}
import mxnet as mx
from mxnet import nd
from time import time
```
## 多
设备
的并行计算
## 多
CPU/GPU
的并行计算
我们
首先定义一个函数,它做10次矩阵乘法
。
我们
先介绍多CPU/GPU的并行计算,例如程序中的计算既发生在CPU,又发生在GPU之上
。
```
{.python .input}
from mxnet import nd
先定义一个函数,令它做10次矩阵乘法。
```
{.python .input}
def run(x):
return [nd.dot(x,
x) for i
in range(10)]
return [nd.dot(x,
x) for _
in range(10)]
```
我们分别计算在CPU和GPU上运行时间
接下来,分别在CPU和GPU上创建NDArray。
```
{.python .input}
from mxnet import gpu
from time import time
x_cpu = nd.random.uniform(shape=(2000, 2000))
x_gpu = nd.random.uniform(shape=(6000, 6000), ctx=gpu(0))
nd.waitall()
x_gpu = nd.random.uniform(shape=(6000, 6000), ctx=
mx.
gpu(0))
```
# 预热阶段。
run(x_cpu)
然后,分别使用它们在CPU和GPU上运行
`run`
函数并打印所需时间。
```
{.python .input}
run(x_cpu) # 预热开始。
run(x_gpu)
nd.waitall()
nd.waitall()
# 预热结束。
start = time()
run(x_cpu)
nd.waitall()
print('
R
un on CPU: %f sec'%(time()-start))
print('
r
un on CPU: %f sec'%(time()-start))
start = time()
run(x_gpu)
nd.waitall()
print('
R
un on GPU: %f sec'%(time()-start))
print('
r
un on GPU: %f sec'%(time()-start))
```
我们去掉
两次
`run`
之间的
`waitall`
,希望系统能自动并行这两个任务:
我们去掉
`run(x_cpu)`
和
`run(x_gpu)`
两个计算任务之间的
`nd.waitall()`
,希望系统能自动并行这两个任务。
```
{.python .input}
start = time()
run(x_cpu)
run(x_gpu)
nd.waitall()
print('
R
un on both CPU and GPU: %f sec'%(time()-start))
print('
r
un on both CPU and GPU: %f sec'%(time()-start))
```
可以看到
两个一起执行时,总时间不是分开执行的总和。这个表示后端系统能有效并行执行它们
。
可以看到
,当两个计算任务一起执行时,执行总时间小于它们分开执行的总和。这表示,MXNet能有效地在多CPU/GPU上自动并行计算
。
## 计算和通讯的并行
## 计算和通讯的并行
计算
在多
设备计算中,我们经常需要在设备之间复制数据。例如下面我们在GPU上计算,然后将结果复制回CPU
。
在多
CPU/GPU计算中,我们经常需要在CPU/GPU之间复制数据,造成数据的通讯。举个例子,在下面例子中,我们在GPU上计算,然后将结果复制回CPU。我们分别打印GPU上计算时间和GPU到CPU的通讯时间
。
```
{.python .input}
from mxnet import cpu
def copy_to_cpu(x):
"""copy data to a device"""
return [y.copyto(cpu()) for y in x]
return [y.copyto(mx.cpu()) for y in x]
start = time()
y = run(x_gpu)
nd.waitall()
print('
Run on GPU: %f sec'%(time()-
start))
print('
run on GPU: %f sec' % (time() -
start))
start = time()
copy_to_cpu(y)
nd.waitall()
print('
Copy to CPU: %f sec'%
(time() - start))
print('
copy to CPU: %f sec' %
(time() - start))
```
同样我们去掉运行和复制之间的
`waitall`
:
我们去掉计算和通讯之间的
`waitall`
函数,打印这两个任务完成的总时间。
```
{.python .input}
start = time()
...
...
@@ -89,16 +90,19 @@ t = time() - start
print('Run on GPU then Copy to CPU: %f sec'%(time() - start))
```
可以看到
总时间小于前面两者之和。这个任务稍微不同于上面,因为运行和复制之间有依赖关系。就是
`y[i]`
必须先计算好才能复制到CPU。但在计算
`y[i]`
的时候系统可以复制
`y[i-1]`
,从而获得总运行时间的减少
。
可以看到
,执行计算和通讯的总时间小于两者分别执行的耗时之和。需要注意的是,这个计算并通讯的任务不同于前面多CPU/GPU的并行计算中的任务。这里的运行和通讯之间有依赖关系:
`y[i]`
必须先计算好才能复制到CPU。所幸的是,在计算
`y[i]`
的时候系统可以复制
`y[i-1]`
,从而减少计算和通讯的总运行时间
。
## 小结
*
MXNet能够自动并行执行没有数据依赖关系的任务从而提升系统性能。
*
MXNet能够通过自动并行计算提升计算性能,例如多CPU/GPU的并行和计算与通讯的并行。
## 练习
*
`run`
里面计算了10次运算,他们也没有依赖关系。看看系统有没有自动并行执行他们
*
试试有更加复杂数据依赖的任务,看看系统能不能得到正确的结果,而且性能有提升吗?
*
本节中定义的
`run`
函数里做了10次运算。它们之间也没有依赖关系。看看MXNet有没有自动并行执行它们。
*
试试包含更加复杂的数据依赖的计算任务。MXNet能不能得到正确结果并提升计算性能?
## 扫码直达[讨论区](https://discuss.gluon.ai/t/topic/1883)
...
...
chapter_gluon-advances/lazy-evaluation.md
浏览文件 @
375a475e
...
...
@@ -52,7 +52,7 @@ print(y)
print('workloads are completed: %f sec' % (time() - start))
```
的确,除非我们需要打印或者保存计算结果,我们基本无需关心目前结果在内存中是否已经计算好了。只要数据是保存在NDArray里并使用MXNet提供的运算
子
,MXNet后端将默认使用惰性计算来获取最高的计算性能。
的确,除非我们需要打印或者保存计算结果,我们基本无需关心目前结果在内存中是否已经计算好了。只要数据是保存在NDArray里并使用MXNet提供的运算
符
,MXNet后端将默认使用惰性计算来获取最高的计算性能。
## 用同步函数让前端等待计算结果
...
...
@@ -231,7 +231,9 @@ print('increased memory: %f MB' % (get_mem() - mem))
## 小结
*
惰性计算有助于提升计算性能。
*
MXNet包括用户直接用来交互的前端和系统用来执行计算的后端。
*
MXNet能够通过惰性计算提升计算性能。
*
我们建议使用每个小批量训练或预测时至少使用一个同步函数,从而避免将过多计算任务同时添加进后端。
...
...
chapter_gluon-basics/use-gpu.md
浏览文件 @
375a475e
...
...
@@ -32,7 +32,7 @@ from mxnet import gluon, nd
import sys
```
MXNet使用
`context`
来指定用来存储和计算的
设备
。默认情况下,MXNet会将数据开在主内存,然后利用CPU来计算。在MXNet中,CPU和GPU可分别由
`mx.cpu()`
和
`mx.gpu()`
来表示。需要注意的是,
`mx.cpu()`
表示所有的物理CPU和内存。这意味着计算上会尽量使用所有的CPU核。但
`mx.gpu()`
只代表一块显卡和相应的显卡内存。如果有多块GPU,我们用
`mx.gpu(i)`
来表示第$i$块GPU($i$从0开始)。
MXNet使用
`context`
来指定用来存储和计算的
CPU/GPU
。默认情况下,MXNet会将数据开在主内存,然后利用CPU来计算。在MXNet中,CPU和GPU可分别由
`mx.cpu()`
和
`mx.gpu()`
来表示。需要注意的是,
`mx.cpu()`
表示所有的物理CPU和内存。这意味着计算上会尽量使用所有的CPU核。但
`mx.gpu()`
只代表一块显卡和相应的显卡内存。如果有多块GPU,我们用
`mx.gpu(i)`
来表示第$i$块GPU($i$从0开始)。
```
{.python .input n=3}
[mx.cpu(), mx.gpu(), mx.gpu(1)]
...
...
@@ -40,7 +40,7 @@ MXNet使用`context`来指定用来存储和计算的设备。默认情况下,
## NDArray的GPU计算
每个NDArray都有一个
`context`
属性来表示它存在哪个
设备
上。默认情况下,NDArray存在CPU上。因此,之前我们每次打印NDArray的时候都会看到
`@cpu(0)`
这个标识。
每个NDArray都有一个
`context`
属性来表示它存在哪个
CPU/GPU
上。默认情况下,NDArray存在CPU上。因此,之前我们每次打印NDArray的时候都会看到
`@cpu(0)`
这个标识。
```
{.python .input n=4}
x = nd.array([1,2,3])
...
...
@@ -49,7 +49,7 @@ print('x: ', x, '\ncontext of x: ', x.context)
### GPU上的存储
我们可以在创建NDArray的时候通过
`ctx`
指定存储
设备
。
我们可以在创建NDArray的时候通过
`ctx`
指定存储
的CPU/GPU
。
```
{.python .input n=5}
a = nd.array([1, 2, 3], ctx=mx.gpu())
...
...
@@ -59,7 +59,7 @@ c = nd.random.uniform(shape=(2, 3), ctx=mx.gpu(1))
print('a: ', a, '\nb: ', b, '\nc: ', c)
```
我们可以通过
`copyto`
和
`as_in_context`
函数在
设备
之间传输数据。
我们可以通过
`copyto`
和
`as_in_context`
函数在
CPU/GPU
之间传输数据。
```
{.python .input n=7}
y = x.copyto(mx.gpu())
...
...
@@ -80,13 +80,13 @@ print('z_target and z share memory? ', z_target is z)
### GPU上的计算
MXNet的计算会在数据的
`context`
上执行。为了使用GPU计算,我们只需要事先将数据放在GPU上面。而计算结果会自动保存在相同的
设备
上。
MXNet的计算会在数据的
`context`
上执行。为了使用GPU计算,我们只需要事先将数据放在GPU上面。而计算结果会自动保存在相同的
GPU
上。
```
{.python .input n=9}
nd.exp(z + 2) * y
```
注意,MXNet要求计算的所有输入数据都在同一个
设备上。这个设计的原因是设备之间的数据交互通常比较耗时。因此,MXNet希望用户确切地指明计算的输入数据都在同一个设备
上。例如,如果将CPU上的
`x`
和GPU上的
`y`
做运算,会出现错误信息。
注意,MXNet要求计算的所有输入数据都在同一个
CPU/GPU上。这个设计的原因是不同CPU/GPU之间的数据交互通常比较耗时。因此,MXNet希望用户确切地指明计算的输入数据都在同一个CPU/GPU
上。例如,如果将CPU上的
`x`
和GPU上的
`y`
做运算,会出现错误信息。
### 其他复制到主内存的操作
...
...
@@ -100,7 +100,7 @@ print(y.sum().asscalar())
## Gluon的GPU计算
同NDArray类似,Gluon的大部分函数可以通过
`ctx`
指定
设备
。下面代码将模型参数初始化在GPU上。
同NDArray类似,Gluon的大部分函数可以通过
`ctx`
指定
CPU/GPU
。下面代码将模型参数初始化在GPU上。
```
{.python .input n=12}
net = gluon.nn.Sequential()
...
...
@@ -123,7 +123,7 @@ net[0].weight.data()
## 小结
*
通过
`context`
,我们可以在不同的
设备
上存储数据和计算。
*
通过
`context`
,我们可以在不同的
CPU/GPU
上存储数据和计算。
## 练习
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录