目录

  1. 什么是resource
  2. 如何使用resource
  3. 如何管理resource
  4. 常用resource
  5. 其它结构
  6. 关系图
  7. 涉及的文件
  8. 迭代记录

1. 什么是resource

我们知道,TF的计算是由设备完成的。每个设备包含若干个节点,由这些节点完成实际的计算。有些时候,我们需要在不同的节点之间,共享一些内容,比如,张量值,kv存储表,队列,读取器等等,这些被同设备上的节点共享的内容,就是资源。

所有的资源类都继承自一个基类,ResourceBase,下面看下它的实现:

class ResourceBase : public core::RefCounted {
public:
virtual string DebugString() = 0;
virtual int64 MemoryUsed() const {return 0;};
};

它继承自core::RefCounted,也就是说,资源本身拥有引用计数的功能。这是为了方便对资源的使用进行监控,在资源无用后能够及时将它释放掉。

2. 如何使用resource

普通节点是无法直接使用共享资源的,必须通过一种包含了ResourceOpKernel的特殊节点(关于OpKernel的详细定义,请参见Kernel,目前仅需要知道,这是节点中用于实际执行计算的类就好了),这种节点帮助普通节点在资源管理器中查找或创建某种特定类型的资源,如图所示:

graph LR
A(OpKernel) -->|派生| B(ResourceOpKernel)
B(ResourceOpKernel) -->|创建或查找| C(Resource)
A(OpKernel) -->|派生| D(NormalOpKernel)
D(NormalOpKernel) -->|使用资源| B(ResourceOpKernel)

ResourceOpKernel类的定义如下(仅保留接口):

template <typename T>
class ResourceOpKernel : public OpKernel {
public:
//...
void Compute(OpKernelContext* context) override LOCKS_EXCLUDED(mu_);
protected:
mutex mu_;
ContainerInfo cinfo_ GUARDED_BY(mu_);//包含了对资源的要求
T* resource_ GUARDED_BY(mu_) = nullptr;
private:
virtual Status CreateResource(T** resource) EXECLUSIVE_LOCKS_REQUIRED(mu_) = 0;//返回一个T派生类的资源对象,这个对象归当前的ResourceOpKernel对象所有
virtual Status VerifyResource(T* resource);//校验resource是否是当前对象需要的类型
PersistentTensor handle_ GUARDED_BY(mu_);
}

其中的Compute函数,在首次调用时,会根据cinfo_中包含的对资源的要求,从资源管理器中查找,或者创建一个新的资源。

这里牵扯到两个新的概念,一个是ContainerInfo,一个是资源管理器。我们将在下文中讨论。

3. 如何管理resource

最简单的,可以在设备内维护一个资源名称到资源实体的映射,但这种管理方式过于粗糙,TF使用容器的概念实现了对资源的分组,下面看一下对容器的定义:

typedef std::pair<uint64,string> Key;
typedef std::unordered_map<Key,ResourceBase*,KeyHash,KeyEqual> Container;

其中,Key是对资源的描述,uint64代表资源类型的哈希值,string代表了资源的名称,而Container本质上就是一个资源描述到资源指针的映射。

为了对同一个设备上的资源提供统一的管理入口,TF定义了ResourceMgr类,其私有数据成员如下:

class ResourceMgr {
private:
//当前设备上的默认容器名称,如果查找时没有指定容器,则在默认容器中查找
const string default_container_;
mutable mutext mu_;
//容器名称到容器指针的映射
std::unordered_map<string,Container*> containers_ GUARDED_BY(mu_);
//资源类型的哈希值到资源类型名称的映射
std::unordered_map<uint64,string> debug_type_names GUARDED_BY(mu_);
};

资源管理类的公共接口如下:

class ResourceMgr {
public:
//在container容器中创建一个名为name的资源
Status Create(const string& container, const string& name, T* resource);
//在container中查找一个名为name的资源
Status Lookup(const string& container, const string& name, T** resource) const;
//如果container中包含名为name的资源,填充到*resource中,否则,使用creater()创建一个资源
Status LookupOrCreate(const string& container, const string& name, T** resource, std::function<Status(T**)> creater);
//删除container中的名为name的资源
Status Delete(const string& container, const string& name);
//删除句柄handle指向的资源
Status Delete(const ResourceHandle& handle);
//删除container中的所有资源,并删除该container
Status Cleanup(const string& container);
//删除所有容器中的所有资源
void Clear();
};

注释很清晰,这里就不再赘述了。

刚才提到,为了让ResourceOpKernel类的对象能够找到对应的资源,它在内部包含了一个ContainerInfo的类,它的作用是,帮助一个有状态的OpKernel决定,它需要通过哪个container/shared_name组合来访问对应的资源,类的定义如下:

