實現定長的內存池

池化技術

????????所謂的池化技術,就是程序預先向系統申請過量的資源,然后自己管理起來,以備不時之需。這個操作的價值就是,如果申請與釋放資源的開銷較大,提前申請資源并在使用后并不釋放而是重復利用,能夠提高程序運行效率和減少開銷。

? ? ? ? 在計算機領域,池化技術有非常多的應用場景,如內存池、連接池、線程池和對象池等。以服務器中的線程池為例,它的主要思想是:預先啟動一批線程,讓它們先進入睡眠狀態,當有客戶端請求到來時,喚醒一個線程進行處理,并在處理完請求后,繼續睡眠,等待下一次被喚醒。

什么是定長池

? ? ? ? 我們C語言中使用的malloc實際上是標準庫的函數,底層實現實際上就使用了內存池技術,它支持根據我們的需求分配不同大小的內存空間,而我們今天要設計的定長池則每次只能分配固定大小的空間,在頻繁申請大小相同的空間的情況下,效率比malloc更優秀。

系統調用

? ? ? ? 在windows環境下進行開發,所以使用windows內存申請的API:

VirtualAlloc

在進程的虛擬地址空間中分配或保留內存

#include <windows.h>LPVOID VirtualAlloc
(LPVOID IpAddress,      // 要分配的內存區域的地址SIZE_T dwSize,         // 分配的大小DWORD  flAllocationType,// 分配的類型DWORD  flProtect       // 該內存的初始保護屬性
);

參數解釋:

lpAddress:指定要分配的內存區域的起始地址。如果此參數為nullptr,則系統會自動決定分配內存區域的位置,并且按64KB向上取整。

dwSize:指定要分配或保留的區域的大小,以字節為單位。系統會根據這個大小一直分配到下頁的邊界。

flAllocationType:指定分配類型,可以是指定或合并以下標志:

  • MEM_COMMIT:為指定地址空間提交物理內存。
  • MEM_RESERVE:保留指定地址空間,不分配物理內存。這樣可以阻止其他內存分配函數(如malloc和LocalAlloc等)再使用已保留的內存范圍,直到它被釋放。
  • MEM_TOP_DOWN:在盡可能高的地址分配內存。
  • MEM_LARGE_PAGES:分配內存時使用大頁面支持。大小和對齊必須是一個大頁面的最低倍數。

flProtect:指定被分配區域的訪問保護方式。可能的值包括:

  • PAGE_READWRITE:區域可以執行代碼,應用程序可以讀寫該區域。
  • PAGE_READONLY:區域為只讀。如果應用程序試圖訪問區域中的頁,將會被拒絕訪問。
  • PAGE_NOACCESS:任何訪問該區域的操作將被拒絕。
  • PAGE_GUARD:區域第一次被訪問時進入一個STATUS_GUARD_PAGE異常,這個標志要和其他保護標志合并使用。
  • PAGE_NOCACHE:RAM中的頁映射到該區域時將不會被微處理器緩存(cached)。

返回值:

如果函數調用成功,則返回分配的首地址;
如果調用失敗,則返回nullptr。可以通過GetLastError函數來獲取錯誤信息。

// 直接去堆上按頁申請空間
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32// 8K一頁為單位向操作系統申請內存空間void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}static void*& NextObj(void* obj)
{return *(void**)obj;
}

定長池設計

一個分配固定大小內存的模板類,由于申請的內存大小固定,所以申請固定大小的空間時,性能比malloc更好一些,目前暫時不考慮內存碎片問題。

管理的成員:

  • 預先申請的內存空間的指針memory

  • 管理用戶釋放空間的空閑鏈表的指針freelist

    • 空閑鏈表連接的方式是:用戶將內存釋放后,該內存空間的前4/8字節(取決于系統位數)空間用于存放下一塊內存空間的地址。

    • 插入新空間到空閑鏈表:通過頭插法實現,由于系統位數不確定,所以使用二級指針解引用來獲得指針的大小,從而可以適用于32/64位系統。

  • 記錄空間剩余大小的字段remain_size

