背景

[作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor]

作为一款优秀的异构深度学习算法框架,TensorFlow可以在多种设备上运行算法程序,包括CPU,GPU,Google开发的TPU等。因为TensorFlow的架构特性非常好,可扩展性很强,所以也支持用户自定义补充其他计算设备,比如可以接入FPGA甚至是自定义芯片等。虽然在Google发布的TensorFlow white paper中并没有过多的描述设备管理相关的内容,只是从较高层面上阐述了Device以及Job的命名规则,但是其设备管理模块确实是处于架构中比较核心的地位。本文将从架构层面出发,详细阐述当前TensorFlow源码中关于设备管理的设计思想和相关细节,理解这部分内容不但可以加深对TensorFlow源码的理解,还可以有能力接入一些自定义的设备。本文是TensorFlow设备管理的第一篇文章,为了能让读者更好的切入到TensorFlow源码阅读过程中,先从较为简单地Device的创建和注册机制开始。读者也可以一边对照本文一边对照源码进行阅读和梳理,并欢迎大家提出各种相关的意见和建议。

计算设备(Device)定义

Google在2015年发布的第一版TensorFlow white paper中,从功能角度上阐述了Device的相关内容,我们可以总结出关键的几点如下:

1. 在TensorFlow中的Device有着特殊的命名规则,无论在单机还是分布式任务中,都能依靠命名确定唯一的Device,它是Device的唯一标识符;

2. TensorFlow使用注册机制将实现多种Device的添加管理;

3. 每个Device自己管理Memory的分配和释放。

Device的命名一般使用/job:{job_name}/task:{job_id}/device:{type}:{device_id}的格式,这是为了更好的支持分布式任务。例如/job:worker/task:17/device:gpu:3就表示该Device是ID为17的worker上的ID为GPU设备。至于分布式中的相关概念会在其他文章中详细阐述,在这里我们只需要知道Device的命名可以帮助我们在任务中定位到具体的某个唯一Device即可。

Device的注册机制

TensorFlow有两处涉及到了设备管理,一处存在于TensorFlow的core中,另一部分存在于XLA中。本文只会阐述TensorFlow core中的内容,关于XLA部分的讲解可以参见其他blog。TensorFlow使用工厂来创建各种各样的Device,并且几乎为每一种Device都实现了对应的DeviceFactory。初读代码时可能会被各种Device类名搞混,下面先从TensorFlow中已经有的Device类出发,给出各种Device的类说明。

Device相关类图

TensorFlow对不同种类的Device做了多层级的抽象,下面的类图是从当前TensorFlow源码中梳理出的比较重要的部分。

上图中的每个类(class)都可以在TensorFlow的源码中找到,因为当前TensorFlow的进化过程比较快,代码结构并不处于一个十分稳定的状态,所以上述类图中的类结构关系可能在未来发生一些变化,这一点从注释中也可以看出一些端倪,但是大的架构不会发生变化,所以梳理类结构也是十分有意义的。下面将对每个类的作用进行简单地阐述,读者在理解这些类的作用以及关系后再去阅读源码就会非常清晰了。

1. DeviceAttributes:在TensorFlow源码中并不能直接找到这个类的c++定义,其实它是由protobuf编译出来的。其含义也很好理解,是对特定Device属性的封装,比如Device的type,存储的限制等等;

2. DeviceBase:定义了Device用到的基本方法,比较重要的是GetAllocator和MakeTensorFromProto,前者返回存储器的分配器,后者是从Proto中生成Tensor,该方法必须被重写;

3. Device:这个类比DeviceBase更加具体,新包含了一些用于计算调用的方法,比如Compute函数就会调用某Op的Compute计算;

4. RemoteDevice:这个类会在分布式时使用,在此暂时不进行阐述;

5. SingleThreadedCpuDevice:这是一个仅有单个线程的CPU Device抽象,它和ThreadPoolDevice不同,只被用于in expensive的Op计算,这样做的好处是避免了一些thread初始化工作;

