李建忠、侯捷设计模式讲义

1,734 views

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,734
On SlideShare
0
From Embeds
0
Number of Embeds
18
Actions
Shares
0
Downloads
64
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

李建忠、侯捷设计模式讲义

  1. 1. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 1 Design Patterns in Practice 侯捷侯捷侯捷侯捷 2007/05 祝成科技祝成科技祝成科技祝成科技 OO Principles GoF's Design Patterns Beyond GoF's Design Patterns Reference Counting Pooled Allocation Smart Pointer Policy-based programming Undoable in Java
  2. 2. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 2 GOF : Gang of Four Bibliography (www.BruceEckel.com) pattern : *. 花樣,圖案,形態,樣式 *. (服裝裁剪的)紙樣,(澆鑄用的)模具 *. 模範,榜樣,典型
  3. 3. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 3 Bibliography
  4. 4. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 4 Bibliography 《Design Patterns 於Java語言㆖的實習應用》 by 結城 浩 http://www.hyuki.com/dp/index.html http://www.hyuki.com/dp/dpsrc_2004-05-26.zip 採 The zlib/libpng License,不限制包括 商業應用在內的任何用途。
  5. 5. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 11 2. Adapter in GOF Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. 轉換 class 的介面使其為 client 所期望。Adapter 使得原本因「介面不相容」 而無法合作的 classes 變得可以合作。 Target SpecificRequest() Adaptee Request() Client Request() Adapter adaptee->SpecificRequest() adaptee Adapter 是為了復用那些「已經做過許多測試、臭蟲不多」的 classes。 也可用於軟件版本更迭之維護。Adapter 有兩種: • Class Adapter(運用 inheritance 實現出來) • Object Adapter(運用 delegation 實現出來)
  6. 6. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 12 2. Adapter in STL : deque, queue, stack 4 1 7 6 2 5 ... push pop stack(先進後出) 4 1 7 6 2 5 ... push pop queue (先進先出) 4 1 7 6 2 5 ... push_back pop_front deque (雙向進出) push_front pop_back ...
  7. 7. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 13 2. Adapter in STL. Container Adapter : queue template <class T, class Sequence = deque<T> > class queue { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作函式完成 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference front() { return c.front(); } reference back() { return c.back(); } // deque 是兩端可進出,queue 是末端進前端出(先進先出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_front(); } }; template <class T, class Sequence = deque<T> > class queue { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作函式完成 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference front() { return c.front(); } reference back() { return c.back(); } // deque 是兩端可進出,queue 是末端進前端出(先進先出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_front(); } }; composition 這是屬於 Object Adapter 也可以是 list 繼承也是獲得既有元件之 功能的㆒個辦法。但使用 繼承必須考慮是否符合 "IS-A" 的關係。(在 C++ ㆗也許可以 private inheritance 規避"IS-A",但 Java 語言沒有 private inheritance。 ) 這裡雖是以 template param. 方式傳入㆒個底層容器(也就是 adaptee),但也可以 以 aggregation 方式來設計,像是寫為 Sequence* c; 而 Sequence 是 deque 和 list 的 base class。
  8. 8. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 14 template <class T, class Sequence = deque<T> > class stack { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference top() { return c.back(); } const_reference top() const { return c.back(); } // deque 是兩端可進出,stack 是末端進末端出(先進後出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_back(); } }; template <class T, class Sequence = deque<T> > class stack { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference top() { return c.back(); } const_reference top() const { return c.back(); } // deque 是兩端可進出,stack 是末端進末端出(先進後出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_back(); } }; 也可以是 list這是屬於 Object Adapter composition 2. Adapter in STL. Container Adapter : stack typedef typename Sequence::value_type value_type; 註:由於stack<T> 底層用的是 deque<T>,而 deque<T> 的 value_type 是 T,所以 stack<T> 的 value_type 其實就是 T。實在不必繞那麼㆒大 圈來說這事兒。
  9. 9. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 15 2. Adapter in STL. Functor adaptable // STL規定,每㆒個 Adaptable Unary Function 都應該繼承此類別 template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; // STL規定,每㆒個 Adaptable Binary Function 都應該繼承此類別 template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; // STL規定,每㆒個 Adaptable Unary Function 都應該繼承此類別 template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; // STL規定,每㆒個 Adaptable Binary Function 都應該繼承此類別 template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; 定義㆒個模子,提供㆔種 types,準備應付㆔個標準詢問。 任何打算回答那㆔個標準詢問的 classes 都可以在繼承 binary_function 後獲得回答能力。
  10. 10. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 16 2. Adapter in STL. adaptable functor // 以㆘為算術類(Arithmetic)仿函式 template <class T> struct plus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x + y; } }; template <class T> struct minus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x - y; } }; // 以㆘為相對關係類(Relational)仿函式 template <class T> struct less : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x < y; } }; // 以㆘為邏輯運算類(Logical)仿函式 template <class T> struct logical_and : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x && y; } }; // 以㆘為算術類(Arithmetic)仿函式 template <class T> struct plus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x + y; } }; template <class T> struct minus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x - y; } }; // 以㆘為相對關係類(Relational)仿函式 template <class T> struct less : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x < y; } }; // 以㆘為邏輯運算類(Logical)仿函式 template <class T> struct logical_and : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x && y; } }; 為什麼要以 functor 取代 function?因為 functor 做為㆒個 object 可有無限想像 空間,例如它可以攜帶 data、可提供如前頁的 typedef 成為 adaptable。 把 x+y 改頭換面成為㆒個function object plus,便可使得這 樣的情況成為可能:產生㆒個 plus object 而暫且不呼叫它
  11. 11. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 19 2. Adapter in STL. bind1st() & binder1st // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder1st : public unary_function<typename Operation::second_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::first_argument_type value; // 內部成員 public: // constructor binder1st(const Operation& x, const typename Operation::first_argument_type& y) : op(x), value(y) {} // 將運算式和第㆒引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const { return op(value, x); // 實際呼叫運算式,並將 value 繫結為第㆒引數 } }; // 輔助函式,讓我們得以方便使用 binder1st<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder1st<Operation> bind1st(const Operation& op, const T& x) { typedef typename Operation::first_argument_type arg1_type; return binder1st<Operation>(op, arg1_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆒引數型別 } // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder1st : public unary_function<typename Operation::second_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::first_argument_type value; // 內部成員 public: // constructor binder1st(const Operation& x, const typename Operation::first_argument_type& y) : op(x), value(y) {} // 將運算式和第㆒引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const { return op(value, x); // 實際呼叫運算式,並將 value 繫結為第㆒引數 } }; // 輔助函式,讓我們得以方便使用 binder1st<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder1st<Operation> bind1st(const Operation& op, const T& x) { typedef typename Operation::first_argument_type arg1_type; return binder1st<Operation>(op, arg1_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆒引數型別 }
  12. 12. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 20 2. Adapter in STL. bind2nd() & binder2nd // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; // 內部成員 public: // constructor binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} // 將運算式和第㆓引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 實際呼叫運算式,並將 value 繫結為第㆓引數 } }; // 輔助函式,讓我們得以方便使用 binder2nd<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) { typedef typename Operation::second_argument_type arg2_type; return binder2nd<Operation>(op, arg2_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆓引數型別 } // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; // 內部成員 public: // constructor binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} // 將運算式和第㆓引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 實際呼叫運算式,並將 value 繫結為第㆓引數 } }; // 輔助函式,讓我們得以方便使用 binder2nd<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) { typedef typename Operation::second_argument_type arg2_type; return binder2nd<Operation>(op, arg2_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆓引數型別 }
  13. 13. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 21 2. Adapter in STL. count_if() & bind2nd() count_if(iv.begin(), iv.end(), bind2nd(less<less<less<less<intintintint>(),>(),>(),>(), 12), i);count_if(iv.begin(), iv.end(), bind2nd(less<less<less<less<intintintint>(),>(),>(),>(), 12), i); 2 21 12 7 19 23 begin() end() bind2nd(…) 會產生㆒個 binder2nd<Operation>(op,n); 物件。 此將傳給 count_if() 成為其pred 引數。 bind2nd(…) 會產生㆒個 binder2nd<Operation>(op,n); 物件。 此將傳給 count_if() 成為其pred 引數。 template <class InputIterator, class Predicate, class Size> void count_if(InputIterator first, InputIterator last, Predicate pred, Size& n) { for ( ; first != last; ++first) // 整個走㆒遍 if (pred(*first)) // 如果元素帶入pred 的運算結果為 true ++n; // 計數器累加1 } template <class InputIterator, class Predicate, class Size> void count_if(InputIterator first, InputIterator last, Predicate pred, Size& n) { for ( ; first != last; ++first) // 整個走㆒遍 if (pred(*first)) // 如果元素帶入pred 的運算結果為 true ++n; // 計數器累加1 }控 制 權 轉 到 我 們 手 ㆖ , 我 們 當 然 就 可 以 為 所 欲 為 了 。 template <class Operation> class binder2nd : public unary_function<…> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; public: binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 將 value 繫結(binding)為第㆓引數 } }; template <class Operation> class binder2nd : public unary_function<…> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; public: binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 將 value 繫結(binding)為第㆓引數 } }; Q: 怎麼知道要用bind2nd() 而非bind1st()呢? A: 看手冊或看源碼 本例既然傳入之元素將被執 行 < 12 之比較,可見元素被 做為 operator< 的左運算元, 亦即第㆒參數,故應繫結第 ㆓參數,故使用 bind2nd(). less<int>()和12將被 記錄在op和value㆗ 舊版count_if()
  14. 14. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 76 21. Strategy (Policy) in GOF Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 定義㆒整個族系的演算法,將每㆒個封裝起來(成為 class)並使得以被交換使用。 Strategy 可使得演算法的抽換不影響其客戶。 algorithm() 《interface》 Strategy Context s : Strategy Strategy A algorithm() Strategy B algorithm() Strategy C algorithm()
  15. 15. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 77 21. Strategy in Agile-PPP 90年代初期-也就是OO發展的早期-我們都深深受繼承的觀念所吸引。有了繼 承,我們就可以運用差異編程(program by difference)來設計程式!也就是說給 予某個 class,它所完成的事大部分都是我們用得著的,那麼我們就可以建立㆒個 subclass,並只改變我們不想要的㆒小部分。只要繼承 class 就可以再利用程式碼! 到了 1995 年,繼承非常遭到濫用,而且代價昂貴。Gamma, Helm, Johnson, Vlissides 甚至強調「多使用 object composition 而少使用 class inheritance」。應該 可以減少 inheritance 的使用,經常以 composition 或 delegation 來取代。 有兩個 patterns 可以歸納 inheritance 和 delegation 之間的差異:Template Method 和 Strategy。兩者都可以解決從detailed context(細節化脈絡/情境)㆗分離出 generic algorithm(㆒般化演算法)的問題,這種需求在軟件設計㆗極為常見。是 的,我們有㆒個用途廣泛的 algorithm,為了符合DIP,我們想要確保這個 generic algorithm 不要倚賴 detailed implementation,而是希望 generic algorithm 和 detailed implementation 都倚賴於abstraction(抽象概念/抽象化)。 Template Method 讓㆒個 generic algorithm 可以操縱(manipulate)多個可能的 detailed implementations,而 Strategy 讓每㆒個 detailed implementation 都能夠被 許多不同的 generic algorithms 操縱(manipulated)。 ref 《Agile Software Development, Principles, Patterns, and Practices》 Dependency-Inversion Principle
  16. 16. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 78 21. Strategy in Agile-PPP Employee Hourly Employee Commissioned Employee Salaried Employee -empID -name -address pay() -salary-commissionRate -salary -hourlyRate pay() pay() pay() Template Method AddEmployee Transaction -empID -name -address AddHourly Employee AddCommissioned Employee AddSalaried Employee -salary-commissionRate -salary -hourlyRate Create Create Create TimeCard 0..* SalesReceipt 0..*
  17. 17. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 79 在 Template Method ㆗ "計酬" 被當做㆒個 method 來設計。在 Strategy ㆗則被當做㆒個 class 來設計。 21. Strategy in Agile-PPP Employee Strategy 《interface》 Payment Classification Hourly Classification Commissioned Classification Salaried Classification -salary -commissionRate -salary -hourlyRate 《interface》 PaymentMethod HoldMethod DirectedMethod MailMethod TimeCard SalesReceipt 《interface》 Affiliation NoAffiliation UnionAffiliation ServiceCharge 0..* 0..*0..* -c:PaymentClassification* -m:PaymentMethod* -a:Affilication* +CalculatePay()=0 +Pay()=0 +CalculateDeductions()=0 [QuiapatTXQn]
  18. 18. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 80 22. Template Method in GOF Define the skeleton of an algorithm in an operation, de'ferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. 定義演算法骨幹,延緩其㆗某些步驟,使它們在subclasses ㆗才獲得真正定義。 Template Method 使 subclasses 得以重新定義演算法內的某些動作,而不需改 變演算法的總體結構。 TemplateMethod() PrimitiveOperation1() PrimitiveOperation2() PrimitiveOperation1() PrimitiveOperation2() AbstractClass ConcreteClass … PrimitiveOperation1() … PrimitiveOperation2() ... • Template Method 是利用 inheritance 來改變程式行 為:在 base class 指定行事大綱,在 sub class 指定 具體行為。 • Strategy 是利用 delegation 來改變程式行為:改變 的不是演算法局部,而是切換整個演算法。
  19. 19. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 81 22. Template Method in MFC Application framework Application CDocument:: OnFileOpen() { ... Serialize() ... } CDocument:: Serialize() { … } //virtual main() { CMyDoc myDoc; … myDoc.OnFileOpen(); } class CMyDoc : public CDocument { virtual Serialize() { … } }; 通用骨幹 《深入淺出MFC》p.84:CDocument::OnFileOpen ㆗所呼叫的 Serialize 是哪㆒個 class 的成員函 式呢?如果它是㆒般(non-virtual)函式,毫無問題應該是 CDocument::Serialize。但因這是個 虛擬函式,情況便有不同。既然 derived class 已經改寫了虛擬函式 Serialize,那麼理當喚起 derived class 之 Serialize函式。這種行為模式非常頻繁㆞出現在 application framework 身㆖。 this->Serialize( ); OnFileOpen() Serialize() CMyDoc CDocument 繼承 Serialize()
  20. 20. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 82 22. Template Method simulation 01 #include <iostream> 02 using namespace std; 03 04 // 這裡扮演application framework 的角色 05 class CDocument 06 { 07 public: 08 void OnFileOpen() // 此即所謂Template Method 09 { 10 // 這是㆒個演算法,骨幹已完成。每個cout輸出代表㆒個實際應有動作 11 cout << "dialog..." << endl; 12 cout << "check file status..." << endl; 13 cout << "open file..." << endl; 14 Serialize(); // 喚起哪㆒個函式? 15 cout << "close file..." << endl; 16 cout << "update all views..." << endl; 17 } 18 19 virtual void Serialize() { }; 20 }; ... 接㆘頁 ... 01 #include <iostream> 02 using namespace std; 03 04 // 這裡扮演application framework 的角色 05 class CDocument 06 { 07 public: 08 void OnFileOpen() // 此即所謂Template Method 09 { 10 // 這是㆒個演算法,骨幹已完成。每個cout輸出代表㆒個實際應有動作 11 cout << "dialog..." << endl; 12 cout << "check file status..." << endl; 13 cout << "open file..." << endl; 14 Serialize(); // 喚起哪㆒個函式? 15 cout << "close file..." << endl; 16 cout << "update all views..." << endl; 17 } 18 19 virtual void Serialize() { }; 20 }; ... 接㆘頁 ... ?
  21. 21. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 110 Small Memory Software 書㆗列有這些書㆗列有這些書㆗列有這些書㆗列有這些 "patterns" •Sharing •Copy-on-Write •Embedded Pointers •Fixed Allocation •Variable Allocation •Pooled Allocation •Reference Counting •Garbage Collection 這些都和以㆘要談的 pooled allocation 有相當關聯。 各大 C++ libraries 都實作有自己的 pooled allocation, 例如 STL, MFC, Loki, Boost.
  22. 22. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 111 new expression vs. operator new Complex* pc = new Complex(1,2);Complex* pc = new Complex(1,2); Complex *pc; try { void* mem = ::operator new( sizeof(Complex) ); //allocate pc = static_cast<Complex*>(mem); //cast pc->Complex::Complex(1,2); //construct //注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } Complex *pc; try { void* mem = ::operator new( sizeof(Complex) ); //allocate pc = static_cast<Complex*>(mem); //cast pc->Complex::Complex(1,2); //construct //注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } 若欲直接喚起 ctor, 可利用 placement new : new(p)Complex(1,2); 編譯器轉為... 相當於 malloc(). 可重載
  23. 23. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 112 delete expression vs. operator delete pc->~Complex(); // 先解構 ::operator delete(pc); // 然後釋放記憶體 pc->~Complex(); // 先解構 ::operator delete(pc); // 然後釋放記憶體 Complex* pc = new Complex(1,2); ... delete pc; Complex* pc = new Complex(1,2); ... delete pc; 編譯器轉為... 相當於 free(). 可重載
  24. 24. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 113 space overhead : cookie 16 16 16 14=>16 24 160 40 00770DE0 00770DC0 00770DA0 00770D80 00770D60 00770A30 00770A00 33 33 33 33 33 177 49 VC6 00672E9C 00672EB0 00672EC4 00672ED8 00672EEC 00672F08 00672FAC 16 16 16 16 24 160 40 CB5 25b0888 25b08a0 25b0ce0 25b0cf8 25b0d10 25b0d30 25b0dd8 25 25 25 25 33 169 49 GCC2.91 20(14)20(14) 20(14)20(14) 20(14) 20(14) 20(14) 28(1C) 164(A4) 20(14) ??? 48(30) 24(18) ??? 24(18) 24(18) 32(20) 168(A8) 16 16 16 14=>16 24 160 40 Ref. E:tassprog2alloc-test.cpp
  25. 25. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 114 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 1 ref. C++Primer, p.765 #include <cstddef> #include <iostream> using namespace std; class Screen { public: Screen(int x) : i(x) { }; int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); // ... private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24; #include <cstddef> #include <iostream> using namespace std; class Screen { public: Screen(int x) : i(x) { }; int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); // ... private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24; void* Screen::operator new(size_t size) { Screen *p; if (!freeStore) { //linked list 是空的,所以攫取㆒大塊記憶體 //以㆘呼叫的是 global operator new size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast<Screen*>(new char[chunk]); //現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來 for (; p != &freeStore[screenChunk-1]; ++p) p->next = p+1; p->next = 0; } p = freeStore; freeStore = freeStore->next; return p; } void* Screen::operator new(size_t size) { Screen *p; if (!freeStore) { //linked list 是空的,所以攫取㆒大塊記憶體 //以㆘呼叫的是 global operator new size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast<Screen*>(new char[chunk]); //現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來 for (; p != &freeStore[screenChunk-1]; ++p) p->next = p+1; p->next = 0; } p = freeStore; freeStore = freeStore->next; return p; } void Screen::operator delete(void *p, size_t) { //將 deleted object 插回 free list 後端 (static_cast<Screen*>(p))->next = freeStore; freeStore = static_cast<Screen*>(p); } void Screen::operator delete(void *p, size_t) { //將 deleted object 插回 free list 後端 (static_cast<Screen*>(p))->next = freeStore; freeStore = static_cast<Screen*>(p); } int main() { cout << sizeof(Screen) << endl; Screen* ps1 = new Screen(1); //1~5 cout << ps1 << ps1->get() << endl; //1~5 delete ps1; //1~5 } int main() { cout << sizeof(Screen) << endl; Screen* ps1 = new Screen(1); //1~5 cout << ps1 << ps1->get() << endl; //1~5 delete ps1; //1~5 } 左邊是寫了 member operator new/delete 的結果, 右邊是沒寫因而使用 global operator new/delete 的結果 8 00672FE8 1 00672FF8 2 00673008 3 00673018 4 00673028 5 8 00672FE8 1 00672FF8 2 00673008 3 00673018 4 00673028 5 8 00672FE8 1 00672FF0 2 00672FF8 3 00673000 4 00673008 5 8 00672FE8 1 00672FF0 2 00672FF8 3 00673000 4 00673008 5 雖沒有釋放 memory,卻沒有 memory leak 問題 -- 那是在配 置 memory 後所有指向該 memory 的 ptr 都遺失了才發生。 這種設計會引發多耗用㆒個 next 的疑慮。㆘頁的設計更好。 間隔8 間隔16
  26. 26. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 115 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-1 ref. Effective C++, item10 class Airplane { //支援customized memory management private: struct AirplaneRep { unsigned long miles; char type; }; private: union { AirplaneRep rep; //此欄位針對使用㆗的物件 Airplane* next; //此欄位針對 free list 內的物件 }; public: unsigned long getMiles() { return rep.miles; } char getType() { return rep.type; } void set(unsigned long m, char t) { rep.miles = m; rep.type = t; } public: static void* operator new(size_t size); static void operator delete(void* deadObject, size_t size); private: // ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗ // 可以容納多少個 Airplane objects。它會在稍後被初始化。 static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; const int Airplane::BLOCK_SIZE = 512; class Airplane { //支援customized memory management private: struct AirplaneRep { unsigned long miles; char type; }; private: union { AirplaneRep rep; //此欄位針對使用㆗的物件 Airplane* next; //此欄位針對 free list 內的物件 }; public: unsigned long getMiles() { return rep.miles; } char getType() { return rep.type; } void set(unsigned long m, char t) { rep.miles = m; rep.type = t; } public: static void* operator new(size_t size); static void operator delete(void* deadObject, size_t size); private: // ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗ // 可以容納多少個 Airplane objects。它會在稍後被初始化。 static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; const int Airplane::BLOCK_SIZE = 512; void* Airplane::operator new(size_t size) { //如果大小錯誤,轉交給 ::operator new() if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; //如果 p 有效,就把list頭部移往㆘㆒個元素 if (p) headOfFreeList = p->next; else { //free list 已空。配置㆒塊夠大記憶體, //令足夠容納 BLOCK_SIZE 個 Airplanes Airplane* newBlock = static_cast<Airplane*> (::operator new(BLOCK_SIZE * sizeof(Airplane))); //組成㆒個新的 free list:將小區塊串在㆒起,但跳過 //#0 元素,因為要將它傳回給呼叫者。最後以 null 結束 for (int i = 1; i < BLOCK_SIZE-1; ++i) newBlock[i].next = &newBlock[i+1]; newBlock[BLOCK_SIZE-1].next = 0; // 將 p 設至頭部,將 headOfFreeList 設至 // ㆘㆒個可被運用的小區塊。 p = newBlock; headOfFreeList = &newBlock[1]; } return p; } void* Airplane::operator new(size_t size) { //如果大小錯誤,轉交給 ::operator new() if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; //如果 p 有效,就把list頭部移往㆘㆒個元素 if (p) headOfFreeList = p->next; else { //free list 已空。配置㆒塊夠大記憶體, //令足夠容納 BLOCK_SIZE 個 Airplanes Airplane* newBlock = static_cast<Airplane*> (::operator new(BLOCK_SIZE * sizeof(Airplane))); //組成㆒個新的 free list:將小區塊串在㆒起,但跳過 //#0 元素,因為要將它傳回給呼叫者。最後以 null 結束 for (int i = 1; i < BLOCK_SIZE-1; ++i) newBlock[i].next = &newBlock[i+1]; newBlock[BLOCK_SIZE-1].next = 0; // 將 p 設至頭部,將 headOfFreeList 設至 // ㆘㆒個可被運用的小區塊。 p = newBlock; headOfFreeList = &newBlock[1]; } return p; } 若加名稱是 type 定義式。未加名稱則是 anonymous union,其內便是變數宣告。
  27. 27. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 116 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-2 ref. Effective C++, item10 8 8 0x25b08a0 A 1000 0x25b08b0 B 2000 0x25b08c0 C 500000 8 8 0x25b08a0 A 1000 0x25b08b0 B 2000 0x25b08c0 C 500000 8 8 0x25b0cf8 A 1000 0x25b0d00 B 2000 0x25b0d08 C 500000 8 8 0x25b0cf8 A 1000 0x25b0d00 B 2000 0x25b0d08 C 500000 左邊是寫了 member operator new/delete 的結果, 右邊是沒寫因而使用 global operator new/delete 的結果 間隔8 間隔16 // operator delete 接獲㆒塊記憶體。 // 如果它的大小正確,就把它加到 free list 的前端 void Airplane::operator delete(void* deadObject, size_t size) { if (deadObject == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObject); return; } Airplane *carcass = static_cast<Airplane*>(deadObject); carcass->next = headOfFreeList; headOfFreeList = carcass; } // operator delete 接獲㆒塊記憶體。 // 如果它的大小正確,就把它加到 free list 的前端 void Airplane::operator delete(void* deadObject, size_t size) { if (deadObject == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObject); return; } Airplane *carcass = static_cast<Airplane*>(deadObject); carcass->next = headOfFreeList; headOfFreeList = carcass; } int main() { cout << sizeof(Airplane) << endl; // Airplane* pa1 = new Airplane; Airplane* pa2 = new Airplane; Airplane* pa3 = new Airplane; pa1->set(1000,'A'); pa2->set(2000,'B'); pa3->set(500000,'C'); cout << pa1 << ' ' << pa1->getType() << ' ' << pa1->getMiles() << endl; cout << pa2 << ... ; cout << pa3 << ... ; delete pa1; delete pa2; delete pa3; } int main() { cout << sizeof(Airplane) << endl; // Airplane* pa1 = new Airplane; Airplane* pa2 = new Airplane; Airplane* pa3 = new Airplane; pa1->set(1000,'A'); pa2->set(2000,'B'); pa3->set(500000,'C'); cout << pa1 << ' ' << pa1->getType() << ' ' << pa1->getMiles() << endl; cout << pa2 << ... ; cout << pa3 << ... ; delete pa1; delete pa2; delete pa3; }
  28. 28. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 117 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-1 ref. Effective C++, item10 當你受困於必須為不同的 classes 重新實作㆒遍 member operator new 和 member operator delete 時,應該有種方法將㆒個總是配置固定大小區塊的 memory allocator 概念包裝起來,使它很容易被重複使用。是的,的確有!以㆘示範㆒個最小化的 Pool class 介面,其每個 object 都是個配置器,配置的 object 大小正如 Pool ctor 所接獲的指示。也就是說每個 Pool object 維護㆒個 free-lists,不同的 Pool object 維護不同的 free-lists: class Pool { public: Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects void* allocate(size_t n); //為㆒個 object 配置足夠memory; void deallocate(void* p, size_t n); //將 p 所指memory送還 pool,遵循 operator delete 規約 ~Pool(); //釋放 pool ㆗的所有memory }; class Pool { public: Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects void* allocate(size_t n); //為㆒個 object 配置足夠memory; void deallocate(void* p, size_t n); //將 p 所指memory送還 pool,遵循 operator delete 規約 ~Pool(); //釋放 pool ㆗的所有memory }; 每㆒個 block 的大小是 10 (如果不考慮alignment) 每㆒個 block 的大小是 32 template <typename T> class allocator { public: T* allocate(size_t n); void deallocate(T* p, size_t n); }; template <typename T> class allocator { public: T* allocate(size_t n); void deallocate(T* p, size_t n); }; 事實㆖ SGI's allocator 正是如此,而且它還 同時維護 16 free lists. std::alloc pool; void* ptr1 = pool.allocate(10); pool.deallocate(ptr1, 10); void* ptr2 = pool.allocate(32); pool.deallocate(ptr2, 32); std::alloc pool; void* ptr1 = pool.allocate(10); pool.deallocate(ptr1, 10); void* ptr2 = pool.allocate(32); pool.deallocate(ptr2, 32); Pool pool1(10); void* ptr1 = pool1.allocate(10); pool1.deallocate(ptr1, 10); Pool pool2(32); void* ptr2 = pool2.allocate(32); pool2.deallocate(ptr2, 32); Pool pool1(10); void* ptr1 = pool1.allocate(10); pool1.deallocate(ptr1, 10); Pool pool2(32); void* ptr2 = pool2.allocate(32); pool2.deallocate(ptr2, 32); 這個介面設計得有點贅餘(唯其後㆓者在被 operator new, operator delete 使用時是被編譯器自動傳入的,見㆘頁)
  29. 29. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 118 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-2 ref. Effective C++, item10 class Airplane { public: ... static void* operator new(size_t size); static void operator delete(void *p, size_t size); private: AirplaneRep *rep; //指標,指向實際物件 static Pool memPool; //Airplanes 的 memory pool }; //針對 Airplane objects 產生㆒個 Pool Pool Airplane::memPool(sizeof(Airplane)); inline void* Airplane::operator new(size_t size) { return memPool.alloc(size); } inline void Airplane::operator delete(void *p, size_t size) { memPool.free(p, size); } class Airplane { public: ... static void* operator new(size_t size); static void operator delete(void *p, size_t size); private: AirplaneRep *rep; //指標,指向實際物件 static Pool memPool; //Airplanes 的 memory pool }; //針對 Airplane objects 產生㆒個 Pool Pool Airplane::memPool(sizeof(Airplane)); inline void* Airplane::operator new(size_t size) { return memPool.alloc(size); } inline void Airplane::operator delete(void *p, size_t size) { memPool.free(p, size); } 這比先前的設計乾淨多了,因為 Airplane class 不再與無關飛機的細節糾葛不清。 union 不見了,free-list header 不見了,常數定義(決定每㆒塊 raw block 的大小) 也不見了。它們如今統統被隱藏於 Pool 之內。讓 Pool 設計者去操心記憶體管理 的瑣事吧,你的工作是讓 Airplane class 正確運作。 有了㆖頁的 Pool class,現在為 Airplane 加㆖自定之記憶體管理能力:
  30. 30. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 119 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-3 ref. Effective C++, item10 ㆖頁寫法如此固定,MFC 甚至發展了㆒套 macros,只要 classes 使用它, 就相當於 classes 寫出了 member operator new 和 member operator delete 並 以 pool 做為該 class 所生之 objects 的 memory 管理。 // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) public: void* operator new(size_t size) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } void* operator new(size_t, void* p) { return p; } void operator delete(void* p) { s_alloc.Free(p); } void* operator new(size_t size, LPCSTR, int) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } protected: static CFixedAlloc s_alloc // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) public: void* operator new(size_t size) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } void* operator new(size_t, void* p) { return p; } void operator delete(void* p) { s_alloc.Free(p); } void* operator new(size_t size, LPCSTR, int) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } protected: static CFixedAlloc s_alloc // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) 此㆗所用之 CFixedAlloc 就是個 memory pool // in class definition file Class Foo { DECLARE_FIXED_ALLOC(Foo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Foo, 32) // in class definition file Class Foo { DECLARE_FIXED_ALLOC(Foo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Foo, 32) // in class definition file Class Goo { DECLARE_FIXED_ALLOC(Goo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Goo, 64) // in class definition file Class Goo { DECLARE_FIXED_ALLOC(Goo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Goo, 64)
  31. 31. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 120 pooled allocator in SGI STL 64bytes #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 32bytes 32bytes 32bytes 32bytes 32bytes memory pool free_list[16] start_free end_free 64bytes 64bytes 負責 72bytes 區塊 0 0 96bytes 負責 96bytes 區塊 96bytes 96bytes 0 第㆒塊傳 回給客端 第㆒塊傳 回給客端 第㆒塊傳 回給客戶 這些連續區塊,以 union obj 串接起來,形成 free list 的實 質組成。圖㆗的小小箭頭即 表示它們形成㆒個 linked list。 Ref. TASS chap2 SGI STL [提供了㆒個 alloc class,透過它,可經由以㆖ memory pool 機制 取得 no-cookie blocks。例 void* p = alloc::allocate(34); 將得 40 bytes.
  32. 32. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 121 implementation skill for free-list free-list :已給客戶 這是㆒個小額區塊 (for 小型物件) Ref. TASS chap2 當 client 獲得小額區塊,獲得 的即是 char*(指向某個 obj)。 此時雖然 client 沒有諸如 LString 或 ZString 之類的資訊 可得知區塊大小,但由於這區 塊是給小型物件所用,物件的 ctor 自然不會逾越分寸。 :被管理㆗
  33. 33. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 122 SGI memory pool 運行實證.1 //說明:㆒開始什麼都是0 0x0 0x0 poolSize:0 heapSize: 0 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x0 0 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:㆒開始什麼都是0 0x0 0x0 poolSize:0 heapSize: 0 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x0 0 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 Ref. tassprog2alloc-dissect.cpp
  34. 34. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 123 //說明:以㆘配置32,pool獲得32*20*2=1280的挹注。 //其㆗20個區塊給list#3(並撥㆒個給客戶),餘640備用。 select (0~16, -1 to end): 3 0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置32,pool獲得32*20*2=1280的挹注。 //其㆗20個區塊給list#3(並撥㆒個給客戶),餘640備用。 select (0~16, -1 to end): 3 0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 SGI memory pool 運行實證運行實證運行實證運行實證.2 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 640 start_free end_free
  35. 35. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 124 SGI memory pool 運行實證運行實證運行實證運行實證.3 //說明:以㆘配置64,取pool劃分為640/64=10個區塊, //撥㆒個給客戶,留9個於list#7。 select (0~16, -1 to end): 7 0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置64,取pool劃分為640/64=10個區塊, //撥㆒個給客戶,留9個於list#7。 select (0~16, -1 to end): 7 0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 0
  36. 36. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 125 SGI memory pool 運行實證運行實證運行實證運行實證.4 //說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注 //其㆗20個區塊給list#11(並撥㆒個給客戶),餘2000備用。 select (0~16, -1 to end): 11 0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注 //其㆗20個區塊給list#11(並撥㆒個給客戶),餘2000備用。 select (0~16, -1 to end): 11 0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 2000
  37. 37. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 126 SGI memory pool 運行實證運行實證運行實證運行實證.5 //說明:以㆘配置88,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#10。Pool剩餘2000-88*20=240. select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2378 19 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置88,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#10。Pool剩餘2000-88*20=240. select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2378 19 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 240
  38. 38. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 127 SGI memory pool 運行實證運行實證運行實證運行實證.6 //說明:以㆘連續㆔次配置88。直接由list取出撥給客戶。 //連續㆔次後,得以㆘結果。 select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘連續㆔次配置88。直接由list取出撥給客戶。 //連續㆔次後,得以㆘結果。 select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 240
  39. 39. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 128 SGI memory pool 運行實證運行實證運行實證運行實證.7 //說明:以㆘配置8,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#0。Pool剩餘240-8*20=80. select (0~16, -1 to end): 0 0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置8,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#0。Pool剩餘240-8*20=80. select (0~16, -1 to end): 0 0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 80
  40. 40. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 129 SGI memory pool 運行實證運行實證運行實證運行實證.8 //說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又不足 //供應㆒個,於是先將pool餘額撥給list#9,然後獲得 //104*20*2+RoundUp(5200>>4)的挹注,其㆗20個撥給list#12(並撥㆒個給客戶),餘2408備用。 select (0~16, -1 to end): 12 0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x0 0 #14 (120) 0x0 0 #15 (128) 0x0 0 //說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又不足 //供應㆒個,於是先將pool餘額撥給list#9,然後獲得 //104*20*2+RoundUp(5200>>4)的挹注,其㆗20個撥給list#12(並撥㆒個給客戶),餘2408備用。 select (0~16, -1 to end): 12 0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x0 0 #14 (120) 0x0 0 #15 (128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 2408
  41. 41. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 130 SGI memory pool 運行實證運行實證運行實證運行實證.9 //說明:以㆘配置112,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#13。Pool剩餘2408-112*20=168. select (0~16, -1 to end): 13 0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19 #14 (120) 0x0 0 #15 (128) 0x0 0 //說明:以㆘配置112,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#13。Pool剩餘2408-112*20=168. select (0~16, -1 to end): 13 0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19 #14 (120) 0x0 0 #15 (128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 168
  42. 42. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 131 //說明:以㆘配置48,取pool劃分為3個區塊,撥㆒個 //給客戶,留2個於list#5。Pool剩餘168-48*3=24. select (0~16, -1 to end): 5 0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置48,取pool劃分為3個區塊,撥㆒個 //給客戶,留2個於list#5。Pool剩餘168-48*3=24. select (0~16, -1 to end): 5 0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 SGI memory pool 運行實證運行實證運行實證運行實證.10 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 24
  43. 43. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 132 SGI memory pool 運行實證運行實證運行實證運行實證.11 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額 //撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小 //(我將它設定為10000),因此記憶體不足,於是反求諸己取80區塊(#9)回填pool,再以之當做72區塊給客戶,餘8備用 select (0~16, -1 to end): 8 0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額 //撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小 //(我將它設定為10000),因此記憶體不足,於是反求諸己取80區塊(#9)回填pool,再以之當做72區塊給客戶,餘8備用 select (0~16, -1 to end): 8 0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 8
  44. 44. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 133 SGI memory pool 運行實證運行實證運行實證運行實證.12 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額撥給list#0,然後爭取 //72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,於是反求諸己取88區塊(#10)回填pool,再以之當做72區塊給客戶,餘16備用。 select (0~16, -1 to end): 8 0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688 #0 (8) 0x25c2ae8 20 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額撥給list#0,然後爭取 //72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,於是反求諸己取88區塊(#10)回填pool,再以之當做72區塊給客戶,餘16備用。 select (0~16, -1 to end): 8 0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688 #0 (8) 0x25c2ae8 20 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 16
  45. 45. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 134 SGI memory pool 運行實證運行實證運行實證運行實證.13 //說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應㆒個, //於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4) //的挹注,但此要求已超越system heap大小(我將它設定為10000,後述), //因此記憶體不足,但反求諸己後仍得不到自由區塊,於是失敗。 //檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312, //各個free lists內亦可能有些連續自由區塊。 select (0~16, -1 to end): 14 out-of-memory -- jjhou simulation. //說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應㆒個, //於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4) //的挹注,但此要求已超越system heap大小(我將它設定為10000,後述), //因此記憶體不足,但反求諸己後仍得不到自由區塊,於是失敗。 //檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312, //各個free lists內亦可能有些連續自由區塊。 select (0~16, -1 to end): 14 out-of-memory -- jjhou simulation. #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 16
  46. 46. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 144 small object allocator in Loki Ref. MCD chap4, and Loki smallObj.cpp 4. SmallObject * 提供物件層次(object level)的服務 * 透通性 “ classes 繼承 SmallObject 即可享受服務 3. SmallObjAllocator * 能夠配置多種尺寸不同的小型物件,類似 std::alloc。 * 可根據參數而組態化(configurable) * 內含 vector<FixedAllocator> 2. FixedAllocator * 配置某固定大小的物件,類似㆒個 free-list。 * 內含 vector<Chunk>,因此可以無限擴充(多個) char[blockSize * blocks]; 1. Chunk * 根據某特定大小來配置物件。有物件個數㆖限。 * 類似㆒個元素個數受限的 free-list。 * Init() 時 new char[blockSize * blocks]; 而後呼叫 Reset() 將 每隔 blockSize 個 bytes 的 1st byte 設為流水號碼(1~blocks), 以此做為 index 來管理 blocks.
  47. 47. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 145 Loki small object allocator, overview f12 f16 f30 f32 f48 ... 負責 30 bytes 區塊 的配置和歸還 可由 client 自由添加 ㆒定會按區塊大小排序 負責 48 bytes 區塊 的配置和歸還 blocksAvailable_ firstAvailableBlock_ pData_ 1 16 bytes (blockSize_) m 1 m blocksAvailable_ firstAvailableBlock_ pData_ ... SmallObjAllocator 內含 vector<FixedAllocator> pool_; FixedAllocator 內含 vector<Chunk> Chunk chunkSize_ (bytes) numBlocks (blocks) 在 SGI STL ㆗,每㆒個 fixed size blocks 都被安置於 free list,並以其前 4 bytes 做為 singly linked list 所需的 pointer。在 Loki ㆗,每㆒個 fixed size blocks 被安置於 chunk,那是塊連續空 間,不會變大也不會變小。與 free list 對應的是 vector<Chunk>。 #0 #1 #2 #3 32bytes 32bytes 32bytes 32bytes 32bytes free_list[16] 0 第㆒塊傳 回給客戶 ...2 3 Loki 把index當做ptr來使用 (當然可以),在chunk內實 施的(配置或歸還動作), 完全是標準 list 動作。 SGI STL index backfront ... 只有當某尺寸的需求至少 出現㆒次才產出㆒個對應 的 FixedAllocator
  48. 48. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 146 雖然要求區塊的時間次 序是:20, 64, 40,代表 創建 FixedAllocator 的時 間順序,但置於 vector 內的位置次序卻是 20, 40,64,以區塊大小排序。 侯捷認為排序意義不大. Loki small object allocator, 1, allocate vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 101 1 pData_ 1 40 bytes per block. (40*102 =4080) 102 共 1 個 Chunks SmallObjAllocator myAlloc(2048,256); // 可應付小於 256 的各種大小的配置 void* p1 = (void*)myAlloc.Allocate(20); void* p4 = (void*)myAlloc.Allocate(64); void* p2 = (void*)myAlloc.Allocate(40); void* p3 = (void*)myAlloc.Allocate(300); // 大於 256,將轉交系統處理 void* p5 = (void*)myAlloc.Allocate(64); void* p6 = (void*)myAlloc.Allocate(64); void* p7 = (void*)myAlloc.Allocate(64); 60 4 pData_ 1 64 bytes per block 64 共 1 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 5 allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ indexindexindex chunk 大小 = 20*204= 4080 chunk size(沒被用到,是設計遺漏吧。預設使用4096) maxBlock p1 p2 p4,p5,p6,p7 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish
  49. 49. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 147 Loki small object allocator, 2, allocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks // 續㆖頁程式 void* ptr[100]; for (int i=0; i< 65; ++i) ptr[i] = (void*)myAlloc.Allocate(64); 0 64 pData_ 1 64 bytes per block 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 1 64 5 deallocChunk_ allocChunk_ index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish
  50. 50. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 148 Loki small object allocator, 3, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 1 1 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 1 64 // 續㆖頁程式 myAlloc.Deallocate(p5,64); // 註,其㆗運用deallocChunk_ 協助儘可能快速找到 // 「待刪區塊落在哪㆒個 chunk 內」 // 可觀察 SmallObj.cpp 的 VicinityFind() p5 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64start finish start finish start finish start finish start finish start finish SmallObjAllocator 內含 vector<FixedAllocator> start finish
  51. 51. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 149 Loki small object allocator, 4, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 2 2 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 1 64 1 4 // 續㆖頁程式 myAlloc.Deallocate(p6,64); p6 index index index deallocChunk_ allocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  52. 52. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 150 Loki small object allocator, 5, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 3 0 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 2 64 1 4 // 續㆖頁程式 myAlloc.Deallocate(p4,64); p4 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  53. 53. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 151 Loki small object allocator, 6, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 4 3 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 2 64 1 0 // 續㆖頁程式 myAlloc.Deallocate(p7,64); p7 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  54. 54. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 152 Loki small object allocator, 7, deallocate 102 0 pData_ 1 40 bytes per block 102 共 1 個 Chunks 64 4 pData_ 5 64 bytes per block 64 共 1 個 Chunks 204 0 pData_ 1 20 bytes per block 204 共 1 個 Chunks 0 1 2 3 // 續㆖頁程式 myAlloc.Deallocate(p1,20); myAlloc.Deallocate(p2,40); myAlloc.Deallocate(p3,300); for (int i=0; i< 65; ++i) myAlloc.Deallocate(ptr[i], 64); 真的釋放了㆒個 Chunk 還給系統 6 7 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  55. 55. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 153 Loki small object allocator, level 1 Ref. MCD chap4, and Loki smallObj.cpp class FixedAllocator { private: struct Chunk { void Init(blockSize,blocks); void* Allocate(_); void Deallocate(_,_); unsigned char* pData_; unsigned char firstAvailableBlock_, blocksAvailable_; }; ... }; class FixedAllocator { private: struct Chunk { void Init(blockSize,blocks); void* Allocate(_); void Deallocate(_,_); unsigned char* pData_; unsigned char firstAvailableBlock_, blocksAvailable_; }; ... }; blocksAvailable_ : 255 firstAvailableBlock_ : 0 pData_ 1 2 254 255 … 253 區塊(blocks)... Init(4,255); 4 bytes blocksAvailable_ : 254 firstAvailableBlock_ : 1 pData_ 2 254 255 … 253 區塊(blocks)... Allocate(4); ※ 以 array 管理,不若以 list 管理(如 SGI allocator)有彈性 ※ 區塊大小不需配合 alignment (但物件大小通常都由編譯器配合 alignment,那麼此㆒特性有何意義?) 這個 chunk 只負責供應 4byte block 現在,配置㆒個 4byte block index index pData_ = new unsigned char [blockSize * blocks]; 這表示每個chunk內的區 塊數最多只能256
  56. 56. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 154 Loki small object allocator, level 1, 實作實作實作實作 Ref. MCD chap4, and Loki smallObj.cpp void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks) { pData_ = new unsigned char[blockSize * blocks]; Reset(blockSize, blocks); } void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks) { pData_ = new unsigned char[blockSize * blocks]; Reset(blockSize, blocks); } void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks) { firstAvailableBlock_ = 0; blocksAvailable_ = blocks; unsigned char i = 0; unsigned char* p = pData_; for (; i != blocks; p += blockSize) //流水標示索引 *p = ++i; } void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks) { firstAvailableBlock_ = 0; blocksAvailable_ = blocks; unsigned char i = 0; unsigned char* p = pData_; for (; i != blocks; p += blockSize) //流水標示索引 *p = ++i; } void FixedAllocator::Chunk::Release() { delete[] pData_; //釋放自己 } void FixedAllocator::Chunk::Release() { delete[] pData_; //釋放自己 } void* FixedAllocator::Chunk::Allocate(std::size_t blockSize) { if (!blocksAvailable_) return 0; //此㆞無銀 unsigned char* pResult = //指向第㆒個可用區塊 pData_ + (firstAvailableBlock_ * blockSize); firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區塊的索引 --blocksAvailable_; return pResult; } void* FixedAllocator::Chunk::Allocate(std::size_t blockSize) { if (!blocksAvailable_) return 0; //此㆞無銀 unsigned char* pResult = //指向第㆒個可用區塊 pData_ + (firstAvailableBlock_ * blockSize); firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區塊的索引 --blocksAvailable_; return pResult; } void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize) { unsigned char* toRelease = static_cast<unsigned char*>(p); *toRelease = firstAvailableBlock_; firstAvailableBlock_ = static_cast<unsigned char>( (toRelease - pData_) / blockSize); ++blocksAvailable_; //可用區塊數加 1 } void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize) { unsigned char* toRelease = static_cast<unsigned char*>(p); *toRelease = firstAvailableBlock_; firstAvailableBlock_ = static_cast<unsigned char>( (toRelease - pData_) / blockSize); ++blocksAvailable_; //可用區塊數加 1 } 1 2 3 4 ... ... 254 1 pData_ 255 2 配置第㆒個區塊 後的狀態: 253 2 pData_ 255 配置第㆓個區塊 後的狀態: 254 0 pData_ 2 255 歸還第㆒個區塊 後的狀態: 1 23
  57. 57. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 155 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2 class FixedAllocator { private: std::size_t blockSize_; // n unsigned char numBlocks_; // m typedef std::vector<Chunk> Chunks; Chunks chunks_; Chunk* allocChunk_; //初值是0 Chunk* deallocChunk_; //初值是0 //For ensuring proper copy semantics mutable const FixedAllocator* prev_; mutable const FixedAllocator* next_; ... }; class FixedAllocator { private: std::size_t blockSize_; // n unsigned char numBlocks_; // m typedef std::vector<Chunk> Chunks; Chunks chunks_; Chunk* allocChunk_; //初值是0 Chunk* deallocChunk_; //初值是0 //For ensuring proper copy semantics mutable const FixedAllocator* prev_; mutable const FixedAllocator* next_; ... }; blocksAvailable_ firstAvailableBlock_ pData_ 1 n bytes blocksAvailable_ firstAvailableBlock_ pData_ m 1 m blocksAvailable_ firstAvailableBlock_ pData_ 1 m chunks_front back FixedAllocator f16(16); // 16 表示區塊大小 // 區塊數 m 為以㆘㆔者之㆒: // m=C/n 或 256 或 8*n // 其㆗ C 為 chunk 大小,預設4096 FixedAllocator f16(16); // 16 表示區塊大小 // 區塊數 m 為以㆘㆔者之㆒: // m=C/n 或 256 或 8*n // 其㆗ C 為 chunk 大小,預設4096 ㆒個 FixedAllocator object 功能就像 SGI allocator 的㆒個 free list 似的。其內可有多個 'chunk',每個 'chunk' 管理 m 個 n bytes 區塊。 有趣的是,Loki 使用 DEFAULT_CHUNK_SIZE 而非 SmallObjAllocator::chunkSize_。後者完全沒被用到 (a bug) 以 cache 和 locality 強化 chunks_ 的搜尋速度 只用於 ctor, copy ctor, dtor, Q:為什麼需要特別考慮 copy semantics 呢? 表現於 allocChunk_ 和 deallocChunk_
  58. 58. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 156 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #01 void* FixedAllocator::Allocate() #02 { #03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0) #04 { //目前沒有標定chunk或該chunk已無可用區塊 #05 Chunks::iterator i = chunks_.begin(); //從頭找起 #06 for (;; ++i) #07 { #08 if (i == chunks_.end()) //到達尾端,都沒找著 #09 { #10 // Initialize #11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於末端 #12 Chunk& newChunk = chunks_.back(); //指向末端 chunk #13 newChunk.Init(blockSize_, numBlocks_); //設好狀態 #14 allocChunk_ = &newChunk; //標定,稍後將對此 new chunk 取區塊 #15 deallocChunk_ = &chunks_.front(); //另㆒標定 #16 break; #17 } #18 if (i->blocksAvailable_ > 0) #19 { //current chunk 有可用區塊 #20 allocChunk_ = &*i; //取其位址 #21 break; //不找了,退出迴圈 #22 } #23 } #24 } #25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區塊 #26 } #01 void* FixedAllocator::Allocate() #02 { #03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0) #04 { //目前沒有標定chunk或該chunk已無可用區塊 #05 Chunks::iterator i = chunks_.begin(); //從頭找起 #06 for (;; ++i) #07 { #08 if (i == chunks_.end()) //到達尾端,都沒找著 #09 { #10 // Initialize #11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於末端 #12 Chunk& newChunk = chunks_.back(); //指向末端 chunk #13 newChunk.Init(blockSize_, numBlocks_); //設好狀態 #14 allocChunk_ = &newChunk; //標定,稍後將對此 new chunk 取區塊 #15 deallocChunk_ = &chunks_.front(); //另㆒標定 #16 break; #17 } #18 if (i->blocksAvailable_ > 0) #19 { //current chunk 有可用區塊 #20 allocChunk_ = &*i; //取其位址 #21 break; //不找了,退出迴圈 #22 } #23 } #24 } #25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區塊 #26 } //找遍每個chunk 直至找到擁有 free 區塊者. 每在某chunk找到可用區塊,㆘次就從該 chunk 找起 找到適當chunk就記錄㆘來, ㆘次就從那兒找起 假設 blockSize 是 20,blocks 個數是 4096 / 20 = 204, 不大於 256, OK. 假設 blockSize 是 16,blocks 個數是 4096 / 16 = 256, 不大於 256, OK ref. VC's <limits.h> 其值為 0xFF FixedAllocator::FixedAllocator(std::size_t blockSize) : blockSize_(blockSize) , deallocChunk_(0) , allocChunk_(0) { prev_ = next_ = this; //以㆘計算並設定 numBlocks_; std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize; if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX; else if (numBlocks == 0) numBlocks = 8 * blockSize; numBlocks_ = static_cast<unsigned char>(numBlocks); } FixedAllocator::FixedAllocator(std::size_t blockSize) : blockSize_(blockSize) , deallocChunk_(0) , allocChunk_(0) { prev_ = next_ = this; //以㆘計算並設定 numBlocks_; std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize; if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX; else if (numBlocks == 0) numBlocks = 8 * blockSize; numBlocks_ = static_cast<unsigned char>(numBlocks); } 1 2 3 void FixedAllocator::Deallocate(void* p) { deallocChunk_ = VicinityFind(p); DoDeallocate(p); } void FixedAllocator::Deallocate(void* p) { deallocChunk_ = VicinityFind(p); DoDeallocate(p); } (內部函式, ㆘頁) 執行 deallocation. 假設 deallocChunk_ 已經指向正確的 chunk. (內部函式, ㆘頁) 找出相對於某指標的 那個 chunk, 採用㆒種高效搜尋法.
  59. 59. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 157 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #01 // FixedAllocator::VicinityFind (internal) #02 // Finds the chunk corresponding to a pointer, using an efficient search #03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p) #04 { #05 const std::size_t chunkLength = numBlocks_ * blockSize_; #06 #07 Chunk* lo = deallocChunk_; #08 Chunk* hi = deallocChunk_ + 1; #09 Chunk* loBound = &chunks_.front(); #10 Chunk* hiBound = &chunks_.back() + 1; #11 #12 for (;;) { //兵分兩路 #13 if (lo) { //㆖路未到盡頭 #14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength) #15 return lo; //p指標值落於 lo 區間內 #16 if (lo == loBound) lo = 0; //到頂了,讓迴圈結束 #17 else --lo; //否則往 lo 的 front 繼續找 #18 } #19 #20 if (hi) { //㆘路未到盡頭 #21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength) #22 return hi; //p指標值落於 hi 區間內 #23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續找 #24 } //若到最尾則讓迴圈結束 #25 } #26 return 0; #27 } #01 // FixedAllocator::VicinityFind (internal) #02 // Finds the chunk corresponding to a pointer, using an efficient search #03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p) #04 { #05 const std::size_t chunkLength = numBlocks_ * blockSize_; #06 #07 Chunk* lo = deallocChunk_; #08 Chunk* hi = deallocChunk_ + 1; #09 Chunk* loBound = &chunks_.front(); #10 Chunk* hiBound = &chunks_.back() + 1; #11 #12 for (;;) { //兵分兩路 #13 if (lo) { //㆖路未到盡頭 #14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength) #15 return lo; //p指標值落於 lo 區間內 #16 if (lo == loBound) lo = 0; //到頂了,讓迴圈結束 #17 else --lo; //否則往 lo 的 front 繼續找 #18 } #19 #20 if (hi) { //㆘路未到盡頭 #21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength) #22 return hi; //p指標值落於 hi 區間內 #23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續找 #24 } //若到最尾則讓迴圈結束 #25 } #26 return 0; #27 } blocksAvailable_ firstAvailableBlock_ pData_ 1 m lo = deallocChunk_lo = deallocChunk_ 2 blocksAvailable_ firstAvailableBlock_ pData_ 1 m 2 hi = deallocChunk_+1hi = deallocChunk_+1 如果找不到 就往 front 找去 如果找不到 就往 back 找去 front back 如果這 p 並非當初從此系統㆗取得,這裡便肯定找不到對應的 chunk, 於是永遠不以 return 跳出 for loop,於是陷於無窮迴路之㆗。
  60. 60. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 158 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #0001 void FixedAllocator::DoDeallocate(void* p) #0002 { #0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory #0004 deallocChunk_->Deallocate(p, blockSize_); #0005 #0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) { #0007 // deallocChunk_ 目前完全 free, 該 release 它嗎? #0008 #0009 Chunk& lastChunk = chunks_.back(); //指向最後㆒個 #0010 //如果 "最後㆒個" 就是 "當前回收者" #0011 if (&lastChunk == deallocChunk_) { #0012 // 檢查是否擁有兩個 last chunks empty #0013 // 這只需往 front 方向看前㆒個 chunk 便知. #0014 if (chunks_.size() > 1 && #0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) { #0016 // 是的有 2 free chunks, 拋棄最後㆒個 #0017 lastChunk.Release(); #0018 chunks_.pop_back(); #0019 allocChunk_ = deallocChunk_ = &chunks_.front(); #0020 } #0021 return; #0022 } #0023 #0001 void FixedAllocator::DoDeallocate(void* p) #0002 { #0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory #0004 deallocChunk_->Deallocate(p, blockSize_); #0005 #0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) { #0007 // deallocChunk_ 目前完全 free, 該 release 它嗎? #0008 #0009 Chunk& lastChunk = chunks_.back(); //指向最後㆒個 #0010 //如果 "最後㆒個" 就是 "當前回收者" #0011 if (&lastChunk == deallocChunk_) { #0012 // 檢查是否擁有兩個 last chunks empty #0013 // 這只需往 front 方向看前㆒個 chunk 便知. #0014 if (chunks_.size() > 1 && #0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) { #0016 // 是的有 2 free chunks, 拋棄最後㆒個 #0017 lastChunk.Release(); #0018 chunks_.pop_back(); #0019 allocChunk_ = deallocChunk_ = &chunks_.front(); #0020 } #0021 return; #0022 } #0023 blocksAvailable_ firstAvailableBlock_ pData_ 1 m deallocChunk_deallocChunk_ 2 待刪之 p 已確定 落在這個 chunk #0024 if (lastChunk.blocksAvailable_ == numBlocks_) { #0025 //2 free chunks, 拋棄最後㆒個 #0026 lastChunk.Release(); #0027 chunks_.pop_back(); #0028 allocChunk_ = deallocChunk_; #0029 } #0030 else { #0031 //將free chunk 與後㆒個chunk 對調 #0032 std::swap(*deallocChunk_, lastChunk); #0033 allocChunk_ = &chunks_.back(); #0034 } #0035 } #0036 } #0024 if (lastChunk.blocksAvailable_ == numBlocks_) { #0025 //2 free chunks, 拋棄最後㆒個 #0026 lastChunk.Release(); #0027 chunks_.pop_back(); #0028 allocChunk_ = deallocChunk_; #0029 } #0030 else { #0031 //將free chunk 與後㆒個chunk 對調 #0032 std::swap(*deallocChunk_, lastChunk); #0033 allocChunk_ = &chunks_.back(); #0034 } #0035 } #0036 } 這裡殺掉deallocChunk_ 似乎較好, 這就可以保持最後㆒個 chunk 為 free 以目前實作來看,似乎有可能某種情況 ㆘不歸還chunk 給系統。試證之。
  61. 61. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 159 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 3 class SmallObjAllocator { private: typedef std::vector<FixedAllocator> Pool; Pool pool_; FixedAllocator* pLastAlloc_; FixedAllocator* pLastDealloc_; std::size_t chunkSize_; std::size_t maxObjectSize_; public: void* Allocate(std::size_t nBytes); void Deallocate(void* p, std::size_t size); }; class SmallObjAllocator { private: typedef std::vector<FixedAllocator> Pool; Pool pool_; FixedAllocator* pLastAlloc_; FixedAllocator* pLastDealloc_; std::size_t chunkSize_; std::size_t maxObjectSize_; public: void* Allocate(std::size_t nBytes); void Deallocate(void* p, std::size_t size); }; f48 f32 f30 f16 f12 pool_ 這有點像 SGI allocator 的 free_list[16] 但此處個數不限 16,區塊 大小亦不限為 8 倍數 ... 負責 12 bytes 區塊 的配置和歸還 負責 32 bytes 區塊 的配置和歸還 負責 48 bytes 區塊 的配置和歸還 SmallObjAllocator myAlloc(2048,256); // 第㆒參數表 ChunkSize // 第㆓參數表 maxObjSize // 這樣的彈性表示 // 它可產出「區塊大小低於 maxObjSize」 // 的任何 FixedAllocator 出來。 SmallObjAllocator myAlloc(2048,256); // 第㆒參數表 ChunkSize // 第㆓參數表 maxObjSize // 這樣的彈性表示 // 它可產出「區塊大小低於 maxObjSize」 // 的任何 FixedAllocator 出來。 以 cache 和 binary search 強化 pool_ 的搜尋速度 每個元素都是 FixedAllocator
  62. 62. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 160 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 3, 實作實作實作實作 SmallObjAllocator::SmallObjAllocator( std::size_t chunkSize, std::size_t maxObjectSize) : pLastAlloc_(0), pLastDealloc_(0) , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) { } SmallObjAllocator::SmallObjAllocator( std::size_t chunkSize, std::size_t maxObjectSize) : pLastAlloc_(0), pLastDealloc_(0) , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) { } #01 void* SmallObjAllocator::Allocate(std::size_t numBytes) #02 { #03 if (numBytes > maxObjectSize_) //區塊大小超過服務範圍 #04 return operator new(numBytes); //由 global operator new 服務 #05 #06 if (pLastAlloc_ && //㆖次那 FixedAllocator 合適否 #07 pLastAlloc_->BlockSize() == numBytes) { #08 return pLastAlloc_->Allocate(); //從㆗取區塊 #09 } //至此表示㆖次那個 FixedAllocator 不適當 #10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出適當位置 #11 if (i == pool_.end() || i->BlockSize() != numBytes) { #12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚無 chunk) 並插入vector #13 pLastDealloc_ = &*pool_.begin(); #14 } #15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位址 #16 return pLastAlloc_->Allocate(); //從㆗取區塊 #17 } #01 void* SmallObjAllocator::Allocate(std::size_t numBytes) #02 { #03 if (numBytes > maxObjectSize_) //區塊大小超過服務範圍 #04 return operator new(numBytes); //由 global operator new 服務 #05 #06 if (pLastAlloc_ && //㆖次那 FixedAllocator 合適否 #07 pLastAlloc_->BlockSize() == numBytes) { #08 return pLastAlloc_->Allocate(); //從㆗取區塊 #09 } //至此表示㆖次那個 FixedAllocator 不適當 #10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出適當位置 #11 if (i == pool_.end() || i->BlockSize() != numBytes) { #12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚無 chunk) 並插入vector #13 pLastDealloc_ = &*pool_.begin(); #14 } #15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位址 #16 return pLastAlloc_->Allocate(); //從㆗取區塊 #17 } #01 // Deallocates memory previously allocated with Allocate #02 // (undefined behavior if you pass any other pointer) #03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt #04 { //若超過服務範圍,轉給 global operator delete() 去做 #05 if (numBytes > maxObjectSize_) return operator delete(p); #06 //標定者為適當之 FixedAllocator,令它 Deallocate(p). #07 if (pLastDealloc_ && pLastDealloc_->BlockSize() == #08 numBytes) { #09 pLastDealloc_->Deallocate(p); #10 return; #11 } //至此,以㆘搜尋最適當之 FixedAllocator(㆒定有) #12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end( #13 numBytes); #14 pLastDealloc_ = &*i; //取其位址 #15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p) #16 } #01 // Deallocates memory previously allocated with Allocate #02 // (undefined behavior if you pass any other pointer) #03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt #04 { //若超過服務範圍,轉給 global operator delete() 去做 #05 if (numBytes > maxObjectSize_) return operator delete(p); #06 //標定者為適當之 FixedAllocator,令它 Deallocate(p). #07 if (pLastDealloc_ && pLastDealloc_->BlockSize() == #08 numBytes) { #09 pLastDealloc_->Deallocate(p); #10 return; #11 } //至此,以㆘搜尋最適當之 FixedAllocator(㆒定有) #12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end() #13 numBytes); #14 pLastDealloc_ = &*i; //取其位址 #15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p) #16 } Q: lower_bound() 操作的對象應該是sorted,也就需要 op<, 但 FixedAllocator 沒有 op< 呀! A: 有的,是個in-class inline: bool operator<(size_t rhs) const { return blockSize_ < rhs; }
  63. 63. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 161 MFC's CPlex CPlex object this nMax * cbElement void* CPlex::data() { return this+1; } ... CPlex 扮演 memory pool 角色 pNext pNext CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement) { //配置記憶體後順便鏈接起來. CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement]; p->pNext = pHead; pHead = p; // change head (adds in reverse order) return p; } CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement) { //配置記憶體後順便鏈接起來. CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement]; p->pNext = pHead; pHead = p; // change head (adds in reverse order) return p; } void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還每㆒長條 } void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還每㆒長條 } struct CPlex { CPlex* pNext; #if (_AFX_PACKING >= 8) DWORD dwReserved[1]; // align on 8 byte boundary #endif void* data() { return this+1; } static CPlex* PASCAL Create(CPlex*& head, UINT nMax, UINT cbElement); void FreeDataChain(); // free this one and links }; struct CPlex { CPlex* pNext; #if (_AFX_PACKING >= 8) DWORD dwReserved[1]; // align on 8 byte boundary #endif void* data() { return this+1; } static CPlex* PASCAL Create(CPlex*& head, UINT nMax, UINT cbElement); void FreeDataChain(); // free this one and links }; 全 部 清 除 每次配置㆒長條 Ref. MFC's afxplex_.h & plex.cpp
  64. 64. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 162 MFC's CPtrList CPlex object pos pospos CPtrList object : GetHeadPosition() GetNext() m_pNodeHead m_pNodeTail m_pNodeFree m_pBlocks m_nBlockSize (default 10) CNode object in free list void* CPlex::data() { return this+1; } used CNode object (in "Ptr-List") interface implementation + m_nBlockSize - 1 ... ... CPlex扮演 memory pool 角色 個數:m_nCount Ref. MFC's afxcoll.h & list_p.cpp CPtrList 的每個節點都來自 pooled allocator CPlex,因此 都不帶 cookie。
  65. 65. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 166 Boost's Pool 主要有主要有主要有主要有 6 files, poolfwd.hpp宣告各個宣告各個宣告各個宣告各個 classes #include <boost/config.hpp> // for workarounds #include <cstddef> // std::size_t #include <boost/pool/detail/mutex.hpp> template <typename SizeType = std::size_t> class simple_segregated_storage; struct default_user_allocator_new_delete; struct default_user_allocator_malloc_free; template <typename UserAllocator = default_user_allocator_new_delete> class pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete> class object_pool; template <typename Tag, unsigned RequestedSize, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> struct singleton_pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> class pool_allocator; #include <boost/config.hpp> // for workarounds #include <cstddef> // std::size_t #include <boost/pool/detail/mutex.hpp> template <typename SizeType = std::size_t> class simple_segregated_storage; struct default_user_allocator_new_delete; struct default_user_allocator_malloc_free; template <typename UserAllocator = default_user_allocator_new_delete> class pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete> class object_pool; template <typename Tag, unsigned RequestedSize, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> struct singleton_pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> class pool_allocator; 預設使用這個,㆘頁 各個 classes 定義於... poolfwd.hpp 另有個 boost::details::PODptr 也定義於 pool.hpp
  66. 66. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 168 Boost Pool 的㆕個主要的㆕個主要的㆕個主要的㆕個主要 classes 的的的的關係關係關係關係 pool simple_segregated_storage template <typename UserAllocator=…> class pool: protected simple_segregated_storage< typename UserAllocator::size_type> template <typename SizeType> class simple_segregated_storage template <typename T, typename UserAllocator=…> class object_pool: protected pool<UserAllocator>object_pool template <typename T, typename UserAllocator, typename Mutex, unsigned NextSize> class pool_allocator default_user_allocator_malloc_freedefault_user_allocator_malloc_free default_user_allocator_new_deletedefault_user_allocator_new_delete T T T template <typename Tag, unsigned RequestedSize, typename UserAllocator, typename Mutex, unsigned NextSize> struct singleton_pool #store() #nextof(void* const):void*& malloc():T* free(T*) construct():T* destroy(T*) #first : void*指向 free-list 的 第㆒個chunk. #nextof(void* const) segregate(void*,size_t, size_t, void*=0) add_block(void*,size_t, size_t) malloc() free(void*) list:PODptr requested_size //區塊大小 next_size=32 //區塊數 全都沒有全都沒有全都沒有全都沒有 virtual functions PODptr ptr:char* sz:size_type
  67. 67. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 173 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 boost::pool::ordered_malloc_need_resize()的動作: 首先計算 partition_size為 8,再計算 POD_size為264,然後配置 264 bytes 大區塊, 然後設立㆒個局部變數 node(是個 PODptr): ptr 264 264 = 32*8 + 4 + 4 (這㆒整個大區塊就是所謂POD) 0 0 0 32 8 fooPool 然後切割、調整㆘次配置之小區塊數(兩倍增長)、整合: 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0 0 0 node class Foo { public: Foo(char c, long v) : type(c),value(v) { } private: char type; long value; };
  68. 68. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 174 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 最後,給出㆒個(也就是 ordered_malloc_need_resize() 內部呼叫 store().malloc() 造成): 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0 ㆘次用戶再呼叫 fooPool.construct() 或 foolPool.malloc() 時,直接給出: 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0
  69. 69. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 175 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 配置 32 次之後,變成(注意 first 指標又變成 null): 0 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0 再次配置時,所有小區塊都已用完,於是串接另㆒個大區塊(依然切割、調整㆘次 配置之小區塊數、整合): 520 128 8 fooPool ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264 0x007E0490 0x007E0250
  70. 70. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 176 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 配置㆒個後: 520 128 8fooPool ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264 配置多個後: 0 520 128 8fooPool ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264
  71. 71. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 177 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264 再次配置時,所有小區塊都已用完,於是串接另㆒個大區塊(依然切割、調整㆘次 配置之小區塊數、整合): ... 1032 bytes = 128*8 + 4 + 4 128個小區塊 520 007E0250 007E0490 007D09A4 1032 256 8 fooPool 表示這個 pool 為 8-bytes 服務 ㆘次配置的 chunks 個數。 這裡的記錄將會是 64,128,256,... 目前的 block 大小。 這裡的記錄將會是 264,520,1032,... 指向目前可用的 chunk. 指向目前使用㆗的 block 1 2 3 4 5
  72. 72. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 179 ㆔層㆔層㆔層㆔層. Memory Deallocation void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } 如果目前有㆒個 block,已分配 5 chunks 出去, first 指向 6th chunk: 264 64 8 ... 0 0 那麼當歸還 2rd chunk 指標時: 264 64 8 ... 0 0 歸還 4th chunk 指標時: 264 64 8 ... 0 0 p2 p4 p4 iter 最後停在這裡, 傳回被 loc 承接 滿足這個條件 first first first 這個迴圈將找出first之後 的㆒個 "適當" 位置 滿足這個條件 這個algorithm㆒定和 "blocks 的串接永遠從低 位址至高位址排列" 有關
  73. 73. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 180 ㆔層㆔層㆔層㆔層. Memory Deallocation void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } 如果目前有 3 個 blocks 如㆘: 1032 256 8 ... 32個小區塊 0 0 ... 64個小區塊 264 ... 128個小區塊 520 007E0250 007E0490 007D09A4 0 p4 歸還 p4 會變成這樣 (見㆘頁) iter 最後停在這裡, 傳回被 loc 承接
  74. 74. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 181 ㆔層㆔層㆔層㆔層. Memory Deallocation void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } 1032 256 8 ...0 32個小區塊 0 0 ... 64個小區塊 264 ... 128個小區塊 520 007E0250 007E0490 007D09A4 歸還 pp1 會變成這樣 見㆘頁 pp1 iter 最後停在這裡＀

×