以前在簡書的文章,搬遷過來 ^-^
本文是做者原創,若有理解錯誤,懇請你們指出,如需引用,請註明出處。函數
#Blob內存管理分析spa
在caffe的分層結構中,Blob充當了內存管理的角色,屏蔽了上層邏輯代碼對於數據的申請釋放的感知,同時也屏蔽了底層設備對上層邏輯的影響,本文主要分析Blob的管理機制和實際內存申請單元SyncedMemory 的機制。 首先咱們看一下Blob和SyncedMemory的關係,類圖以下:指針
實際上整個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 過程以下:
##結束 以上就是我對Blob的一些理解,但願對你們有幫助。