6. ThreadPoolDevice:这就是CPU Device的实现;

7. RenamedDevice:Device的封装类,封装时会再取一个新的Device name;

8. GPUCompatibleCPUDevice:这也是CPU Device的实现,和ThreadPoolDevice不同的是,它更多的是为了和GPU进行交互而存在,从其使用的CudaHostAllocator就可以看出这一点;

9. BaseGPUDevice,GPUDevice:这两个类都和GPU Device的实现有关,其中GPU Device类只是在继承BaseGPUDevice的基础上重写了Allocator,但没有理解这样设计的深层次原因。

DeviceFactory相关类图

上文提到过,TensorFlow中的Device是通过注册机制添加到运行的进程中的。注册机制在开源代码中是十分常见的设计技巧,它涉及到了一种非常经典的设计模式——工厂模式。在定义每个Device时,通过利用C++事先定义好的宏(Macro)将类对象主动注册到工厂中,这样就可以达到在程序启动完毕时,工厂里已经储备有各种各样所需要的内容。在TensorFlow中存在多处使用工厂模式的例子,比如本文阐述的Device管理,以及Session管理等。在其他开源框架中我们也能够看到这一模式,比如Caffe中的Layer也使用的是工厂模式。

从源码中可以看到,TensorFlow在启动时会调用一系列static的函数,这些函数是通过宏(Macro)展开得到的。对于设备管理模块来说,每种Device都由对应的Factory负责管理,而每种DeviceFactory会在程序启动时注册到全局唯一的static device factory表中。下面的类图展示了各种DeviceFactory之间的继承关系。

DeviceFactory的注册机制

在理清了上一小节中Device相关类的继承关系和说明之后,对于上图中各种DeviceFactory之间的继承关系就很好理解了。这里面比较陌生的类是Registrar,这可以看做是一个带有模板的控制类,它只负责一件事——各种DeviceFactory向全局表的注册。在代码层面,注册函数的调用是通过宏实现的,该宏通过传入DeviceFactory的类名称即可触发Regsitrar的调用逻辑,在每个DeviceFactory的C++实现文件后面都会引用此宏。下图形象的展示了注册的过程。

有了上述的DeviceFactory的注册后,就可以在使用时根据使用的Device类型,从对应的DeviceFactory中“获取”想要的Device了。

Device的创建

有了DeviceFactory之后,我们就可以从Factory中拿到各种各样的Device了。真正从Factory中取出Device的过程是在Session创建时进行的,调用的函数是DeviceFactory中的static函数AddDevices。它会遍历全局device factories表中全部的DeviceFactory并取出,然后逐个调用每个具体XXDeviceFactory的CreateDevices函数,将创建的Device放进vector数组中。下面给出一个简化版的时序图。

上述的时序图描述的较为简单,实际上DeviceFactory的static函数调用AddDevices时会先将CPU Device创建出来,如果没有可用的CPU Device,那么程序就会直接报错退出(一般情况下不会发生此类情况)。这是因为TensorFlow需要保证当没有其他Device存在时,至少还有CPU可以完成整体程序的计算和调度运行。创建CPU Device之后,就会去遍历所有DeviceFactory,把所有能够创建的Device全部创建出来放入vector数组中。

总结

本文主要阐述了TensorFlow设备管理模块中的设备创建于注册机制,它是TensorFlow进行设备管理的第一步,也是最简单的部分。想要深入TensorFlow源码的新人可以先从此模块开始阅读,进而熟悉TensorFlow的Coding style。Device的创建和注册过程触发于程序运行的初始阶段,因为创建Device时使用了工厂模式,所以此处涉及到了各种DeviceFactory的定义和注册。在TensorFlow的C++代码中,各种DeviceFactory在实现文件中通过宏主动将自己注册到全局表中,这样做的目的不但减少了大量重复的注册代码,还与Device的创建解耦合,是一个非常经典常见的编码技巧。至于Device的创建是在创建Session时才会触发,该过程十分简单。

