tensorflow源码解析之framework-resource
目录
- 什么是resource
- 如何使用resource
- 如何管理resource
- 常用resource
- 其它结构
- 关系图
- 涉及的文件
- 迭代记录
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,目前仅需要知道,这是节点中用于实际执行计算的类就好了),这种节点帮助普通节点在资源管理器中查找或创建某种特定类型的资源,如图所示:
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. 关系图
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 文档重构
tensorflow源码解析之framework-resource的更多相关文章
- tensorflow源码解析之framework拾遗
把framework中剩余的内容,按照文件名进行了简单解析.时间原因写的很仓促,算是占个坑,后面有了新的理解再来补充. allocation_description.proto 一个对单次内存分配结果 ...
- tensorflow源码解析系列文章索引
文章索引 framework解析 resource allocator tensor op node kernel graph device function shape_inference 拾遗 c ...
- Tensorflow源码解析1 -- 内核架构和源码结构
1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层.比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android ...
- tensorflow源码解析之common_runtime-executor-上
目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...
- tensorflow源码解析之framework-allocator
目录 什么是allocator 内存分配器的管理 内存分配追踪 其它结构 关系图 涉及的文件 迭代记录 1. 什么是allocator Allocator是所有内存分配器的基类,它定义了内存分配器需要 ...
- tensorflow源码解析之common_runtime-executor-下
目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...
- tensorflow源码解析之distributed_runtime
本篇主要介绍TF的分布式运行时的基本概念.为了对TF的分布式运行机制有一个大致的了解,我们先结合/tensorflow/core/protobuf中的文件给出对TF分布式集群的初步理解,然后介绍/te ...
- tensorflow源码解析之common_runtime拾遗
把common_runtime中剩余的内容,按照文件名排序进行了简单的解析,时间原因写的很仓促,算是占个坑,后续有了新的理解再来补充. allocator_retry 有时候内存分配不可能一次完成,为 ...
- tensorflow源码解析之framework-op
目录 什么是op op_def定义 op注册 op构建与注册辅助结构 op重写 关系图 涉及的文件 迭代记录 1. 什么是op op和kernel是TF框架中最重要的两个概念,如果一定要做一个类比的话 ...
- Tensorflow源码解析2 -- 前后端连接的桥梁 - Session
Session概述 1. Session是TensorFlow前后端连接的桥梁.用户利用session使得client能够与master的执行引擎建立连接,并通过session.run()来触发一次计 ...
随机推荐
- 分布式系统及CAP理论
一.集中式系统 在学习分布式之前,先了解一下与之相对应的集中式系统是什么样的. 集中式系统用一句话概括就是:一个主机带多个终端.终端没有数据处理能力,仅负责数据的录入和输出.而运算.存储等全部在主机上 ...
- java run()方法无法throws 异常
感谢大佬:https://blog.csdn.net/z_ssyy/article/details/95345205 其实原因很简单,run()方法是我们调用start()方法后,jvm再去调用run ...
- nginx实现跨域访问并支持(GET, POST,PUT,DELETE, OPTIONS)
最近有同事提出在使用客户端跨域访问的时候,发现服务器对option请求返回了403,后来查看了网络添加了一段配置,发现option服务返回204了,但是后续的put操作也直接返回了204导致无法使用图 ...
- 各种字体.效果和名字对应图. --- by 王朋
原帖地址:http://www.oschina.net/question/565065_76244
- SpringMVC探秘-请求之路
SpringMVC探秘-请求之路 开始 今天来分析一下SpringMVC的原理,探究SpringMVC如何把请求传递到每个Controller的方法上,从Servlet到Controller,一个请求 ...
- PHP实习生面试准备的建议
你好,是我琉忆.PHP程序员面试笔试图书系列作者. 今天就跟大家聊聊作为一个PHP程序员,每年的跳槽季都应该怎么准备一番. 其实普遍的跳槽季总的就有2个. 分别是新年后的3-4月,还有9-10月份. ...
- 【Azure 应用服务】应用代码需要客户端证书进行验证,部署到App Service后,如何配置让客户端携带证书呢?
问题描述 .NET 6 MVC应用,代码中要求客户端访问时候必须携带正确的证书,如果不携带或者携带错误的证书,都会得到 HTTP ERROR 403 Forbidden 错误 在App Service ...
- InfluxDB 2.x Open Source Time Series Database
1. 说明 目前,大家普遍还在采用 InfluxDB 1.x 的版本,官方2.x的版本已经发布一段时间了, 其主推flux语言且自带前端炫酷图表. 2. 官方网站 https://www.influx ...
- Prometheus之Dockerfile编写、镜像构建、容器启动
目录 从官方镜像启动:prom/prometheus 官方Dockerfile分析 编写自己的Dockerfile 构建镜像: 启动容器: 从官方镜像启动:prom/prometheus 拉取镜像 $ ...
- scanf坑我的那些年
scanf函数作为用户输入指令给计算机的一种输入方法,它的使用有如下几被坑点: scanf用法:#include<stdio.h>;scanf("格式控制符",地址表列 ...