template<class T>
class ObjectPool
{
private:char* _memory = nullptr; // 預先申請的內存空間size_t _remain_size = 0; // 剩余空間大小void* _freelist = nullptr; // 管理用戶釋放空間的空閑鏈表
};

提供的方法:

  • New:用戶申請空間的接口。

    • 如果剩余空間大小不足一個空間,則重新開辟一塊新的固定大小的內存空間。

    • 使用定位new,顯示調用構造函數后返回對象指針給用戶

    • 更新memory指針偏移量和remain_size大小,如果T類型大小不足以存放下一塊空間的地址,則更新大小應為指針的大小。

T* New()
{T* obj = nullptr;// 優先使用空閑鏈表中的空間if (_freelist != nullptr){void* next = *((void**)_freelist);obj = (T*)_freelist;_freelist = next;}else{// 當空間不足時,開辟固定大小的空間if (_remain_size < sizeof T){_remain_size = 128 * 1024;//_memory = (char*)malloc(128 * 1024); // 定長池,開辟128KB的內存空間_memory = (char*)SystemAlloc(128 * 1024); // 定長池,開辟128KB的內存空間if (_memory == nullptr){throw std::bad_alloc();}}obj = (T*)_memory;// 分配的空間的大小至少要能夠存放下一塊空間的地址size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);_memory += objSize; // 分配定長空間后,指針向后偏移_remain_size -= objSize; // 更新剩余空間大小}// 定位new,顯式調用T的構造進行初始化new(obj)T;return obj;
}
  • Delete,釋放T*類型的指針指向的空間,但不會返回給操作系統,而是通過空閑鏈表管理起來。

    • 顯式調用析構函數清理指針指向對象的資源,并將空閑空間頭插到空閑鏈表。

    • 空閑鏈表連接的方式是:用戶將內存釋放后,該內存空間的前4/8字節(取決于系統位數)空間用于存放下一塊內存空間的地址。

    • 插入新空間到空閑鏈表:通過頭插法實現,由于系統位數不確定,所以使用二級指針解引用來獲得指針的大小,從而可以適用于32/64位系統。

// 將用戶要釋放的空間用空閑鏈表管理起來
// 空閑鏈表連接的方式是:空間的前4/8字節(其實就是指針的大小,具體取決于系統位數)存放下一塊空間的地址
void Delete(T* obj)
{obj->~T();// 使用二級指針獲取指針,頭插法將空間添加到空閑鏈表*(void**)obj = _freelist;_freelist = obj;
}

性能測試

接下來我們對定長池進行性能測試,并與malloc進行比較,以下是測試代碼:

void TestPool()
{const int round = 5;const int times = 50000;std::vector<TreeNode*> v1;v1.reserve(5);size_t begin1 = clock();for (size_t j = 0; j < round; ++j){for (int i = 0; i < times; ++i){v1.push_back(new TreeNode);}for (int i = 0; i < times; ++i){delete v1[i];}v1.clear();}size_t end1 = clock();ObjectPool<TreeNode> TNPool;std::vector<TreeNode*> v2;v2.reserve(50000);size_t begin2 = clock();for (int i = 0; i < round; i++){for (int j = 0; j < times; j++){v2.push_back(TNPool.New());}for (int j = 0; j < times; j++){TNPool.Delete(v2[i]);}v2.clear();}size_t end2 = clock();std::cout << "malloc耗時:" << end1 - begin1 << std::endl;std::cout << "ObjectPool耗時:" << end2 - begin2 << std::endl;
}

Debug版本的比較:

Release版本的比較:

可以發現,在高頻分配固定大小對象的場景下,定長池的效率要比malloc更高,這是因為:

定長池只用于分配固定大小的對象,每次開辟的都是固定大小的內存塊,管理空閑空間也只需要使用簡單的空閑鏈表就能完成;而malloc需要處理各種各樣的場景,根據用戶需要分配不同大小的內存塊,空閑空間的管理也要復雜得多。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/77023.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/77023.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/77023.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

路由器原理與配置技術詳解

