ProtoBuf - Arena


1、概述

  最近看 Protocal Buffer 的源碼,初次見到這個庫源自陳碩的 muduo ,便打算看一看,在此做一下記錄。官網文檔不能訪問,只能憑借代碼的自己理解,查看的源碼版本為 3.6.0。

  初識 Arena 時,發現是個 allocator。Arena 每次分配一大塊內存,使用時在已經分配的內存塊上獲取,用來代替 new/delete 的堆上分配。通過一次申請大塊內存,並且一次性釋放,同時可取消調用對象的析構函數來提升效率。

  Arena 線程安全:多個線程可並發在Arena分配空間。但銷毀時非線程安全,銷毀線程必須和Arena 的使用者同步。

 

2、特點

Arena 分配器使用 CreateMessage<T> 接口創建消息:

  -類型 T 必須(至少)有兩個構造函數:1 不含參數的構造函數,當在堆上分配時使用; 2 含 google::protobuf::Arena* 參數的構造函數,在Arena上分配時使用。當第二種情況傳入空指針時,與第一種情況相同。

  -類型 T 必須有特殊的特點:定義類型 |InternalArenaConstructable_|。一般被定義為 typedef void 類型。如果沒有會在編譯時報錯。實現在 InternalHelper 模板類中,具體代碼如下。

 1 template <typename U>
 2     static char ArenaConstructable(const typename U::InternalArenaConstructable_*);
 4     template <typename U>
 5     static double ArenaConstructable(...);
 6 
 7     typedef std::integral_constant<bool, sizeof(ArenaConstructable<T>(
 8                                              static_cast<const T*>(0))) ==
 9                                              sizeof(char)>
10         is_arena_constructable;

  -類型T還 *可以* 有特征:| DestructorSkippable_|。如果該類型被定義,在傳入非空 Arena指針時,對象的析構函數不會被調用。如果沒有該特征,則在Arena銷毀時,對象的析構函數一定會被調用。實現方法與上面類似。

 1 template <typename U>
 2     static char DestructorSkippable(const typename U::DestructorSkippable_*);
 3     template <typename U>
 4     static double DestructorSkippable(...);
 5 
 6     typedef std::integral_constant<
 7         bool, sizeof(DestructorSkippable<T>(static_cast<const T*>(0))) ==
 8                       sizeof(char) ||
 9                   std::is_trivially_destructible<T>::value>
10         is_destructor_skippable;

在 proto 文件中, 設置 option cc_enable_arenas = true; 則使用 Arena 分配器。此時在使用 protoc 編譯器生成 pb.h 的代碼中會有 InternalArenaConstructable_ 和 DestructorSkippable_的定義,如

1 typedef void InternalArenaConstructable_;
2 typedef void DestructorSkippable_;

 

 

3、實現

   Arena 的實現在 arena.h arena.cc arena_impl.h 中,同時還有專門的字符串域的實現在 arenastring.h 中。其中arena_impl 中是實現存儲分配的核心代碼,是arena中一個對象。

  Arena 的內存結構如下圖所示,圖使用 Dia 繪制。每個線程都會分配一個Block,在Block上分配一個 SerialArena。Block是個鏈表結構,在空間分配時,如果該Block空間不夠用,則新建一個Block,新Block的大小是舊Block的二倍,同時新Block的next指向舊 Block。一個線程在獲取 Arena 時,如果該線程還沒分配 Block,則申請一個Block,在該Block上分配 SerialArena,該SerialArena 的 next 指向上個線程的 SerialArena。

 

  在 ArenaImpl 銷毀時,首先進行 Cleanup,然后釋放所有空間。Cleanup是根據添加的 Cleanup 表,順序調用各個對象的 cleanup 函數,Cleanup 列表如下圖所示。釋放空間時會循環銷毀每個 SerialArena 的所有 Block。

 

  暫時先寫到這里吧。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM