OneFlow源码解析:Eager模式下Tensor的存储管理

news2024/12/30 1:43:43

dcde8982a7d8db1204f28e2f26bc8cbb.jpeg

作者|郑建华

不同Tensor类型的存储管理方式

Lazy Tensor 的存储是由 Runtime 和 Actor 等对象管理的。静态图完成编译后,需要多少个对象、多少存储空间都是确定的,Runtime 等在初始化时会分配存储,在退出时回收资源。

Eager 模式下,Global Tensor 可以视为对 Local Tensor 的分布式封装,EagerGlobalTensorImpl 在本地的数据是一个

EagerLocalTensorImpl 对象。可以通过考察 EagerLocalTensorImpl 来理解 eager 模式下 tensor 的存储管理。

参考的示例代码如下:

 
 
import numpy as np
 import oneflow as flow
 
 a = np.random.randn(1, 4)
 flow.tensor(a, device=flow.device("cpu"), dtype=flow.float)

Tensor 存储相关类的关系

EagerLocalTensorImpl 的存储相关的类关系如下。

后续会顺着示例代码的执行过程,看看图中的对象都是在何时、如何构造的,存储被谁持有、如何分配并释放。

15c2bd31384ec829680f6bcaa6efe0f2.png

3 

通过虚拟机指令为 Tensor 分配存储

tensor 的构造函数通过 Python C API 注册为 PyTensorObject_init,由 functional::_legacy_tensor_ctor 

根据签名进行转发。

示例代码对应的是 TensorWithDataFunctor

,调用 MakeLocalTensorFromData 构造 tensor,在这个函数中通过调用 functional::Empty以及 EmptyFunctor分配存储。在 EmptyFunctor 中把相关属性都存到 attrs,然后调用 OpInterpUtil::Dispatch在 vm 指令的执行准备过程中分配存储。

EmptyFunctor 返回的 tensor 是一个只有存储空间、不含数据的对象。数据拷贝在后面由 CopyLocalTensorFromUntypedArray

完成。

3.1 存储相关对象的构造

因为是 eager 模式下的 local tensor,OpInterpUtil::Dispatch 会被转发到 NaiveInterpret执行。对于示例代码,这个函数的输入参数如下:

  • inputs 是一个空数组

  • outputs 只有一个元素、且是空指针

因为 outputs 中的 tensor 指针都是空的,所以需要创建一个 EagerLocalTensorImpl 对象,其 one::TensorStorage 成员变量是空指针。

因为 output_eager_blob_objects 中的元素尚未初始化,会调用 tensor_impl->InitEagerBlobObject 

进行初始化。因为 tensor_storage_ 还是空的,这个过程会执行如下操作:

  • 创建 vm::TensorStorage 对象

  • 创建 EagerBlobObject 对象

  • set_eager_blob_object

    • UpdateTensorStorage

      • 创建 one::TensorStorage 对象

      • 设置 tensor 存储释放的回调函数

上述对象的创建,都只是记录相关信息,还不涉及 tensor 的存储分配。

需要注意的是,注册到 one::TensorStorage 的回调函数被赋值给了成员变量 releaser_hook_,这个函数会通过虚拟机指令释放 tensor。

3.2 在指令执行过程中分配 tensor 存储

分配 tensor 存储的过程如下:

  • vm::Instruction::Compute

  • vm::InstructionPolicy::ComputeIf

  • vm::OpCallInstructionPolicy::Compute

  • OpCallInstructionUtil::Compute

  • 获取内存分配器

  • OpCallInstructionUtil::AllocateOutputBlobsMemory

  • blob_object->TryAllocateBlobBodyMemory

  • allocator->Allocate

在 EagerBlobObject::TryAllocateBlobBodyMemory 中,allocator 分配的存储地址会赋值给 dptr,存储地址 dptr 和 Free 函数一起构造一个智能指针,并赋值给 vm::TensorStorage 的 blob_dptr_ 变量。

通过虚拟机指令释放 Tensor 存储

在前面的 3.1 节提到,EagerLocalTensorImpl 在初始化 EagerBlobObject、创建 one::TensorStorage 的同时,会设置一个释放 tensor 的回调函数,回调函数保存在变量 releaser_hook_ 中

,one::TensorStorage 析构时调用这个回调函数。把这些信息综合整理一下,one::TensorStorage 析构时会执行如下操作:

 
 
vm::InstructionList instruction_list;
 InstructionsBuilder instructions_builder(&instruction_list);
 
 // JUST(Build(&instructions_builder));
 if (eager_blob_object->producer_stream().has_value()) {
   JUST(instructions_builder->ReleaseTensor(eager_blob_object));
 }
 
 JUST(vm::Run(instructions_builder.mut_instruction_list()));

在 InstructionsBuilder::ReleaseTensor 中,如果有其它 stream 最近使用了 eager_blob_object,会通过 SoftSyncStreamBetween 进行同步。通过这种方式解决存储的依赖问题。

