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
}