TensorFlow中的设备管理——Device的创建与注册机制的更多相关文章

  1. TensorFlow中的并行执行引擎——StreamExecutor框架

    背景 [作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] 在前一篇文章中,我们梳理了TensorFlow中各种异构Device的添加和注 ...

  2. tensorflow中创建多个计算图(Graph)

    tf程序中,系统会自动创建并维护一个默认的计算图,计算图可以理解为神经网络(Neural Network)结构的程序化描述.如果不显式指定所归属的计算图,则所有的tensor和Operation都是在 ...

  3. TensorFlow中的通信机制——Rendezvous(二)gRPC传输

    背景 [作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] 本篇是TensorFlow通信机制系列的第二篇文章,主要梳理使用gRPC网络传 ...

  4. TensorFlow中的Placement启发式算法模块——Placer

    背景 [作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] 受限于单个Device的计算能力和存储大小,许多深度学习模型都有着使用模型分片 ...

  5. tensorflow中slim模块api介绍

    tensorflow中slim模块api介绍 翻译 2017年08月29日 20:13:35   http://blog.csdn.net/guvcolie/article/details/77686 ...

  6. TensorFlow中的显存管理器——BFC Allocator

    背景 作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] 使用GPU训练时,一次训练任务无论是模型参数还是中间结果都需要占用大量显存.为了 ...

  7. [翻译] Tensorflow中name scope和variable scope的区别是什么

    翻译自:https://stackoverflow.com/questions/35919020/whats-the-difference-of-name-scope-and-a-variable-s ...

  8. UE3中Object和Actor的创建与销毁

    创建Object ① 在uc脚本中使用new运算符来创建 /********************************************************************** ...

  9. TensorFlow中的变量和常量

    1.TensorFlow中的变量和常量介绍 TensorFlow中的变量: import tensorflow as tf state = tf.Variable(0,name='counter') ...

随机推荐

  1. HDU 6346 整数规划 (最佳完美匹配)

    整数规划 Time Limit: 5500/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Subm ...

  2. PAT乙级1003

    1003 我要通过! (20 point(s)) “答案正确”是自动判题系统给出的最令人欢喜的回复.本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”, ...

  3. Adnroid开发环境搭建(四步搞定)

    新手博友,多多关照 下面给大家介绍JDK Eclipse AndroidSDK ADT环境搭建,安装教程 第一步.安装JDK: 第二步.安装Eclipse: 第三步.下载并安装AndroidSDK: ...

  4. 11、OpenCV实现图像的灰度变换

    1.灰度变换的基本概念 灰度变换指对图像的单个像素进行操作,主要以对比度和阈值处理为目的.其变换形式如下: s=T(r) 其中,T是灰度变换函数:r是变换前的灰度:s是变换后的像素.图像灰度变换的有以 ...

  5. jQuery-少见获取元素的方式

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  6. 监听HTTP请求

    using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Oracle.DataAccess.Client; using System; usi ...

  7. Java语言中的面向对象特性

    面向对象的基本特征 1.封装性 封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义: ◇ 把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位( ...

  8. 在react中使用less(官方做法)

    概述 在用create-react-app搭建react app的时候,原生并不支持less,只支持css.不过create-react-app官方给了一种方法使用less,我觉得很有用,记录下来,供 ...

  9. 嘿!我用python帮我干这些事

    python 无疑是当下火上天的语言,但是我们又不拿来工作,那么能拿来干啥呢?我是这么干的. 1. 平时工作开发用不上,就当个计算器吧! python # 加减乘除 >>> (3 + ...

  10. python基础-列表(7)

    一.列表格式 列表名 = [列表元素1,列表元素2,列表元素3,… ] 说明: 列表元素之间是有顺序的,也是通过下标表示,第一个元素的小标为0. 列表元素可以不是同种类型,任何类型都行 列表通常当做容 ...