本文主要解析caffe源码文件/src/caffe/SycedMem.cpp,该文件主要实现cpu与gpu的内存同步。
先看SycedMem.hpp中SycedMem的类定义:::
#ifndef CAFFE_SYNCEDMEM_HPP_
#define CAFFE_SYNCEDMEM_HPP_
#include <cstdlib>
#include "caffe/common.hpp"
namespace caffe {
/*下面注释翻译:::
在Cuda可用并且在GPU模式下,用cudaMallocHost可以分配得到固定的内存。
这样分配好的内存不会被例如DMA这种内存存取机制动态占用,
这样分配的内存对于单一的GPU来说不会有太大的用处,
但对于并行训练来说他会更有用,尤其,这样分配的内存能显著提高大型模型在多GPU情形下的稳定性*/
// If CUDA is available and in GPU mode, host memory will be allocated pinned,
// using cudaMallocHost. It avoids dynamic pinning for transfers (DMA).
// The improvement in performance seems negligible in the single GPU case,
// but might be more significant for parallel training. Most importantly,
// it improved stability for large models on many GPUs.
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);//单CPU模式下则通过c的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));//GPU模式下用cuda库函数cudaFreeHost释放内存
return;
}
#endif
free(ptr);//单cpu模式用C库函数释放内存
}
/**
* @brief Manages memory allocation and synchronization between the host (CPU)
* and device (GPU).
*
* TODO(dox): more thorough description.
*/
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();
const void* cpu_data(); /*返回分配的cpu的内存地址:cpu_ptr_*/
void set_cpu_data(void* data); /*cpu_ptr_所指向的内存释放,并且cpu_ptr_指向入参data所指向内存*/
const void* gpu_data(); /*如果GPU模式,返回分配的gpu的内存地址:gpu_ptr_*/
void set_gpu_data(void* data);/*如果GPU模式,gpu_ptr_所指向的内存释放,并且gpu_ptr_指向入参data所指向内存*/
void* mutable_cpu_data();/*返回分配的cpu的内存地址:cpu_ptr_, 置状态为head_ = HEAD_AT_CPU*/
void* mutable_gpu_data();/*如果GPU模式,返回分配的gpu的内存地址:gpu_ptr_, 置状态为head_ = HEAD_AT_GPU*/
enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED }; /*SyncedHead枚举类型,用来设定head_的状态*/
SyncedHead head() { return head_; } /*返回相应的数据内存状态*/
size_t size() { return size_; } /*返回数据内存大小*/
#ifndef CPU_ONLY
void async_gpu_push(const cudaStream_t& stream); /*异步传输数据,将数据从cpu拷贝到gpu*/
#endif
private:
void to_cpu(); /*见.cpp中注释*/
void to_gpu(); /*见.cpp中注释*/
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的设备ID号*/
DISABLE_COPY_AND_ASSIGN(SyncedMemory); /*见common.cpp解析*/
}; // class SyncedMemory
} // namespace caffe
#endif // CAFFE_SYNCEDMEM_HPP_
其实上面的注释已经解释了大部分SycedMem成员变量与成员函数的意义了,这里主要关注函数to_cpu,与to_gpu的是如何实现的::::
#######略##########
/*如果是第一次初始化,就CaffeMallocHost分配CPU内存,
如果数据处在GPU状态,如果是GPU模式就分配CPU内存,把GPU内存数据拷贝到CPU,
如果数据处在CPU状态或者已经同步,则不处理
总之就是将数据同步到CPU*/
inline void SyncedMemory::to_cpu() {
switch (head_) {
case UNINITIALIZED:
CaffeMallocHost(&cpu_ptr_, size_, &cpu_malloc_use_cuda_);
caffe_memset(size_, 0, cpu_ptr_);
head_ = HEAD_AT_CPU;
own_cpu_data_ = true;
break;
case HEAD_AT_GPU:
#ifndef CPU_ONLY
if (cpu_ptr_ == NULL) {
CaffeMallocHost(&cpu_ptr_, size_, &cpu_malloc_use_cuda_);
own_cpu_data_ = true;
}
caffe_gpu_memcpy(size_, gpu_ptr_, cpu_ptr_);
head_ = SYNCED;
#else
NO_GPU;
#endif
break;
case HEAD_AT_CPU:
case SYNCED:
break;
}
}
/*如果是GPU模式下才处理,如果是单cpu模式下则报错
如果数据处在第一次初始化状态,则分配GPU内存并初始化为0
如果数据处在CPU状态,则分配GPU内存将数据从CPU拷贝到GPU
其他情况不处理,
总之数据同步以GPU*/
inline void SyncedMemory::to_gpu() {
#ifndef CPU_ONLY
switch (head_) {
case UNINITIALIZED:
CUDA_CHECK(cudaGetDevice(&gpu_device_));
CUDA_CHECK(cudaMalloc(&gpu_ptr_, size_));
caffe_gpu_memset(size_, 0, gpu_ptr_);
head_ = HEAD_AT_GPU;
own_gpu_data_ = true;
break;
case HEAD_AT_CPU:
if (gpu_ptr_ == NULL) {
CUDA_CHECK(cudaGetDevice(&gpu_device_));
CUDA_CHECK(cudaMalloc(&gpu_ptr_, size_));
own_gpu_data_ = true;
}
caffe_gpu_memcpy(size_, cpu_ptr_, gpu_ptr_);
head_ = SYNCED;
break;
case HEAD_AT_GPU:
case SYNCED:
break;
}
#else
NO_GPU;
#endif
}
#######略##########