内容简介:之前在简书的文章,搬迁过来 ^-^本文是作者原创,如有理解错误,恳请大家指出,如需引用,请注明出处。#Blob内存管理分析
之前在简书的文章,搬迁过来 ^-^
本文是作者原创,如有理解错误,恳请大家指出,如需引用,请注明出处。
#Blob内存管理分析
在caffe的分层结构中,Blob充当了内存管理的角色,屏蔽了上层逻辑代码对于数据的申请释放的感知,同时也屏蔽了底层设备对上层逻辑的影响,本文主要分析Blob的管理机制和实际内存申请单元SyncedMemory 的机制。 首先我们看一下Blob和SyncedMemory的关系,类图如下:
实际上整个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 过程如下:
##结束 以上就是我对Blob的一些理解,希望对大家有帮助。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java Servlet & JSP Cookbook
Bruce W. Perry / O'Reilly Media / 2003-12-1 / USD 49.99
With literally hundreds of examples and thousands of lines of code, the Java Servlet and JSP Cookbook yields tips and techniques that any Java web developer who uses JavaServer Pages or servlets will ......一起来看看 《Java Servlet & JSP Cookbook》 这本书的介绍吧!