一、SyncedMemory的作用简介
SyncedMemory类主要负责在GPU或者CPU上分配内存以及保持数据的同步作用。
SyncedMemory类主要应用在BLOB类中,我们可以在BLOB类中看出一些使用方法
比如:
- // blob中的reshape 的具体实现
- template <typename Dtype>
- void Blob<Dtype>::Reshape(const vector<int>& shape) {
- CHECK_LE(shape.size(), kMaxBlobAxes); //是否小于规定的最大BLOB的维度(35维)
- count_ = 1;
- shape_.resize(shape.size());// 首先将大小设置为vector<int> shape_; 即新的形状数据的大小
- if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
- shape_data_.reset(new SyncedMemory(shape.size() * sizeof(int)));// shared_ptr<SyncedMemory> shape_data_;
- }
- int* shape_data = static_cast<int*>(shape_data_->mutable_cpu_data());
- for (int i = 0; i < shape.size(); ++i) {
- // 检查形状数据是否合法
- CHECK_GE(shape[i], 0);
- CHECK_LE(shape[i], INT_MAX / count_) << "blob size exceeds INT_MAX";
- // 计算数据个数
- count_ *= shape[i];
- // 复制shape到新的和旧的形状数据
- shape_[i] = shape[i];
- shape_data[i] = shape[i];
- }
- // 判断是否大于存储的容量
- if (count_ > capacity_) {
- capacity_ = count_;
- // 重新分配内存
- data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
- diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
- }
- }
- // 注意这里的 gpu_data和mutable_gpu_data的区别是
- // mutable_gpu_data是设置了head_ 的而
- // gpu_data是不设置head_
- // 但是我并不明白为啥要有个mutable
- // 难道是因为设置了head_就可以实现互斥了?
- // 可能的解释是设置了head_可以保护gpu或者内存中的数据不被销毁
- template <typename Dtype>
- const int* Blob<Dtype>::gpu_shape() const {
- CHECK(shape_data_);
- // shared_ptr<SyncedMemory> shape_data_;
- // 因此也分gpu_data和cpu_data
- return (const int*)shape_data_->gpu_data();
- }
另一个blob中使用的例子:
- // Update是计算data=-1 * diff + data
- template <typename Dtype>
- void Blob<Dtype>::Update() {
- // We will perform update based on where the data is located.
- switch (data_->head()) {
- case SyncedMemory::HEAD_AT_CPU:
- // perform computation on CPU
- // axpby即alpha * x plus beta *y 这个含义,blas的函数命名真是见名知意
- // template <> void caffe_axpy<float>(const int N, const float alpha, const float* X, float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }
- // caffe_axpy计算的是Y=alpha * X + Y ,其中alpha=-1了这里
- // 存储的时候用到了mutable_cpu_data,防止其他线程访问
- caffe_axpy<Dtype>(count_, Dtype(-1),
- static_cast<const Dtype*>(diff_->cpu_data()),
- static_cast<Dtype*>(data_->mutable_cpu_data()));
- break;
- case SyncedMemory::HEAD_AT_GPU:
- case SyncedMemory::SYNCED:
- #ifndef CPU_ONLY
- // perform computation on GPU
- // Y=alpha * X + Y ,其中alpha=-1了这里
- caffe_gpu_axpy<Dtype>(count_, Dtype(-1),
- static_cast<const Dtype*>(diff_->gpu_data()),
- static_cast<Dtype*>(data_->mutable_gpu_data()));
- #else
- NO_GPU;
- #endif
- break;
- default:
- LOG(FATAL) << "Syncedmem not initialized.";
- }
- }
二、SyncedMemory类的详细介绍
1)构造函数
- // 构造函数
- SyncedMemory()
- // 一开始的时候就是未初始化
- : cpu_ptr_(NULL), gpu_ptr_(NULL), size_(0), head_(UNINITIALIZED),
- own_cpu_data_(false), own_gpu_data_(false), gpu_device_(-1) {}
- 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) {}
- // 析构函数
- ~SyncedMemory();
2)成员变量
- // 指向内存的指针
- void* cpu_ptr_;
- void* gpu_ptr_;
- // 数据大小
- size_t size_;
- // 同步状态
- SyncedHead head_;
- // 是否拥有cpu还是gpu数据
- bool own_cpu_data_;
- bool own_gpu_data_;
- // 设备编号
- int gpu_device_;
3)成员函数
- // 获取CPUDATA
- const void* cpu_data();
- // 设置CPUDATA
- void set_cpu_data(void* data);
- // 获取GPUDATA
- const void* gpu_data();
- // 设置GPUDATA
- void set_gpu_data(void* data);
- // 获取互斥的CPU或者GPUDATA
- void* mutable_cpu_data();
- void* mutable_gpu_data();
- // 枚举类型,未初始化,在CPU、在GPU、同步状态
- enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED };
- // 获取数据的位置
- SyncedHead head() { return head_; }
- // 数据大小
- size_t size() { return size_; }
- #ifndef CPU_ONLY
- void async_gpu_push(const cudaStream_t& stream);
- #endif
- private:
- // 内部使用的到cpu还是gpu
- void to_cpu();
- void to_gpu();
具体的实现如下:
三、SyncedMemory类的具体实现
- #include <cstring>
- #include "caffe/common.hpp"
- #include "caffe/syncedmem.hpp"
- #include "caffe/util/math_functions.hpp"
- namespace caffe {
- // 析构函数就是释放内存
- SyncedMemory::~SyncedMemory() {
- if (cpu_ptr_ && own_cpu_data_) {
- CaffeFreeHost(cpu_ptr_);
- }
- #ifndef CPU_ONLY// 只要不是定义的CPU_ONLY的编译模式
- if (gpu_ptr_ && own_gpu_data_) {
- int initial_device;
- // 获取可用设备
- cudaGetDevice(&initial_device);
- if (gpu_device_ != -1) {
- // 当前所使用的设备
- CUDA_CHECK(cudaSetDevice(gpu_device_));
- }
- // 释放当前
- CUDA_CHECK(cudaFree(gpu_ptr_));
- cudaSetDevice(initial_device);
- }
- #endif // CPU_ONLY
- }
- // 内部使用的
- // 如果当前未初始化,直接在内存分配空间
- // 如果在GPU上则复制到内存
- // 如果已经在内存则啥都不动
- inline void SyncedMemory::to_cpu() {
- switch (head_) {
- // 如果当前是未初始化,直接分配CPU上的内存
- case UNINITIALIZED:
- CaffeMallocHost(&cpu_ptr_, size_);
- caffe_memset(size_, 0, cpu_ptr_);
- head_ = HEAD_AT_CPU;
- own_cpu_data_ = true;
- break;
- case HEAD_AT_GPU:
- #ifndef CPU_ONLY
- // 如果当前数据在GPU,然后cpu_ptr为空
- if (cpu_ptr_ == NULL) {
- // 分配内存
- CaffeMallocHost(&cpu_ptr_, size_);
- own_cpu_data_ = true;
- }
- // 复制数据
- caffe_gpu_memcpy(size_, gpu_ptr_, cpu_ptr_);
- head_ = SYNCED;
- #else// CPU_ONLY模式当然只能报错了
- NO_GPU;
- #endif
- break;
- case HEAD_AT_CPU:
- case SYNCED:
- break;
- }
- }
- // 内部使用的
- // 如果当前未初始化直接在GPU分配内存
- // 如果当前在CPU,则在GPU上分配内存并且复制到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_));
- // 初始化为0
- 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
- }
- // 首先不管三七二十一将数据搞到内存上去
- // 然后获取cpu上的数据
- const void* SyncedMemory::cpu_data() {
- to_cpu();
- return (const void*)cpu_ptr_;
- }
- // 如果当前cpu_ptr_有内存上的数据则先释放
- // 然后再将地址给内部变量cpu_ptr_
- // 设置cpu上的数据
- void SyncedMemory::set_cpu_data(void* data) {
- CHECK(data);
- if (own_cpu_data_) {
- CaffeFreeHost(cpu_ptr_);
- }
- cpu_ptr_ = data;
- head_ = HEAD_AT_CPU;
- own_cpu_data_ = false;
- }
- // 首先不管三七二十一将数据搞到GPU上去
- // 然后在获取gpu上的数据
- // 但是并没有改变head_的值(head_表明数据究竟在哪儿)
- const void* SyncedMemory::gpu_data() {
- #ifndef CPU_ONLY
- to_gpu();
- return (const void*)gpu_ptr_;
- #else
- NO_GPU;
- #endif
- }
- // 如果当前gpu_ptr_有内存上的数据则先释放
- // 然后再将地址给内部变量gpu_ptr_
- // 设置gpu上的数据
- void SyncedMemory::set_gpu_data(void* data) {
- #ifndef CPU_ONLY
- CHECK(data);
- if (own_gpu_data_) {
- int initial_device;
- cudaGetDevice(&initial_device);
- if (gpu_device_ != -1) {
- CUDA_CHECK(cudaSetDevice(gpu_device_));
- }
- CUDA_CHECK(cudaFree(gpu_ptr_));
- cudaSetDevice(initial_device);
- }
- gpu_ptr_ = data;
- head_ = HEAD_AT_GPU;
- own_gpu_data_ = false;
- #else
- NO_GPU;
- #endif
- }
- // 首先不管三七二十一先数据搞到CPU上去
- // 然后返回互斥的cpu_ptr_指针
- // mutable_cpu_data与cpu_data的区别就是是否设置head
- void* SyncedMemory::mutable_cpu_data() {
- to_cpu();
- head_ = HEAD_AT_CPU;
- return cpu_ptr_;
- }
- // 首先不管三七二十一先数据搞到GPU上去
- // 然后返回互斥的gpu_ptr_指针
- // mutable_gpu_data与gpu_data的区别就是是否设置head
- void* SyncedMemory::mutable_gpu_data() {
- #ifndef CPU_ONLY
- to_gpu();
- head_ = HEAD_AT_GPU;
- return gpu_ptr_;
- #else
- NO_GPU;
- #endif
- }
- #ifndef CPU_ONLY
- // 异步推送数据到gpu
- void SyncedMemory::async_gpu_push(const cudaStream_t& stream) {
- CHECK(head_ == HEAD_AT_CPU);
- if (gpu_ptr_ == NULL) {
- CUDA_CHECK(cudaGetDevice(&gpu_device_));
- 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
- } // namespace caffe
四、总结
该类主要就是在内存分配空间以及在GPU上分配空间,并且负责同步数据,此外我看mutable_cpu_data和cpu_data 这两个函数的主要区别就是head_是否改变,至于这两个函数的命名上的mutable是有着互斥的含义的。究竟体现在哪儿,我的感受是,这里的mutable的体现主要是在调用了mutable_cpu_data之后强制设置了head_为HEAD_AT_CPU,从而保护了cpu上的数据。