为了账号安全,请及时绑定邮箱和手机立即绑定

深度学习 Caffe 内存管理机制理解

本文是作者原创,如有理解错误,恳请大家指出,如需引用,请注明出处。

Blob内存管理分析

在caffe的分层结构中,Blob充当了内存管理的角色,屏蔽了上层逻辑代码对于数据的申请释放的感知,同时也屏蔽了底层设备对上层逻辑的影响,本文主要分析Blob的管理机制和实际内存申请单元SyncedMemory 的机制。
首先我们看一下Blob和SyncedMemory的关系,类图如下:

https://img1.sycdn.imooc.com//5d5aa9f700010a0c04230163.png

blob_class.jpg

实际上整个Blob的实现就是在SyncedMemory上封装了一层,所以首先需要分析一下SyncedMemory的实现机制。

SyncedMemory的实现机制

SyncedMemory的目的是为了屏蔽上层代码对不同硬件设备的内存分配的感知,同时隐藏了CPU和GPU之间的同步过程。同时,SyncedMemory实现时,采用的是 “lazy”的模式,就是内存的实际申请时机是在第一次使用时进行的。有了大体的了解,下面我们来详细分析一下。
下面是SyncedMemory 提供的一组接口,

名称功能
cpu_data()获取CPU数据指针
gpu_data()获取GPU数据指针

实现的代码如下:

const void* SyncedMemory::cpu_data() {
  to_cpu(); return (const void*)cpu_ptr_;
}const void* SyncedMemory::gpu_data() {#ifdef USE_CUDA
  to_gpu();  return (const void*)gpu_ptr_;#else
  NO_GPU;  return NULL;#endif  // USE_CUDA}

可以看出,每次调用接口时,都会有 to_cpu() 和 to_gpu() 的操作,那么这两个操作是什么作用呢,我们先看下SyncedMemory中的一些关键参数:

名称功能
cpu_ptr_cpu数据指针
gpu_ptr_gpu数据指针
size_当前SyncedMemory需要维护的数据个数
head_当前 SyncedMemory处于的状态

前三个都比较好理解,最后一个比较特殊,它维护的是 SyncedMemory 当前的状态,分为 UNINITIALIZED,HEAD_AT_GPU,HEAD_AT_CPU ,SYNCED 四中状态。现在介绍一下具体的流程,当第一次调用 to_cpu()时, head_处于UNINITIALIZED状态,那么系统会调用 CPU的申请内存的方式去获得内存区域,之后设置 head_ = HEAD_AT_CPU ,如果中间过程没有GPU设备则不会有状态变动,如果中间有代码调用了 to_gpu() ,则会发现 head_处于 HEAD_AT_CPU 状态,此时会调用同步函数,将数据从CPU同步到GPU, 之后如果又回到CPU上,则同样会发现 head_ 处于HEAD_AT_GPU的状态,那么又会调用相应的同步代码,将数据同步回CPU,通过 head_这样一个状态参数屏蔽了GPU和CPU间的申请和切换的不同。

所以上层业务只需要知道当前自己需要的是CPU还是GPU的数据,然后调用不同的接口,就可以完成数据获取的操作。

Blob的实现分析

了解了SyncedMemory的实现,再来看Blob 就较为简单了,它仅仅做了一些上层的管理逻辑,向外界提供了几个关键的接口:

名称功能
cpu_data()获取CPU数据指针,不能改变数据内容
mutable_cpu_data()获取CPU数据指针,可以改变数据内容
gpu_data()获取GPU数据指针,不能改变数据内容
mutable_gpu_data()获取GPU数据指针,可以改变数据内容
Reshape()调整数据的维度信息

前四个就是对 SyncedMemory 的 cpu_data() 和 gpu_data()的封装,只需要确保每次获取数据前都调用相对的 to_cpu 或者 to_gpu就可以了。对于最后一个Reshape函数,主要是为了调整维度信息,同时可能是出于适配多种数据格式的目的,所以提供3个重载函数,如下:

void Blob::Reshape(const int num, const int channels,const int height, const int width);void Blob::Reshape(const BlobShape& shape) ;void Blob::Reshape(const vector<int>& shape);

前两个重载函数仅仅进行了数据格式的转换,然后调用第三个函数,所以 void Blob::Reshape(const vector<int>& shape);才是实际的执行者,这里需要介绍一下Blob里面较为关键的几个参数:

名称功能
data_数据的实际存储位置
shape_data_数据的维度信息存储位置(NCHW)
capacity_当前数据块的大小
count_reshape后的数据块的大小

阅读代码不难发现,cout_中所存储的就是所有维度的的乘积,也就是当前要reshape到的数据大小,整个的reshape 过程如下:

https://img1.sycdn.imooc.com//5d5aa9fa0001af0e02960443.png

reshape.jpg

结束

以上就是我对Blob的一些理解,希望对大家有帮助。



作者:深度Zz
链接:https://www.jianshu.com/p/2e99bb0421c5


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消