C++的SGI版本的STL二級空間配置器實現(基於記憶體池的實現方式)

来源:https://www.cnblogs.com/woden3702/archive/2022/05/27/16318018.html
-Advertisement-
Play Games

位運算 與& 或| 異或^ 左移<< 右移>> \(x<<y=x·2^{y}\) \(x>>y=\frac{x}{2^{y}}\) \(2a+1=(a<<1)|1\) \(a\)%\(2=a\)&\(1\) st表 當st表合併的複雜度為$O(1)$時,st表構建的複雜度為$O(nlogn)$,查詢 ...


C++STL中的空間配置器只有一種,是同過底層的malloc和free實現的,空間配置器中有四種方法:

SGI STL中有兩種空間配置器,一級allocator是與stl一致的malloc和free的方式,二級allocator是通過記憶體池的方式實現的。

SGI STL中的vector容器的模板中用到了空間配置器,預設用的是二級allocator。該容器底層存儲對象的構造和析構是通過全局的函數模板construct和destroy實現的。這裡我們著重研究allocator中的allocatedeallocate方法。

allocate:

// breaks if we make these template class members:
  enum {_ALIGN = 8};
  enum {_MAX_BYTES = 128};
  enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN  
/* __n must be > 0      */
	//分配大小為__n的記憶體
  static void* allocate(size_t __n)
  {
    void* __ret = 0;

    if (__n > (size_t) _MAX_BYTES) {//如果需要的記憶體塊大小超過最大記憶體,則按照標準庫的方式分配記憶體
      __ret = malloc_alloc::allocate(__n);//調用一級allocator
    }
    else {
      _Obj* __STL_VOLATILE* __my_free_list//是一個double*的類型,指向數組的位置,解引用之後是數組中元素的值
          = _S_free_list + _S_freelist_index(__n);//根據_S_freelist_index(__n)定位數組中chunk塊的位置
        //_S_free_list就是上圖中的數組
        
      // Acquire the lock here with a constructor call.
      // This ensures that it is released in exit or during stack
      // unwinding.
#     ifndef _NOTHREADS
      /*REFERENCED*/
      _Lock __lock_instance;//上鎖
#     endif
      _Obj* __RESTRICT __result = *__my_free_list;//result是數組中的__Obj*
      if (__result == 0)//如果數組中的元素未初始化
        __ret = _S_refill(_S_round_up(__n));//構建鏈表,返回鏈表的地址
      else {//已經初始化
        *__my_free_list = __result -> _M_free_list_link;//指向下一個節點
        __ret = __result;//返回下一個節點的地址
      }
    }

    return __ret;
  };

指針的加法操作註意事項:

​ 指針類型占用的記憶體多大,其指針加一就會偏移多少位元組。舉個例子:char類型只占1個位元組,那麼char* +1就只偏移一個位元組,指向下一個記憶體地址;int類型占4個位元組,int* +1就會偏移4個位元組,指向4個位元組後的記憶體地址。

_S_refill函數:

/* Returns an object of size __n, and optionally adds to size __n free list.*/
/* We assume that __n is properly aligned.                                */
/* We hold the allocation lock.                                         */
template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);//分配起始位置的地址
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return(__chunk);//如果數量為1,直接返回當前塊
    __my_free_list = _S_free_list + _S_freelist_index(__n);//指向數組塊的位置,這裡先以 __n=8 為例

    /* Build free list in chunk */
      __result = (_Obj*)__chunk;//result加一次可以跳到下個節點
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);//使數組中的第一個元素指向第二個chunk
      for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);//指向下一個節點
        if (__nobjs - 1 == __i) {//到鏈表最後節點
            __current_obj -> _M_free_list_link = 0;//使next節點等於nullptr表示最後一個節點
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return(__result);//返回鏈表首節點地址
}

_S_chunk_alloc(size_t __size, int& __nobjs):

