【caffe源代码的梳理之三】syncedmem文件

1、syncedmem文件功能介绍

caffe框架在运行神经网络进行数据的计算时,会涉及到内存分配和Caffe的底层数据的切换(cpu模式和gpu模式),需要用到内存同步模块。syncedmem文件主要实现数据(包括data和diff)在CPU和GPU(GPU如果有)内存的同步。

1.1、全局的内联函数

文件定义和实现了两个全局的内联函数,分别是CPU模式和GPU模式下(如果有GPU)内存的分配。
(1)、CaffeMallocHost:CPU模式下,通过调用C语言的malloc函数分配内存;GPU模式下,调用cudaMallocHost()函数实现内存的分配。
(2)、CaffeFreeHost:CPU模式下,通过调用C语言的free函数释放内存;GPU模式下,调用cudaFreeHost()函数实现内存的释放;

1.2、SyncedMemory类

该类主要对CPU数据和GPU数据进行的同步、读和写的操作。

2、syncedmem文件分析

位置

include/caffe/syncedmem.hpp
src/caffe/syncedmem.hpp

2.1、 文件依赖

include/caffe/common.hpp

2.2、主要的成员函数

void check_device();        //检查
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_;         //是否通过SyncedMemory类分配了CPU内存
bool cpu_malloc_use_cuda_;  //
bool own_gpu_data_;         //是否通过SyncedMemory类分配了GPU内存
int device_;                //设备的编号

2.3、主要的功能和成员函数

2.3.1、CaffeMallocHost()函数

CaffeMallocHost()函数主要是实现内存的申请和分配。当只存在CPU的时候,通过C++的malloc函数分配内存,当GPU存在的时候,调用cudaMallocHost()函数实现内存的分配。

inline void CaffeMallocHost(void** ptr, size_t size) {  
#ifndef CPU_ONLY  
  if (Caffe::mode() == Caffe::GPU) {  
    CUDA_CHECK(cudaMallocHost(ptr, size));  
    return;  
  }  
#endif  
  *ptr = malloc(size);  
  CHECK(*ptr) << "host allocation of size " << size << " failed";  
} 

2.3.2、CaffeFreeHost()函数

CaffeFreeHost()函数主要是实现内存的申请和分配。当只存在CPU的时候,通过C++的free函数分配内存,当GPU存在的时候,调用cudaFreeHost()函数实现内存的分配。

inline void CaffeFreeHost(void* ptr) {  
#ifndef CPU_ONLY  
  if (Caffe::mode() == Caffe::GPU) {  
    CUDA_CHECK(cudaFreeHost(ptr));  
    return;  
  }  
#endif  
  free(ptr);  
}  

2.3.3、构造函数

// 默认构造函数,简单初始化,数据状态置为UNINITIALIZED  
SyncedMemory(): 
cpu_ptr_(NULL), gpu_ptr_(NULL), size_(0),head_(UNINITIALIZED),  own_cpu_data_(false), own_gpu_data_(false), gpu_device_(-1) {} 

// 带size参数的显示构造函数,并未分配内存,数据状态置为UNINITIALIZED  
explicit SyncedMemory(size_t size): 
cpu_ptr_(NULL), gpu_ptr_(NULL), size_(size), head_(UNINITIALIZED),own_cpu_data_(false), own_gpu_data_(false), gpu_device_(-1) {}  

2.3.4、CPU或GPU指针对数据的读写操作

//获取CPU数据指针,数据不可更改,内部会调用to_cpu函数,在CPU模式下,数据状态为HEAD_AT_CPU,在GPU模式下,数据状态置为SYNCED  
const void* cpu_data();  

//调用CaffeFreeHost释放内存,如果own_cpu_data_为非空,则调用CaffeFreeHost释放内存,并修改CPU数据指针使其指向data,并置own_cpu_data_为false,数据状态置HEAD_AT_CPU  
void set_cpu_data(void* data);  

// 获取GPU数据指针,数据不可更改,在GPU模式下,数据状态为HEAD_AT_GPU,在CPU模式下,数据状态置为SYNCED  
const void* gpu_data();  

// 在GPU模式下,内部会调用to_gpu函数,如果own_gpu_data_为非空,调用cudaFree释放显存,并修改GPU数据指针使其指向data,并置own_gpu_data_为false,在GPU模式下,数据状态置为HEAD_AT_GPU  
void set_gpu_data(void* data);  

// 获取CPU数据指针,数据可更改,内部会调用to_cpu函数,数据状态置为HEAD_AT_CPU  
void* mutable_cpu_data();  

// 获取GPU数据指针,数据可更改,在GPU模式下,内部会调用to_gpu函数,数据状态置为HEAD_AT_GPU  
void* mutable_gpu_data();  

2.3.5、数据的同步操作

只有在存在GPU的时候,CPU和GPU才会有数据的同步操作

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

//具体实现
#ifndef CPU_ONLY
void SyncedMemory::async_gpu_push(const cudaStream_t& stream) {
  check_device();
  CHECK(head_ == HEAD_AT_CPU);
  if (gpu_ptr_ == NULL) {
    CUDA_CHECK(cudaMalloc(&gpu_ptr_, size_));
    own_gpu_data_ = true;
  }
  const cudaMemcpyKind put = cudaMemcpyHostToDevice;
  CUDA_CHECK(cudaMemcpyAsync(gpu_ptr_, cpu_ptr_, size_, put, stream));
  // Assume caller will synchronize on the stream before use
  head_ = SYNCED;
}
#endif

2.3.6、设备查询函数

查询GPU的信息

void check_device();

//具体实现
void SyncedMemory::check_device() {
#ifndef CPU_ONLY
#ifdef DEBUG
  int device;
  cudaGetDevice(&device);
  CHECK(device == device_);
  if (gpu_ptr_ && own_gpu_data_) {
    cudaPointerAttributes attributes;
    CUDA_CHECK(cudaPointerGetAttributes(&attributes, gpu_ptr_));
    CHECK(attributes.device == device_);
  }
#endif
#endif
}

猜你喜欢

转载自blog.csdn.net/u013108511/article/details/79489750