Caffe源码解析2:SyncedMem

参考自:http://home.cnblogs.com/louyihang-loves-baiyan/

根据前面一章对Blob的分析,我们看到blob.hpp中封装了#include "caffe/syncedmem.hpp",看到SyncedMem就知道,是在做内存同步的操作。这类个类的代码比较少,但是作用是非常明显的。文件对应着syncedmem.hpp和syncedmem.cpp

首先是两个全局的内联函数。如果机器是支持GPU的并且安装了cuda,通过cudaMallocHost分配的host memory将会被pinned,这里我谷歌了一下,pinned的意思就是内存不会被paged out,我们知道内存里面是由页作为基本的管理单元。分配的内存可以常驻在内存空间中对效率是有帮助的,空间不会被别的进程所抢占。同样如果内存越大,能被分配的Pinned内存自然也越大。还有一点是,对于单一的GPU而言提升并不会太显著,但是对于多个GPU的并行而言可以显著提高稳定性。

这里是两个封装过的函数,内部通过cuda来分配主机和释放内存的接口

inline void CaffeMallocHost(void** ptr, size_t size, bool* use_cuda) {
#ifndef CPU_ONLY
  if (Caffe::mode() == Caffe::GPU) {
    CUDA_CHECK(cudaMallocHost(ptr, size));// GPU模式下cuda分配内存
    *use_cuda = true;
    return;
  }
#endif
  *ptr = malloc(size);//如果没有cuda则通过cpu的malloc函数分配
  *use_cuda = false;
  CHECK(*ptr) << "host allocation of size " << size << " failed";
}

inline void CaffeFreeHost(void* ptr, bool use_cuda) {
#ifndef CPU_ONLY
  if (use_cuda) {
    CUDA_CHECK(cudaFreeHost(ptr));//cuda的主机内存释放操作
    return;
  }
#endif
  free(ptr);//cpu的释放操作
}

SyncedMemory类,首先是构造函数和析构函数

class SyncedMemory {
 public:
  SyncedMemory() //参数构造函数,负责初始化
      : cpu_ptr_(NULL), gpu_ptr_(NULL), size_(0), head_(UNINITIALIZED),
        own_cpu_data_(false), cpu_malloc_use_cuda_(false), own_gpu_data_(false),
        gpu_device_(-1) {}
  explicit SyncedMemory(size_t size)//带explicit关键字的,单个参数构造函数,explicit禁止单参数构造函数的隐式转换
      : cpu_ptr_(NULL), gpu_ptr_(NULL), size_(size), head_(UNINITIALIZED),
        own_cpu_data_(false), cpu_malloc_use_cuda_(false), own_gpu_data_(false),
        gpu_device_(-1) {}
  ~SyncedMemory();//其在析构时调用的也是CaffeFreeHost

这几个函数分别是

  const void* cpu_data();
  void set_cpu_data(void* data);
  const void* gpu_data();
  void set_gpu_data(void* data);

cpu_data()主要是获得cpu上data的地址,set_cpu_data是将cpu的data指针指向一个新的区域由data指针传入,并且将原来申请的内存释放。下面两个同理,分别是获得gpu数据地址和set gpu数据地址。

  void* mutable_cpu_data();
  void* mutable_gpu_data();
  enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED }; //状态机变量,表示四种状态:未初始化,CPU数据有效,GPU数据有效,已同步
  SyncedHead head() { return head_; }
  size_t size() { return size_; }

前两个分别是返回cpu和gpu上的data指针,并且置状态为head_ = HEAD_AT_CPU和相应的gpu版本。SyncedHead主要是个枚举类型,用来设定head_的状态,head()函数即返回相应的数据状态,而size()函数返回数据大小

#ifndef CPU_ONLY
  void async_gpu_push(const cudaStream_t& stream);
#endif

这是一个cuda拷贝的异步传输函数,从数据从cpu拷贝到gpu,异步传输是已经假定caller会在使用之前做同步操作。

 private:
  void to_cpu();  //数据同步至CPU
  void to_gpu();  //数据同步至GPU
  void* cpu_ptr_; //位于CPU的数据指针
  void* gpu_ptr_; //位于GPU的数据指针
  size_t size_;   //存储空间的大小
  SyncedHead head_;  //状态机变量
  bool own_cpu_data_;   //标志是否拥有CPU数据所有权(否,即从别的数据共享)
  bool cpu_malloc_use_cuda_;  //
  bool own_gpu_data_;   //标志是否拥有GPU数据所有权
  int gpu_device_;      //GPU设备号

  DISABLE_COPY_AND_ASSIGN(SyncedMemory);//禁止该类的拷贝与赋值
};  // class SyncedMemory

其实这里的东西也不多了,to_cpu(),to_gpu()这个看名字就知道了,需要注意的是,如果head 是未被初始化的状态,那么首先需要先分配内存,这个根据cpu和gpu视情况而定,之后再将数据从cpu或者gpu拷贝到另一处。之后函数会重新标记Head的状态,数据是否在cpu或者在gpu中,cpu这里是简称,其实是主机。
cpu_ptr和gpu_ptr分别是在cpu和gpu中的数据指针,size_这就不再说了,head_之前也以及提到过了,后面几个都是相应的标记位,以及gpu的ID号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值