Tensorflow后端会自动构建计算图。通过计算图,系统可以知道所有计算的依赖关系,并可以选择将没有依赖关系的多个任务并行执行来获得计算性能的提升。例如“异步计算”一节的第一个例子里依次执行了a = tf.ones((1, 2))和b = tf.ones((1, 2))。这两步计算之间并没有依赖关系,因此系统可以选择并行执行它们。
通常,一个运算符会用到所有CPU或单块GPU上全部的计算资源。例如,dot运算符会用到所有CPU(即使是一台机器上有多个CPU处理器)或单块GPU上所有的线程。如果每个运算符的计算量足够大,只在CPU上或者单块GPU上并行运行多个运算符时,每个运算符的运行只分到CPU或单块GPU上部分计算资源。即使这些计算可以并行,最终计算性能的提升可能也并不明显。本节中探讨的自动并行计算主要关注同时使用CPU和GPU的并行计算,以及计算和通信的并行。
首先导入本节中实验所需的包或模块。注意,需要至少一块GPU才能运行本节实验。
import tensorflow as tf
import time
我们先介绍CPU和GPU的并行计算,例如,程序中的计算既发生在CPU上,又发生在GPU上。先定义run
函数,令它做10次矩阵乘法。
def run(x):
return [tf.matmul(x, x) for _ in range(10)]
接下来,分别在CPU和GPU上创建Tensor
。
with tf.device('/CPU:0'):
x_cpu = tf.random.uniform(shape=(2000, 2000))
with tf.device('/GPU:0'):
x_gpu = tf.random.uniform(shape=(6000, 6000))
然后,分别使用它们在CPU和GPU上运行run
函数并打印运行所需时间。
run(x_cpu)
run(x_gpu)
with Benchmark('Run on CPU.'):
run(x_cpu)
with Benchmark('Then Run on GPU.'):
run(x_gpu)
输出:
Run on CPU. time: 1.2657 sec
Then Run on GPU. time: 0.0005 sec
尝试系统能自动并行这两个任务:
with Benchmark('Run on both CPU and GPU in parallel.'):
run(x_cpu)
run(x_gpu)
输出:
Run on both CPU and GPU in parallel. time: 1.2364 sec
可以看到,当两个计算任务一起执行时,执行总时间小于它们分开执行的总和。这表明,Tensorflow能有效地在CPU和GPU上自动并行计算。
在同时使用CPU和GPU的计算中,经常需要在内存和显存之间复制数据,造成数据的通信。在下面的例子中,我们在GPU上计算,然后将结果复制回CPU使用的内存。我们分别打印GPU上计算时间和显存到内存的通信时间。
def copy_to_cpu(x):
with tf.device('/CPU:0'):
return [y for y in x]
with Benchmark('Run on GPU.'):
y = run(x_gpu)
with Benchmark('Then copy to CPU.'):
copy_to_cpu(y)
输出:
Run on GPU. time: 0.0047 sec
Then copy to CPU. time: 0.0007 sec
打印这两个任务完成的总时间。
with Benchmark('Run and copy in parallel.'):
y = run(x_gpu)
copy_to_cpu(y)
输出:
Run and copy in parallel. time: 0.0024 sec
注:本节与原书有很多不同,原书传送门