/* We allocate memory in large chunks in order to avoid fragmenting     */
/* the malloc heap too much.                                            */
/* We assume that size is properly aligned.                             */
/* We hold the allocation lock.                                         */
template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, 
                                                            int& __nobjs)
{//還是以nobjs為20,size為8來假設
    char* __result;
    size_t __total_bytes = __size * __nobjs;//160
    size_t __bytes_left = _S_end_free - _S_start_free;//0 兩者初始化都為0 //第二次_1 __bytes_left=320

    if (__bytes_left >= __total_bytes) {//第二次_2 返回開闢的記憶體
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    } else if (__bytes_left >= __size) {//如果想要申請其他大小的chunk塊,可能會調用此方法在上一步申請的備用記憶體中查找有無可用的記憶體
        __nobjs = (int)(__bytes_left/__size);
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    } else {//初始化_1首先進入該分支
        size_t __bytes_to_get = 
	  2 * __total_bytes + _S_round_up(_S_heap_size >> 4);//_S_heap_size初始為0,初始化__bytes_to_get=320
        // Try to make use of the left-over piece.
        if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                        _S_free_list + _S_freelist_index(__bytes_left);

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }
        _S_start_free = (char*)malloc(__bytes_to_get);//初始化_2跳到這一步
        if (0 == _S_start_free) {//如果開闢記憶體失敗
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
	    _Obj* __p;
            // Try to make do with what we have.  That can't
            // hurt.  We do not try smaller requests, since that tends
            // to result in disaster on multi-process machines.
            for (__i = __size;
                 __i <= (size_t) _MAX_BYTES;
                 __i += (size_t) _ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (0 != __p) {
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    return(_S_chunk_alloc(__size, __nobjs));
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
	    _S_end_free = 0;	// In case of exception.
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
            // This should either throw an
            // exception or remedy the situation.  Thus we assume it
            // succeeded.
        }
        _S_heap_size += __bytes_to_get;//初始化_3 _S_heap_size=320
        _S_end_free = _S_start_free + __bytes_to_get;//_S_end_free是 char*類型,開闢一塊320位元組的記憶體塊
        return(_S_chunk_alloc(__size, __nobjs));//初始化_4 遞歸調用自己
    }
}

deallocate

 /* __p may not be 0 */
  static void deallocate(void* __p, size_t __n)
  {
    if (__n > (size_t) _MAX_BYTES)//如果大於_MAX_BYTES,就用標準庫的方法來
      malloc_alloc::deallocate(__p, __n);
    else {
      _Obj* __STL_VOLATILE*  __my_free_list
          = _S_free_list + _S_freelist_index(__n);//取數組中的元素
      _Obj* __q = (_Obj*)__p;

      // acquire lock
#       ifndef _NOTHREADS
      /*REFERENCED*/
      _Lock __lock_instance;
#       endif /* _NOTHREADS */
      __q -> _M_free_list_link = *__my_free_list;
      *__my_free_list = __q;//回收原來數組Obj*指向的記憶體
      // lock is released here
    }
  }


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 深居內陸的人們,大概每個人都有過大海之夢吧。夏日傍晚在沙灘漫步奔跑;或是在海上衝浪游泳;或是在海島游玩探險;亦或靜待日出日落……本文使用 React + Three.js 技術棧,實現 3D 海洋和島嶼,主要包含知識點包括:Tone Mapping、Water 類、Sky 類、Shader 著色、S... ...
  • 大家好,我是三友,這篇文章想來跟大家來探討一下,在Java中已經提供了併發安全的集合,為什麼有的場景還需要使用讀寫鎖,直接用併發安全的集合難道不行麽? 在java中,併發安全的集合有很多,這裡我就選用常見的CopyOnWriteArrayList為例,來說明一下讀寫鎖的價值到底提現在哪。 CopyO ...
  • 背景 對外服務的介面為了安全起見,往往需要進行相應的安全處理:數據加密傳輸和身份認證。數據加密傳輸有對稱加密和非對稱加密兩種,為了更加安全起見採用非對稱加密比較好些,身份認證則採用數字簽名可以實現。 程式流程 核心代碼 客戶端 package openapi.client.sdk; import c ...
  • 📕深入學習C++還必須掌握的基礎 掌握形參帶預設的函數 1.給預設值方向:從右向左給預設值; 2.調用效率:如果傳預設值或者立即數(不需要從容器或記憶體取取的數字)的話都是直接將數字直接push進棧;沒有mov彙編指令的操作;(面試回答要往彙編上描述) 3.預設值給的地方:定義和聲明處均可以給預設值 ...
  • argparse是深度學習項目調參時常用的python標準庫,使用argparse後,我們在命令行輸入的參數就可以以這種形式python filename.py --lr 1e-4 --batch_size 32來完成對常見超參數的設置。,一般使用時可以歸納為以下三個步驟 使用步驟: 創建Argum ...
  • 前言 emmmm 沒什麼說的,想說的都在代碼里 環境使用 Python 3.8 解釋器 3.10 Pycharm 2021.2 專業版 selenium 3.141.0 本次要用到selenium模塊,所以請記得提前下載好瀏覽器驅動,配置好環境 對於本篇文章有疑問的同學可以加【資料白嫖、解答交流群: ...
  • 1.github上上傳項目(略) 2.在sonatype上註冊賬號 https://issues.sonatype.org/secure/Dashboard.jspa 註意記住用戶名和密碼 3.在sonatype創建問題 4.新建完後客服會給提示 主要是要求:groupId要合理,需要按照要求在gi ...
  • 堆結構是一種數組對象,是一棵完全二叉樹。 性質 若當前節點編號為i,父結點則為i/2,左孩子為2i,右孩子為2i+1。 堆的結點數$\le$數組長度len 下圖為一個大根堆:每個結點均小於其父結點,樹根是堆中最大的結點,小根堆反之。 添加 往堆中添加一個元素。 重覆n次添加操作,即可建立一個小根堆。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...