一般情况下,通过 tensor 的 producer_stream 释放存储,根据这个对象获取对应的 vm::Stream 对象,并据此构造指令 instruction(包含 eager_blob_object 和 vm_stream),示例代码对应的指令类型是 FastReleaseTensorInstructionPolicy,其 Compute 方法执行具体的存储释放逻辑,过程如下:

  • ReleaseTensorInstructionPolicy::Release()

  • eager_blob_object->DeallocateBlobDataPtr()

  • tensor_storage_->Release()

  • tensor_storage_->_Release()

  • blob_dptr_.reset()

    • 智能指针重置,调用分配存储时指定的 Free 方法

5 

reshape 等场景的存储管理

在 reshape、slice、transpose 等场景中,调用的 EagerLocalTensorImpl 构造函数的参数包括 input 的 tensor_storage,所以这个 tensor 的 tensor_storage_ 变量不是空的,在执行 InitEagerBlobObject 时,只创建 EagerBlobObject以提供 shape、stride等信息;但不会再创建 one::TensorStorage,而是复用 input 的存储。

两个 TensorStorage 类型可以合并吗?

为什么在 one::TensorStorage 析构时、由它保存的回调函数来触发释放 vm::TensorStorage 中的存储呢?

one::TensorStorage 只多了一个 releaser,这两个 Storage 类型是否可以合并呢?

在当前的设计下,这两个类型不能合并。因为 one::TensorStorage::releaser_hook_ 中持有 EagerBlobObject 的智能指针,EagerBlobObject 中也持有 vm::TensorStorage 的智能指针。如果两个 Storage 类型合并为一个,就会出现循环引用、对象无法析构而导致内存泄漏。

所以,vm::TensorStorage 只是单纯的存储,可以在多个 tensor 之间共享。EagerBlobObject 既包括存储、也包括 shape、stride、data_type 等独特的对象信息。而 one::TensorStorage 是为了避免循环引用而引入的、专门负责释放存储的角色。

7 

附录

GDB 断点示例

 
 
break oneflow::one::MakeLocalTensorFromData
 break oneflow::one::NaiveInterpret
 break oneflow::vm::VirtualMachineEngine::DispatchInstruction
 break oneflow::vm::OpCallInstructionUtil::Compute
 break oneflow::vm::OpCallInstructionUtil::AllocateOutputBlobsMemory
 break oneflow::vm::EagerBlobObject::TryAllocateBlobBodyMemory
 break oneflow::vm::ReleaseTensorInstructionPolicy::Release
 break oneflow/core/eager/eager_blob_object.cpp:107