class ContainerInfo {
public:
Status Init(ResourceMgr* rmgr, const NodeDef& ndef, bool use_node_name_as_default);
private:
ResourceMgr* rmgr_ = nullptr;
string container_;
string name_;
bool resource_is_private_to_kernel_ = false;
};

其中,这个决策过程如下:

  • 如果这个NodeDef的属性中,包含了"container",就使用这个容器名称,否则使用rmgr的默认容器;
  • 如果这个NodeDef的属性中,包含了"shared_name",那么就以此作为资源的名称,否则,如果"use_node_name_as_default"为真,则使用节点名称作为资源名称,如果为假,则生成一个唯一标识作为资源名;

可见,ContainerInfo本质上提供的是,如何帮助节点找到资源的位置,而不是资源位置的静态描述。为了方便指向资源,TF提出了资源句柄的概念,它的protobuf定义如下:

message ResourceHandleProto {
//资源所在设备
string device = 1;
//资源所在容器
string container = 2;
//资源名称
string name = 3;
//资源类型的哈希值
uint64 hash_code = 4;
//资源类型名称,仅调试用
string maybe_type_name = 5;
}

资源句柄提供了对于资源位置和属性的详细描述,通过句柄我们可以唯一定位到一个资源。由于这个资源句柄是可序列化的,因此我们可以方便的传递它。另外为了避免使kernel依赖于proto,TF还设计了一个与ResourceHandleProto拥有相同功能的类,ResourceHandle,详见源代码。

4. 常用resource

本篇的开头提到了,常用的资源包括张量值,kv存储表,队列,读取器等等,除了张量值之外,TF为后三者提供了应用的接口,如下:

class LookupInterface : public ResourceBase;
class QueueInterface : public ResourceBase;
class ReaderInterface : public ResourceBase;

其中

  • kv存储器接口LookupInterface提供了一个批量查找和插入kv存储器的接口,方便不同的计算节点之间共享kv数据;
  • 队列接口QueueInterface提供了入队、出队等接口,方便不同节点之间共享队列,这对于计算图的异步执行是非常重要的,读取器接口内部也用到了队列接口;
  • 读取器接口ReaderInterface是所有TF支持的文件读取器的统一接口。TF可以支持从不同格式的文件中读取数据,每一个文件读取器都是一种资源,都需要继承自ReaderInterface接口;

其中,关于读取器接口,其实真正的读取器资源,并非直接继承自读取器接口,而是继承自它的一个派生类,ReaderBase,这个类为读取器接口的每一个API提供了一个默认的实现,它的派生类只要重写这些默认实现,就能完成对不同文件格式的读取。

同时,为了方便获取读取器资源,TF还为ResourceOpKernel生成了一个专用于获取读取器接口的派生类ReaderOpKernel:

class ReaderOpKernel : public ResourceOpKernel<ReaderInterface>;

这个类用于查找或者创造读取器资源,而这些读取器资源需要派生自ReaderBase类。

5. 其它结构

为了方便对资源进行操作,TF除了设计ResourceOpKernel这个核之外,还设计了两种核,IsResourceInitialized和ResourceHandleOp,前者用于检查某个资源是否已经初始化,后者用于为某个资源生成资源句柄。具体定义可以查看源代码。

6. 关系图

graph TB
A(core::RefCounted)-->|派生|B(ResourceBase)
B(ResourceBase)-->|派生|C(LookupInterface)
B(ResourceBase)-->|派生|D(QueueInterface)
B(ResourceBase)-->|派生|E(ReaderInterface)
E(ReaderInterface)-->|派生|F(ReaderBase)
F(ReaderBase)-->|派生|G(具体阅读器资源)
H(OpKernel)-->|派生|I(ResourceOpKernel)
I(ResourceOpKernel)-->|派生|J(ReaderOpKernel)
J(ReaderOpKernel)-.查找或创建.->G(具体阅读器资源)
H(OpKernel)-->|派生|K(IsResourceInitialized)
H(OpKernel)-->|派生|L(ResourceHandleOp)
L(ResourceHandleOp)-.生成.->M(ResourceHandle)
M(ResourceHandle)-.指向.->G(具体阅读器资源)
K(IsResourceInitialized)-.判断是否初始化.->G(具体阅读器资源)
I(ResourceOpKernel)-.拥有.->N(ContainerInfo)
N(ContainerInfo)-.定位.->G(具体阅读器资源)

7. 涉及的文件

  • resource_handle
  • resource_mgr
  • resource_op_kernel
  • lookup_interface
  • queue_interface
  • reader_interface
  • reader_base
  • reader_op_kernel

