本文主要讲解caffe master下面的
src/caffe/syncedmem.cpp 和
include/caffe/syncedmem.hpp
这两主要用于CPU与GPU的内存同步使用而定义的类文件。
文章参考:
https://blog.csdn.net/lanxuecc/article/details/53009130
作者
_苏_的一系列caffe源码解析真的帮忙很大。
首先讲下:
syncedmem.hpp
#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_
上面的注释已经解释了大部分syncedmem成员变量与成员函数的意义了,这里主要关注函数to_cpu,与to_gpu的是如何实现的::::
syncedmem.cpp中
#######略########## /*如果是第一次初始化,就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 } #######略##########