参考资料

  • OneFlow(https://github.com/Oneflow-Inc/oneflow/tree/b51cb72430619f6088e47bbb8b8226f37299573a

  • OneFlow源码解析:Tensor类型体系与Local Tensor

其他人都在看

  • “ChatGPT们”的淘金时代

  • 推演语言模型的大小与计算开销

  • 狂追ChatGPT:开源社区的“平替”热潮

  • 谷歌科学家:ChatGPT秘密武器的演进与局限

  • 比快更快,开源Stable Diffusion刷新作图速度

  • OneEmbedding:单卡训练TB级推荐模型不是梦

  • GLM训练加速:性能最高提升3倍,显存节省1/3

欢迎Star、试用OneFlow: github.com/Oneflow-Inc/oneflow/icon-default.png?t=N3I4http://github.com/Oneflow-Inc/oneflow/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/454882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot--图片验证码kaptcha

本文介绍如何在SpringBoot中整合kaptcha,以及如何配置kaptcha,生成验证码和校验等 文章目录 前言环境搭建项目结构添加依赖 代码实现KaptchaConfigKnife4jConfigdomainServiceutilcontroller 测试生成验证码校验验证码 前言 参考链接: Gith…

设计模式:结构型模式 - 适配器模式

文章目录 1.概述2.结构3.类适配器模式4.对象适配器模式5.应用场景6.JDK源码解析 - Reader 与 InputStream 1.概述 如果去欧洲国家去旅游的话,他们的插座如下图最左边,是欧洲标准。而我们使用的插头如下图最右边的。因此我们的笔记本电脑,手机…

【Android车载系列】第9章 车载通信-Socket实现IPC通信机制(实现仿FdBus效果)

1 FDBus简介 FDBus 基于 Socket (TCP 和 Unix domain) 之上的IPC机制, 采用 Google protobuf 做序列化和反序列化。 FDBus还支持字符串形式的名字作为server地址。通过 name server 自动为 server 分配Unix domain 地址和 TCP 端口号, 实现 client 和server 之间用服务名字寻址…

【C++】哈希的应用:位图和布隆过滤器

目录 1. 位图1.1 位图的概念1.2 位图的结构1.3 位图的实现 2. 布隆过滤器2.1 概念2.2 结构2.3 布隆过滤器的实现 1. 位图 1.1 位图的概念 💭位图(bitset)是一种基于哈希思想设计的数据结构,其功能主要用于判断数据是否已存在。适…

【设计模式】模板方法模式--让你的代码更具灵活性与可扩展性

文章目录 前言模板方法模式的定义核心组成模板方法模式与其他设计模式的区别 代码实现抽象类具体类Client 经典类图spring中的例子 总结 前言 在软件开发中,设计模式是一种经过实践检验的、可复用的解决方案,它们可以帮助我们解决某一特定领域的典型问题…

【2023 · CANN训练营第一季】应用开发深入讲解——第一章 模型转换(1)

学习目标 学习资源 1.模型转换 模型转换基础 在线课程 文档AIPP功能说明 文档 应用开发(初级) 课程目标: 了解AscendCL的使用场景、基本概念、基本开发流程。 学会使用AscendCL接口开发基础推理应用,应用中包含对图片的基本处理…

javaScript常见面试题(二)

一、浅拷贝和深拷贝 浅拷贝:(1)对于基本数据类型,拷贝的是值,修改的时候数据互不影响;(2)对于引用数据类型,拷贝的是在堆内存中存储的内存地址,修改的时候&a…

mapreduce基础: 手写本地wordcount案例

文章目录 一、源代码1. WordCountMapper类2. WordCountReducer类3. WordCountDriver类 二、运行截图 一、源代码 1. WordCountMapper类 package org.example.wordcount;import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apac…

yum库和nfs共享服务

yum库 redhat,centos用的是.rpm的包,用yum解决依赖包关系下载 ubuntu,debian用的是.deb的包,用apt解决依赖包的关系下载 yum软件仓库的提供方式 FTP服务: ftp://… HTTP服务: http://…或者https://… 本地目录: fi…

母婴品牌内容输出怎么做?“四板斧”送你

新媒体时代,信息大爆炸,人们的注意力有限,有噱头和亮点的内容才能博得注意,成为用户关注的焦点。 母婴行业重视品牌效益和产品的质量,毕竟类似“三聚氰胺”的惨剧谁也不希望再发生。母婴产品的质量依赖技术和生产线支…

C语言从入门到精通第9天(循环结构的使用)

循环结构的使用 while语句do-while语句for语句嵌套循环 循环结构可以重复的执行一段代码块,在C语言中提供了三种不同类型的循环结构:for、while和do-while。 while语句 语法: while(表达式){ 语句; } 如果表达式为真则执行结构体…

并发编程学习笔记

为什么学? 只要去做一个技术含量稍微好点的系统,并发包下面的东西是很容易会要用到的 synchronized(Object){ } 主要是用来给对象加锁 synchronized底层原理 首先监视器的计数器是0,然后线程一访问,会将值改为1,…

[Gitops--4] OpenELB

OpenELB OpenELB是一个开源的负载均衡器,功能和metalLB类似 OpenELB主要两种工作模式: Layer2和BGP模式.目前OpenELB的BGP不支持ipv6 OpenELB核心思想就是通过某种方式将特定的VIP的流量引导k8s集群中,然后通过Kube-proxy将流量转发到后面的特定服务. 1. OpenELB介绍 1.1 La…

对于python文件,敲下回车后发生了什么

引言 我们常说 Python 一是门解释型语言,只需要敲下 python code.py就可以运行编写的代码,而无需使用类似于 javac 或者 gcc 进行编译。那么,Python 解释器是真的一行一行读取 Python 源代码而后执行吗? 实际上,Python 在执行程序…

为什么要进行倾斜摄影三维模型的顶层合并?

为什么要进行倾斜摄影三维模型的顶层合并? 1、倾斜摄影三维模型顶层合并的重要性 倾斜摄影三维模型的顶层合并是指将拍摄同一区域的多个倾斜角度的影像进行融合,生成一个连续的、完整的三维地理信息数据。其原因主要有以下几点: &#xff0…

关于倾斜摄影三维模型轻量化数据大小和质量关系分析

关于倾斜摄影三维模型轻量化数据大小和质量关系分析 倾斜摄影三维模型轻量化是一种常用的技术,通过对原始三维模型数据进行压缩和简化,减小其数据大小,从而提高数据传输和展示效率。然而,轻量化过程中可能会对数据质量产生影响。以…

性能测试——安装Loadrunner11.0的详细步骤

一、下载Loadrunner11.0版本 去相关网站下载即可 二、安装 (windows与虚拟机上安装操作大相径庭) 1、将ISO文件导入,打开光驱,运行“setup.exe“ 2、点击安装,部分机器会提示缺少“Microsoft Visual C 2005 SP1运行组…

有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 来源:力扣(LeetCode) 链接:https://leetco…

Java 将json中key值中带有下划线的部分转为驼峰格式

一、背景说明 在开发过程中,有时会遇到第三方厂商提供的接口返回结果不是严格按照驼峰命名,需要将其中带有下划线的字段进行格式化转换为驼峰命名。 如下图中的self_auth、user_id、user_name、creator_name 和 others_auths 等 key 值。 如果是对 JS…

Mysql 45讲和45问笔记(未完待续0203/04/24)

一、mysql 45讲 1)索引的本质讲解 定义解释 所以是帮助Mysql高效获取数据的排好序的数据结构 索引数据结构 ①二叉树 ②红黑树 ③Hash表 ④B-Tree 原理讲解 可以看到右边的数据结构里面,是按照k-v来存数据结构的,key是col2的字段&#xf…