8. 迭代记录

  • v1.0 2018-08-25 文档创建
  • v2.0 2018-09-08 文档重构

github地址

tensorflow源码解析之framework-resource的更多相关文章

  1. tensorflow源码解析之framework拾遗

    把framework中剩余的内容,按照文件名进行了简单解析.时间原因写的很仓促,算是占个坑,后面有了新的理解再来补充. allocation_description.proto 一个对单次内存分配结果 ...

  2. tensorflow源码解析系列文章索引

    文章索引 framework解析 resource allocator tensor op node kernel graph device function shape_inference 拾遗 c ...

  3. Tensorflow源码解析1 -- 内核架构和源码结构

    1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层.比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android ...

  4. tensorflow源码解析之common_runtime-executor-上

    目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...

  5. tensorflow源码解析之framework-allocator

    目录 什么是allocator 内存分配器的管理 内存分配追踪 其它结构 关系图 涉及的文件 迭代记录 1. 什么是allocator Allocator是所有内存分配器的基类,它定义了内存分配器需要 ...

  6. tensorflow源码解析之common_runtime-executor-下

    目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...

  7. tensorflow源码解析之distributed_runtime

    本篇主要介绍TF的分布式运行时的基本概念.为了对TF的分布式运行机制有一个大致的了解,我们先结合/tensorflow/core/protobuf中的文件给出对TF分布式集群的初步理解,然后介绍/te ...

  8. tensorflow源码解析之common_runtime拾遗

    把common_runtime中剩余的内容,按照文件名排序进行了简单的解析,时间原因写的很仓促,算是占个坑,后续有了新的理解再来补充. allocator_retry 有时候内存分配不可能一次完成,为 ...

  9. tensorflow源码解析之framework-op

    目录 什么是op op_def定义 op注册 op构建与注册辅助结构 op重写 关系图 涉及的文件 迭代记录 1. 什么是op op和kernel是TF框架中最重要的两个概念,如果一定要做一个类比的话 ...

  10. Tensorflow源码解析2 -- 前后端连接的桥梁 - Session

    Session概述 1. Session是TensorFlow前后端连接的桥梁.用户利用session使得client能够与master的执行引擎建立连接,并通过session.run()来触发一次计 ...

随机推荐

  1. Android返回键

    感谢大佬:https://www.cnblogs.com/qiluboy/p/5308310.html Android中back键和home键的区别: back键 Android的程序无需刻意的去退出 ...

  2. JMeter压力测试简单使用

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11915535.html JMeter压力测试简单使用: 我们可以使用JMeter来测试一下自己 ...

  3. json中传递数组和list

    json的数据类型:List,数组,数字,字符串,逻辑值,对象,null 1.如果json传递的是数组,格式: { "name":"网站", "num ...

  4. 转载_认识C语言的32个关键字

    简单介绍: 1 auto : 声明自动变量 2 short :声明短整型变量或函数 3 int: 声明整型变量或函数 4 long :声明长整型变量或函数 5 float:声明浮点型变量或函数 6 d ...

  5. MAC上安装HEAAN库

    介绍 HEAN是一个软件库,它实现支持定点运算的同态加密(HE),此库支持有理数之间的近似运算.近似误差取决于某些参数,与浮点运算误差几乎相同.该库中的方案发表在"近似数算术的同态加密&qu ...

  6. Linux vi 命令 – 文本编辑器

    vi命令是linux系统字符界面下的最常用的文本编辑器. vi编辑器是所有linux的标准编辑器,用于编辑任何ASCⅡ文本,对于编辑源程序尤其有用.iv编辑器功能非常强大,可以对文本进行创建,查找,替 ...

  7. Diary / Solution Set -「WC 2022」线上冬眠做噩梦

      大概只有比较有意思又不过分超出能力范围的题叭.   可是兔子的"能力范围" \(=\varnothing\) qwq. 「CF 1267G」Game Relics   任意一个 ...

  8. MyBatis功能点二:从责任链设计模式的角度理解插件实现技术

    MyBatis允许对其四大组件(Executor,StatementHandler,ParameterHandler, ResultSetHandler)进行增强处理.在创建四大组件对象的时候 1.每 ...

  9. vue中按需引入Element-ui

    安装 1.安装element-ui:npm i element-ui -S. 2.安装babel-plugin-component:npm install babel-plugin-component ...

  10. PhpStudy集成环境安装教程

    地址:https://www.xp.cn/ 下载软件,直接安装即可.注意路径不能带有空格.安装完成,启动后如下图: 1.数据库的小白操作:先启动服务器Apache和数据库MySQL,如箭头所示 2.可 ...