梳理caffe代码syncedmem(二)
接着最重要的就是内存分配和Caffe的底层数据的切换(cpu模式和gpu模式),需要用到内存同步模块。这类个类的代码比较少,但是作用是非常明显的。文件对应着syncedmem.hpp,着syncedmem.cpp首先是两个全局的内联函数。如果机器是支持GPU的并且安装了cuda,通过cudaMallocHost分配的host memory将会被pinned,pinned的意思就是内存不会被paged out,我们知道内存里面是由页作为基本的管理单元。分配的内存可以常驻在内存空间中对效率是有帮助的,空间不会被别的进程所抢占。同样如果内存越大,能被分配的Pinned内存自然也越大。还有一点是,对于单一的GPU而言提升并不会太显著,但是对于多个GPU的并行而言可以显著提高稳定性。这里是两个封装过的函数,内部通过cuda来分配主机和释放内存的接口.
- #ifndef CAFFE_SYNCEDMEM_HPP_
- #define CAFFE_SYNCEDMEM_HPP_
- #include <cstdlib>
- #include "caffe/common.hpp"
- #include "caffe/util/math_functions.hpp"
- namespace caffe {
- // Theoretically, CaffeMallocHost and CaffeFreeHost should simply call the
- // cudaMallocHost and cudaFree functions in order to create pinned memory.
- // However, those codes rely on the existence of a cuda GPU (I don't know
- // why that is a must since allocating memory should not be accessing the
- // GPU resource, but it just creates an error as of Cuda 5.0) and will cause
- // problem when running on a machine without GPU. Thus, we simply define
- // these two functions for safety and possible future change if the problem
- // of calling cuda functions disappears in a future version.
- //
- // In practice, although we are creating unpinned memory here, as long as we
- // are constantly accessing them the memory pages almost always stays in
- // the physical memory (assuming we have large enough memory installed), and
- // does not seem to create a memory bottleneck here.
- //分别是分配和释放内存,这里指的是CPU内存。
- inline void CaffeMallocHost(void** ptr, size_t size) {
- *ptr = malloc(size);
- CHECK(*ptr) << "host allocation of size " << size << " failed";
- }
- inline void CaffeFreeHost(void* ptr) {
- free(ptr);
- }
- /**
- * @brief Manages memory allocation and synchronization between the host (CPU)
- * and device (GPU).
- *
- * TODO(dox): more thorough description.
- */
- //功能:Caffe的底层数据的切换(cpu模式和gpu模式),需要用到内存同步模块。
- class SyncedMemory {
- public:
- /*
- 第一个为简单初始化,第二个只是把 size (大小)设置了,并未申请内存。
- 然后是析构函数,主要就是释放数据。own_gpu_data和own_cpu_data这两个成员变量
- 的作用表示是否拥有该数据,也即在cpu或gpu中是否
- 有其他指针指向该数据。
- */
- SyncedMemory()
- : cpu_ptr_(NULL), gpu_ptr_(NULL), size_(0), head_(UNINITIALIZED),
- own_cpu_data_(false) {}
- explicit SyncedMemory(size_t size)
- : cpu_ptr_(NULL), gpu_ptr_(NULL), size_(size), head_(UNINITIALIZED),
- own_cpu_data_(false) {}
- ~SyncedMemory();
- /*
- 分别是获取cpu,gpu中数据的指针,需要说明的一点是,该过程会同步数据。
- 有获取,就有设置,下面两个函数就是设置数据了。这里设置后就不是拥有该数据,
- 即own_cpu_data或own_gpu_data就为false,因为还有data指向该数据。
- 一般来说,只有当同步后才会为true。也即to_cpu()或者to_gpu()后。
- */
- const void* cpu_data();//获取cpu数据,返回void * 指针
- void set_cpu_data(void* data);//用一个void * 指针修改指针,功能:清空CPU的数据
- const void* gpu_data();//获取gpu数据,返回void * 指针
- void* mutable_cpu_data();//获取可以更改cpu数据,返回void * 指针,并改变数据的状态为HEAD_AT_CPU
- void* mutable_gpu_data();//获取可以更改gpu数据,返回void * 指针,并改变数据的状态为HEAD_AT_GPU
- //关于SymceHead,有四种状态,分别是未初始化,数据在 cpu 中,数据在 gpu 中,
- //数据在 cpu和 gpu 中都有
- enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED };//enum枚举值
- /*
- 返回数据状态和大小。
- */
- SyncedHead head() { return head_; }//获得枚举值
- size_t size() { return size_; }//获得数据大小
- private:
- /*
- 功能:把数据放到cpu上
- 1.数据未初始化,则在cpu申请内存(申请为0)。此时状态为HEAD_AT_CPU
- 2.数据本来在gpu,则从gpu拷贝内存到cpu。此时状态为SYNCED
- 3.数据本来在cpu,不做处理
- 4.数据在cpu和gpu都有,不做处理
- */
- void to_cpu();
- /*
- 功能:把数据放到gpu上
- 1.数据未初始化,在gpu申请内存(申请为0)。此时状态为HEAD_AT_GPU
- 2.数据在cpu,从cpu拷贝到gpu。此时状态为SYNCED
- 3.数据在gpu,不做操作。
- 4.数据在cpu和gpu都有,不做操作。
- */
- void to_gpu();
- void* cpu_ptr_;//指向cpu的指针
- void* gpu_ptr_;//指向gpu的指指针
- size_t size_; //大小
- SyncedHead head_; //数据存放的位置,枚举值之一
- bool own_cpu_data_;//是否有cpu数据
- /*DISABLE_COPY_AND_ASSIGN是一个宏,用来把该类的拷贝函数和等号操作符给禁止掉,如果想让你的类
- 不能使用 copy 构造函数和赋值操作符,只要将该类的 copy 构造函数和赋值操作符函数定义为 private
- 即可,并且只是声明,不用实现 .
- */
- DISABLE_COPY_AND_ASSIGN(SyncedMemory);
- }; // class SyncedMemory
- } // namespace caffe
- #endif // CAFFE_SYNCEDMEM_HPP_