Caffe原始碼解析2:SycedMem
看到SyncedMem就知道,這是在做記憶體同步的操作。這類個類的程式碼比較少,但是作用是非常明顯的。檔案對應著syncedmem.hpp,著syncedmem.cpp
首先是兩個全域性的行內函數。如果機器是支援GPU的並且安裝了cuda,通過cudaMallocHost分配的host memory將會被pinned,這裡我谷歌了一下,pinned的意思就是記憶體不會被paged out,我們知道記憶體裡面是由頁作為基本的管理單元。分配的記憶體可以常駐在記憶體空間中對效率是有幫助的,空間不會被別的程序所搶佔。同樣如果記憶體越大,能被分配的Pinned記憶體自然也越大。還有一點是,對於單一的GPU而言提升並不會太顯著,但是對於多個GPU的並行而言可以顯著提高穩定性。
這裡是兩個封裝過的函式,內部通過cuda來分配主機和釋放記憶體的介面
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);//如果沒有cuda則通過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));//cuda的主機記憶體釋放操作 return; } #endif free(ptr);//c的釋放操作 }
SyncedMemory類,首先是建構函式和解構函式
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();//其在析構時呼叫的也是CaffeFreeHost
這幾個函式分別是
const void* cpu_data();
void set_cpu_data(void* data);
const void* gpu_data();
void set_gpu_data(void* data);
cpu_data()主要是獲得cpu上data的地址,set_cpu_data是將cpu的data指標指向一個新的區域由data指標傳入,並且將原來申請的記憶體釋放。下面兩個同理,分別是獲得gpu資料地址和set gpu資料地址。
void* mutable_cpu_data();
void* mutable_gpu_data();
enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED };
SyncedHead head() { return head_; }
size_t size() { return size_; }
前兩個分別是返回cpu和gpu上的data指標,並且置狀態為head_ = HEAD_AT_CPU
和響應的gpu版本。SyncedHead主要是個列舉型別,用來設定head_的狀態,head()函式即返回相應的資料狀態,而size()函式返回資料大小
#ifndef CPU_ONLY
void async_gpu_push(const cudaStream_t& stream);
#endif
這是一個cuda拷貝的非同步傳輸,從資料從cpu拷貝到gpu,非同步傳輸是已經假定caller會在使用之前做同步操作。
private:
void to_cpu();
void to_gpu();
void* cpu_ptr_;
void* gpu_ptr_;
size_t size_;
SyncedHead head_;
bool own_cpu_data_;
bool cpu_malloc_use_cuda_;
bool own_gpu_data_;
int gpu_device_;
DISABLE_COPY_AND_ASSIGN(SyncedMemory);//禁止該類的拷貝與賦值
}; // class SyncedMemory
其實這裡的東西也不多了,to_cpu(),to_gpu()這個看名字就知道了,需要注意的是,如果head 是未被初始化的狀態,那麼首先需要先分配記憶體,這個根據cpu和gpu視情況而定,之後再將資料從cpu或者gpu拷貝到另一處。之後函式會重新標記Head的狀態,資料是否在cpu或者在gpu中,cpu這裡是簡稱,其實是主機。
cpu_ptr和gpu_ptr分別是在cpu和gpu中的資料指標,size_這就不再說了,head_之前也液晶提到過了,後面都是幾個相應的標記為,以及gpu的ID號