一、路由基礎原理 1.1 路由器的核心功能 網絡層設備&#xff1a;工作在OSI參考模型第三層&#xff0c;實現不同網絡間的互聯互通智能路徑選擇&#xff1a;基于路由表為數據包選擇最優傳輸路徑協議轉換&#xff1a;處理不同網絡接口間的協議差異&#xff08;如以太網與PPP&…

Leetcode 3518. Smallest Palindromic Rearrangement II

Leetcode 3518. Smallest Palindromic Rearrangement II 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;Leetcode 3518. Smallest Palindromic Rearrangement II 1. 解題思路 這一題是題目Leetcode 3517. Smallest Palindromic Rearrangement I的升級版本&#xff0c;其主要的…

大模型——Crawl4AI 中的數據提取策略

大模型——Crawl4AI 中的數據提取策略 在本章中,將詳細介紹在 Crawl4AI 中可用的數據提取策略。這些策略包括: LLMExtractionStrategy:用于詳細內容提取。JsonCssExtractionStrategy:使用 CSS 選擇器進行結構化數據檢索。CosineStrategy:基于余弦相似性進行有效的語義分段…

職坐標解碼互聯網行業轉型發展新動能

當前&#xff0c;互聯網行業正以前所未有的速度重塑全球產業格局。工信部最新數據顯示&#xff0c;我國互聯網企業營收連續三年保持雙位數增長&#xff0c;其中百強企業在人工智能、物聯網等領域的投入強度同比提升40%&#xff0c;展現出強勁的技術引領力。與此同時&#xff0c…

linux多線(進)程編程——(4)進程間的傳音術(命名管道)

前言&#xff08;前情回顧&#xff09; 進程君&#xff08;父進程&#xff09;在開發出匿名管道這門傳音術后&#xff0c;解決了和自己孩子&#xff08;子進程&#xff09;間的溝通問題&#xff0c;父子關系趨于融洽。和孩子溝通后&#xff0c;進程君發現&#xff0c;自己脫離…

在IDEA里面建立maven項目(便于java web使用)

具體步驟&#xff1a; 第一次有的電腦你再創建項目的時候右下角會提醒你彈窗&#xff1a;讓你下載沒有的東西 一定要下載&#xff01;&#xff01;可能會很慢 運行結果&#xff1a; 因為他是默認的8080端口所以在運行的時候輸入的url如下圖&#xff1a; 新建了一個controller代…

【13】數據結構之樹結構篇章

目錄標題 樹Tree樹的定義樹的基本概念樹的存儲結構雙親表示法孩子表示法孩子兄弟表示法 二叉樹二叉樹與度不超過&#xff12;的普通樹的不同之處二叉樹的基本形態二叉樹的分類二叉樹的性質 二叉樹的順序存儲二叉樹的鏈式存儲二叉樹的鏈式存儲的結點結構樹的遍歷先序遍歷中序遍歷…

雷達生命探測儀,地震救援的生命探測先鋒|鼎躍安全

在地震、山體滑坡、坍塌建筑等突發災害中&#xff0c;會嚴重摧毀建筑物&#xff0c;造成倒塌和人員被困&#xff1b;在瓦礫堆、混凝土板層中&#xff0c;受困人員的生命安全常常面臨嚴峻威脅。傳統救援手段通常存在響應時間長、監測精度有限等不足。 救援現場往往環境復雜&…

512天,倔強生長:一位技術創作者的獨白

親愛的讀者與同行者&#xff1a; 我是倔強的石頭_&#xff0c;今天是我在CSDN成為創作者的第512天。當系統提示我寫下這篇紀念日文章時&#xff0c;我恍惚間想起了2023年11月19日的那個夜晚——指尖敲下《開端——》的標題&#xff0c;忐忑又堅定地按下了“發布”鍵。那時的我…

數據結構*集合框架順序表-ArrayList

集合框架 常見的集合框架 什么是順序表 順序表是一種線性表數據結構&#xff0c;它借助一組連續的存儲單元來依次存儲線性表中的數據元素。一般情況下采用數組存儲。 在數組上完成數據的增刪查改。 自定義簡易版的順序表 代碼展示&#xff1a; public interface IArray…

