目录

  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. synchronized、ReentrantLock、volatile

    名词解释 synchronized 是Java中的关键字,是一种同步锁,可以修饰代码块,方法,静态的方法,类.synchronized(Object) 不能用String常量.Integer. Lon ...

  2. Redis 常见面试题(2020最新版)

    阶段汇总集合:001期~180期汇总,方便阅读(增加设计模式) 概述 什么是Redis Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可) ...

  3. Java8-Consumer、Supplier、Predicate和Function方法总结

    这几个接口都在 java.util.function 包下的,分别是Consumer(消费型).supplier(供给型).predicate(谓词型).function(功能性): 那么,下面,我们 ...

  4. JFrame实现圆角窗体

    感谢大佬:https://blog.csdn.net/Mr_Pang/article/details/47808299?utm_source=blogxgwz0 注:使用AWTUtilities类跨平 ...

  5. CocoaPods使用专题 by h.l

    cocoaPods安装 CocoaPods安装和使用教程(code4app) cocoapods使用问题解决 cocoapods慢如何解决? CocoaPods停在Analyzing dependen ...

  6. 使用Hot Chocolate和.NET 6构建GraphQL应用(8) —— 实现Mutate添加数据

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在讨论完GraphQL中的查询需求后,这篇文章我们将演示如何实现GraphQL中的数据添加任务. 思路 在G ...

  7. 流程控制( if while )

    目录 流程控制 必知必会 分支结构 if 1.单 if 分支结构 2. if与else连用 3. if, else和 elif if 判断之嵌套 if 练习题 while 循环 while+break ...

  8. Solution -「CF 1025G」Company Acquisitions

    \(\mathcal{Description}\)   Link.   \(n\) 个公司,每个公司可能独立或者附属于另一个公司.初始时,每个公司附属于 \(a_i\)(\(a_i=-1\) 表示该公 ...

  9. Solution -「CF 1480G」Clusterization Counting

    \(\mathcal{Description}\)   Link.   给定一个 \(n\) 阶完全图,边权为 \(1\sim\frac{n(n-1)}2\) 的排列.称一种将点集划分为 \(k\) ...

  10. CentOS 7 部署 KVM 虚拟化

    文章目录 KVM的组件 KVM模块load进内存之后,系统的运行模式 部署KVM 基础配置 判断CPU是否支持硬件虚拟化 检测 kvm 模块是否装载 安装用户端工具 qemu-kvm 启动服务 查看网 ...