深度學習 Caffe 內存管理機制理解

以前在簡書的文章,搬遷過來 ^-^
本文是做者原創,若有理解錯誤,懇請你們指出,如需引用,請註明出處。函數

#Blob內存管理分析spa

在caffe的分層結構中,Blob充當了內存管理的角色,屏蔽了上層邏輯代碼對於數據的申請釋放的感知,同時也屏蔽了底層設備對上層邏輯的影響,本文主要分析Blob的管理機制和實際內存申請單元SyncedMemory 的機制。 首先咱們看一下Blob和SyncedMemory的關係,類圖以下:指針

blob_class.jpg

實際上整個Blob的實現就是在SyncedMemory上封裝了一層,因此首先須要分析一下SyncedMemory的實現機制。code

##SyncedMemory的實現機制 SyncedMemory的目的是爲了屏蔽上層代碼對不一樣硬件設備的內存分配的感知,同時隱藏了CPU和GPU之間的同步過程。同時,SyncedMemory實現時,採用的是 「lazy」的模式,就是內存的實際申請時機是在第一次使用時進行的。有了大致的瞭解,下面咱們來詳細分析一下。 下面是SyncedMemory 提供的一組接口,cdn

名稱 功能
cpu_data() 獲取CPU數據指針
gpu_data() 獲取GPU數據指針

實現的代碼以下:blog

const void* SyncedMemory::cpu_data() {
  to_cpu();
 return (const void*)cpu_ptr_;
}

const void* SyncedMemory::gpu_data() {
#ifdef USE_CUDA
  to_gpu();
  return (const void*)gpu_ptr_;
#else
  NO_GPU;
  return NULL;
#endif  // USE_CUDA
}
複製代碼

能夠看出,每次調用接口時,都會有 to_cpu() 和 to_gpu() 的操做,那麼這兩個操做是什麼做用呢,咱們先看下SyncedMemory中的一些關鍵參數:接口

名稱 功能
cpu_ptr_ cpu數據指針
gpu_ptr_ gpu數據指針
size_ 當前SyncedMemory須要維護的數據個數
head_ 當前 SyncedMemory處於的狀態

前三個都比較好理解,最後一個比較特殊,它維護的是 SyncedMemory 當前的狀態,分爲 UNINITIALIZED,HEAD_AT_GPU,HEAD_AT_CPU ,SYNCED 四中狀態。如今介紹一下具體的流程,當第一次調用 to_cpu()時, head_處於UNINITIALIZED狀態,那麼系統會調用 CPU的申請內存的方式去得到內存區域,以後設置 head_ = HEAD_AT_CPU ,若是中間過程沒有GPU設備則不會有狀態變更,若是中間有代碼調用了 to_gpu() ,則會發現 head_處於 HEAD_AT_CPU 狀態,此時會調用同步函數,將數據從CPU同步到GPU, 以後若是又回到CPU上,則一樣會發現 head_ 處於HEAD_AT_GPU的狀態,那麼又會調用相應的同步代碼,將數據同步回CPU,經過 head_這樣一個狀態參數屏蔽了GPU和CPU間的申請和切換的不一樣。內存

因此上層業務只須要知道當前本身須要的是CPU仍是GPU的數據,而後調用不一樣的接口,就能夠完成數據獲取的操做。ci

##Blob的實現分析 瞭解了SyncedMemory的實現,再來看Blob 就較爲簡單了,它僅僅作了一些上層的管理邏輯,向外界提供了幾個關鍵的接口:同步

名稱 功能
cpu_data() 獲取CPU數據指針,不能改變數據內容
mutable_cpu_data() 獲取CPU數據指針,能夠改變數據內容
gpu_data() 獲取GPU數據指針,不能改變數據內容
mutable_gpu_data() 獲取GPU數據指針,能夠改變數據內容
Reshape() 調整數據的維度信息

前四個就是對 SyncedMemory 的 cpu_data() 和 gpu_data()的封裝,只須要確保每次獲取數據前都調用相對的 to_cpu 或者 to_gpu就能夠了。對於最後一個Reshape函數,主要是爲了調整維度信息,同時多是出於適配多種數據格式的目的,因此提供3個重載函數,以下:

void Blob::Reshape(const int num, const int channels,const int height, const int width);
void Blob::Reshape(const BlobShape& shape) ;
void Blob::Reshape(const vector<int>& shape);
複製代碼

前兩個重載函數僅僅進行了數據格式的轉換,而後調用第三個函數,因此 void Blob::Reshape(const vector<int>& shape);纔是實際的執行者,這裏須要介紹一下Blob裏面較爲關鍵的幾個參數:

名稱 功能
data_ 數據的實際存儲位置
shape_data_ 數據的維度信息存儲位置(NCHW)
capacity_ 當前數據塊的大小
count_ reshape後的數據塊的大小

閱讀代碼不難發現,cout_中所存儲的就是全部維度的的乘積,也就是當前要reshape到的數據大小,整個的reshape 過程以下:

reshape.jpg

##結束 以上就是我對Blob的一些理解,但願對你們有幫助。

相關文章
相關標籤/搜索