使用openpyxl時的一些注意點

一、是否需要close()&#xff1f; 在使用 openpyxl 時&#xff0c;wb.save() 后一般不需要再手動調用 wb.close()。wb.save() 會自動處理文件寫入和釋放。 如果是使用openpyxl.load_workbook(filename, read_onlyTrue) 打開了一個只讀模式的工作簿&#xff0c;此時會建立文件…

Python爬蟲第11節-解析庫Beautiful Soup的使用上篇

目錄 前言 一、Beautiful Soup 簡介 1.1 Beautiful Soup概述 1.2 準備工作 1.3 解析器 二、基本使用 三、節點選擇器的使用 3.1 選擇元素 3.2 提取信息 3.2.1 獲取名稱 3.2.2 獲取屬性 3.2.3 獲取內容 3.3 嵌套選擇 3.4 關聯選擇 3.4.1 子節點和子孫節點 3.4.2…

【Docker-13】Docker Container容器

Docker Container&#xff08;容器&#xff09; 一、什么是容器&#xff1f; 通俗地講&#xff0c;容器是鏡像的運行實體。鏡像是靜態的只讀文件&#xff0c;而容器帶有運行時需要的可寫文件層&#xff0c;并且容器中的進程屬于運行狀態。即容器運行著真正的應用進程。容器有…

Spring Cache(筆記)

簡介&#xff1a; 常用注解&#xff1a;

大模型Qwen32b(FP16精度)部署所需的顯存大小和并發數計算分析

大家好&#xff0c;我是微學AI&#xff0c;今天給大家介紹一下大模型Qwen32b(FP16精度)部署所需的顯存大小和并發計算分析。 文章目錄 1. 大模型顯存需求分析1.1 模型參數與顯存占用1.2 不同精度對顯存的影響 2. 不同顯卡配置下的并發能力2.1 80G顯卡并發能力2.2 64G顯卡并發能…

【euclid】10.2 2D變換模塊(transform2d.rs)Arbitrary trait

源碼 #[cfg(feature "arbitrary")] impl<a, T, Src, Dst> arbitrary::Arbitrary<a> for Transform2D<T, Src, Dst> whereT: arbitrary::Arbitrary<a>, {fn arbitrary(u: &mut arbitrary::Unstructured<a>) -> arbitrary::Res…

MAC Mini M4 上測試Detectron2 圖像識別庫

斷斷續續地做圖像識別的應用&#xff0c;使用過各種圖像識別算法&#xff0c;一開始使用openCV 做教室學生計數的程序。以后又使用YOLO 做醫學傷口檢測程序。最近&#xff0c;開始使用meta 公司的Detectron2.打算做OCR 文檔結構分析 Detectron2 的開發者是 Meta 的 Facebook AI…

一天時間,我用AI(deepseek)做了一個配色網站

前言 最近在開發顏色搭配主題的相關H5和小程序&#xff0c;想到需要補充一個web網站&#xff0c;因此有了這篇文章。 一、確定需求 向AI要答案之前&#xff0c;一定要清楚自己想要做什么。如果你沒有100%了解自己的需求&#xff0c;可以先讓AI幫你理清邏輯和思路&#xff0c;…

機器視覺用消色差雙合透鏡

光學系統案例&#xff1a;機器視覺用消色差雙合透鏡 一、設計規格 1. 應用場景&#xff1a;專為工業相機成像而設計&#xff0c;工作于可見光波段&#xff0c;旨在滿足該領域對高精度成像的需求。 2. 核心參數&#xff1a; ? 焦距&#xff1a;精確要求達到 50 mm 1%&#…

批量歸一化(Batch Normalization)原理與PyTorch實現

批量歸一化&#xff08;Batch Normalization&#xff09;是加速深度神經網絡訓練的常用技術。本文通過Fashion-MNIST數據集&#xff0c;演示如何從零實現批量歸一化&#xff0c;并對比PyTorch內置API的簡潔實現方式。 1. 從零實現批量歸一化 1.1 批量歸一化函數實現 import t…