欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++高并發(fā)內存池的整體設計和實現思路

 更新時間:2021年07月13日 10:35:39   作者:Moua  
這篇文章主要介紹了C++高并發(fā)內存池的整體設計和實現思路詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

一、整體設計

1、需求分析

池化技術是計算機中的一種設計模式,內存池是常見的池化技術之一,它能夠有效的提高內存的申請和釋放效率以及內存碎片等問題,但是傳統(tǒng)的內存池也存在一定的缺陷,高并發(fā)內存池相對于普通的內存池它有自己的獨特之處,解決了傳統(tǒng)內存池存在的一些問題。

附:實現一個內存池管理的類方法

1)直接使用new/delete、malloc/free存在的問題

new/delete用于c++中動態(tài)內存管理而malloc/free在c++和c中都可以使用,本質上new/delete底層封裝了malloc/free。無論是上面的那種內存管理方式,都存在以下兩個問題:

  • 效率問題:頻繁的在堆上申請和釋放內存必然需要大量時間,降低了程序的運行效率。對于一個需要頻繁申請和釋放內存的程序來說,頻繁調用new/malloc申請內存,delete/free釋放內存都需要花費系統(tǒng)時間,頻繁的調用必然會降低程序的運行效率。
  • 內存碎片:經常申請小塊內存,會將物理內存“切”得很碎,導致內存碎片。申請內存的順序并不是釋放內存的順序,因此頻繁申請小塊內存必然會導致內存碎片,造成“有內存但是申請不到大塊內存”的現象。

2)普通內存池的優(yōu)點和缺點

針對直接使用new/delete、malloc/free存在的問題,普通內存池的設計思路是:預先開辟一塊大內存,程序需要內存時直接從該大塊內存中“拿”一塊,提高申請和釋放內存的效率,同時直接分配大塊內存還減少了內存碎片問題。

優(yōu)點:申請和釋放內存的效率有所提高;一定程度上解決了內存碎片問題。

缺點:多線程并發(fā)場景下申請和釋放內存存在鎖競爭問題造成申請和釋放內存的效率降低。

3)高并發(fā)內存池要解決的問題

基于以上原因,設計高并發(fā)內存池需要解決以下三個問題:

  • 效率問題
  • 內存碎片問題
  • 多線程并發(fā)場景下的內存釋放和申請的鎖競爭問題。

2、總體設計思路

高并發(fā)內存池整體框架由以下三部分組成,各部分的功能如下:

  • 線程緩存(thread cache):每個線程獨有線程緩存,主要解決多線程下高并發(fā)運行場景線程之間的鎖競爭問題。線程緩存模塊可以為線程提供小于64k內存的分配,并且多個線程并發(fā)運行不需要加鎖。
  • 中心控制緩存(central control cache):中心控制緩存顧名思義,是高并發(fā)內存池的中心結構主要用來控制內存的調度問題。負責大塊內存切割分配給線程緩存以及回收線程緩存中多余的內存進行合并歸還給頁緩存,達到內存分配在多個線程中更均衡的按需調度的目的,它在整個項目中起著承上啟下的作用。(注意:這里需要加鎖,當多個線程同時向中心控制緩存申請或歸還內存時就存在線程安全問題,但是這種情況是極少發(fā)生的,并不會對程序的效率產生較大的影響,總體來說利大于弊)
  • 頁緩存(page cache):以頁為單位申請內存,為中心控制緩存提供大塊內存。當中心控制緩存中沒有內存對象時,可以從page cache中以頁為單位按需獲取大塊內存,同時page cache還會回收central control cache的內存進行合并緩解內存碎片問題。

3、申請內存流程圖

二、詳細設計

1、各個模塊內部結構詳細剖析

1)thread cache

邏輯結構設計

thread cache的主要功能就是為每一個線程提供64K以下大小內存的申請。為了方便管理,需要提供一種特定的管理模式,來保存未分配的內存以及被釋放回來的內存,以方便內存的二次利用。這里的管理通常采用將不同大小的內存映射在哈希表中,鏈接起來。而內存分配的最小單位是字節(jié),64k = 1024*64Byte如果按照一個字節(jié)一個字節(jié)的管理方式進行管理,至少也得需要1024*64大小的哈希表對不同大小的內存進行映射。為了減少哈希表長度,這里采用按一定數字對齊的方式進行內存分配,將浪費率保持在1%~12%之間。具體結構如下:

具體說明如下:

  • 使用數組進行哈希映射,每一個位置存放的是一個鏈表freelists,該鏈表的作用是將相同大小的內存對象鏈接起來方便管理。
  • 每個數組元素鏈接的都是不同大小的內存對象。
  • 第一個元素表示對齊數是8,第2個是16....依次類推。對齊數表示在上一個對齊數和這個對齊數之間的大小的內存都映射在這個位置,即要申請1字節(jié)或者7字節(jié)的內存都在索引位置為0出找8字節(jié)大小的內存,要申請9~16字節(jié)大小的內存都在索引為1的位置找16字節(jié)的內存對象。
  • 通過上面的分析,可以看出如果進行8字節(jié)對齊,最多會浪費7字節(jié)的內存(實際申請1字節(jié)內存,返回的是8字節(jié)大小的內存對象),將這種現象稱為內存碎片浪費。
  • 為了將內存碎片浪費保持在12%以下,也就是說最多容忍有12%的內存浪費,這里使用不同的對齊數進行對齊。
  • 0~128采用8字節(jié)對齊,129~1024采用16字節(jié)對齊,1025~8*1024采用128字節(jié)對齊,8*1024~64*1024采用1024字節(jié)對齊;內存碎片浪費率分別為:1/8,129/136,1025/1032,8102/8199均在12%左右。同時,8字節(jié)對齊時需要[0,15]共16個哈希映射;16字節(jié)對齊需要[16,71]共56個哈希映射;128字節(jié)對齊需要[72,127]共56個哈希映射;1024字節(jié)對齊需要[128,184]共56個哈希映射。
  • 哈希映射的結構如下:

如何保證每個線程獨有?

TLS(Thread Local Stirage)

大于64k的內存如何申請?

當thread cache中申請的內存大于64K時,直接向page cache申請。但是page cache中最大也只能申請128頁的內存,所以當thread cache申請的內存大于128頁時page cache中會自動給thread cache在系統(tǒng)內存中申請。

2)central control cache

central control cache作為thread cache和page cache的溝通橋梁,起到承上啟下的作用。它需要向thread cache提供切割好的小塊內存,同時他還需要回收thread cache中的多余內存進行合并,在分配給其他其他thread cache使用,起到資源調度的作用。它的結構如下:

具體說明如下:

  • central control cache的結構依然是一個數組,他保存的是span類型的對象。
  • span是用來管理一塊內存的,它里邊包含了一個freelist鏈表,用于將大塊內存切割成指定大小的小塊內存鏈接到freelist中,當thread cache需要內存時直接將切割好的內存給thread cache。
  • 開始時,每個數組索引位置都是空的,當thread cache申請內存時,spanList數組會向page cache申請一大塊內存進行切割后掛在list中。當該快內存使用完,會繼續(xù)申請新的內存,因此就存在多個span鏈接的情況。前邊span存在對象是因為有可能后邊已經申請好內存了前邊的內存也釋放回來了。
  • 當某一個span的全部內存都還回來時,central control cache會再次將這塊內存合并,在歸還到page cache中。
  • 當central control cache為空時,向page cache申請內存,每次至少申請一頁,并且必須以頁為單位進行申請(這里的頁大小由我們自己決定,這里采用4K)。

這里需要注意的是,thread cache可能會有多個,但是central control cache只有一個,要讓多個thread cache對象訪問一個central control cache對象,這里的central control cache需要設計成單例模式。

3)page cache

page cache是以頁為單位進行內存管理的,它是將不同頁數的內存利用哈希進行映射,最多映射128頁內存,具體結構如下:

page Cache申請和釋放內存流程:

  • 當central control cache向page cache申請內存時,比如要申請8頁的內存,它會先在span大小為8的位置找,如果沒有就繼續(xù)找9 10...128,那個有就從那個中切割8頁。
  • 例如,走到54時才有內存,就從54處切8頁返回給central control cache,將剩余的54-846頁掛在46頁處。
  • 當page cache中沒有內存時,它直接申請一個128頁的內存掛在128位置。當central control cache申請內存時再從128頁切。

2、設計細節(jié)

1)thread cache

根據申請內存大小計算對應的_freelists索引

  • 1~8都映射在索引為0處,9~16都在索引為2處......
  • 因此以8字節(jié)對齊時,可以表示為:((size + (2^3 - 1)) >> 3) - 1;
  • 如果申請的內存為129,索引如何計算?
  • 首先前128字節(jié)是按照8字節(jié)對齊的,因此:((129-128)+(2^4-1))>>4)-1 + 16
  • 上式中16表示索引為0~15的16個位置以8字節(jié)對齊。

代碼實現:

//根據內存大小和對齊數計算對應下標
static inline size_t _Intex(size_t size, size_t alignmentShift)
{
	//alignmentShift表示對齊數的位數,例如對齊數為8 = 2^3時,aligmentShift = 3
	//這樣可以將除法轉化成>>運算,提高運算效率
	return ((size + (1 << alignmentShift) - 1) >> alignmentShift) - 1;
}
//根據內存大小,計算對應的下標
static inline size_t Index(size_t size)
{
	assert(size <= THREAD_MAX_SIZE);
 
	//每個對齊數對應的索引個數,分別表示8 16 128 1024字節(jié)對齊
	int groupArray[4] = {16,56,56,56};
 
	if (size <= 128)
	{
		//8字節(jié)對齊
		return _Intex(size, 3) + groupArray[0];
	}
	else if (size <= 1024)
	{
		//16字節(jié)對齊
		return _Intex(size, 4) + groupArray[1];
	}
	else if (size <= 8192)
	{
		//128字節(jié)對齊
		return _Intex(size, 7) + groupArray[2];
	}
	else if (size <= 65536)
	{
		//1024字節(jié)對齊
		return _Intex(size, 10) + groupArray[3];
	}
 
	assert(false);
	return -1;
}

freelist向中心緩存申請內存時需要對申請的內存大小進行對齊

首先,需要申請的內存大小不夠對齊數時都需要進行向上對齊。即,要申請的內存大小為1字節(jié)時需要對齊到8字節(jié)。如何對齊?不進行對齊可以嗎?

首先,不進行對齊也可以計算出freelist索引,當第一次申請內存時,freelist的索引位置切割后的內存大小就是實際申請的內存大小,并沒有進行對齊,造成內存管理混亂。對齊方式如下:

  • 對齊數分別為8 = 2^3; 16 = 2^4 ; 128 = 2^7 ; 1024 = 2^10,轉化成二進制后只有1個1.
  • 在對齊區(qū)間內,所有數+對齊數-1后一定是大于等于當前區(qū)間的最大值且小于下一個相鄰區(qū)間的最大值。
  • 因此,size + 對齊數 - 1如果是8字節(jié)對齊只需將低3位變?yōu)?,如果是16字節(jié)對齊將低3位變?yōu)?......
  • 例如:size = 2時,對齊數為8;則size + 8 - 1 = 9,轉為而進制位1001,將低三位變?yōu)?后為1000,轉為十進制就是對齊數8.

代碼表示如下:alignment表示對齊數

(size + alignment - 1) & ~(alignment - 1);

注意:向這些小函數,定義成inline可以減少壓棧開銷。 ‘

如何將小塊內存對象“掛在”freelist鏈表中

哈哈,前邊已經為這里做好鋪墊了。前邊規(guī)定單個對象大小最小為8字節(jié),32位系統(tǒng)下一個指針的大小為4字節(jié),64位機器下一個指針的大小為8字節(jié)。前邊我們規(guī)定單個對象最小大小為8字節(jié)就是為了無論是在32位系統(tǒng)下還是在64位系統(tǒng)下,都可以保存一個指針將小塊對象鏈接起來。那么,如何使用一小塊內存保存指針?

直接在內存的前4/8個字節(jié)將下一塊內存的地址保存,取內存時直接對該內存解引用就可以取出地址。

訪問:*(void**)(mem)

每次從freelist中取內存或者歸還內存時,直接進行頭插或頭刪即可。

從central control cache中申請內存,一次申請多少合適呢?

這里的思路是采用“慢啟動”的方式申請,即第一次申請申請一個,第二次申請2個....當達到一定大?。?12個)時不再增加。這樣做的好處是,第一次申請給的數量少可以防止某些線程只需要一個多給造成浪費,后邊給的多可以減少從central control cache的次數從而提高效率。

當使用慢啟動得到的期望內存對象個數大于當前central control cache中內存對象的個數時,有多少給多少。因為,實際上目前只需要一個,我們多申請了不夠,那就有多少給多少。當一個都沒有的時候才會去page cache申請。

什么時候thread cache將內存還給central controlcache?

當一個線程將內存還給thread cache時,會去判斷對應的_freelist的對應位置是否有太多的內存還回來(thread cache中內存對象的大小大于等于最個數的時候,就向central control cache還)。

2)Central Control Cache

SpanList結構

SpanList在central control cache中最重要的作用就是對大塊內存管理,它存儲的是一個個span類的對象,使用鏈表進行管理。結構如下:

也就是說,SpanList本質上就是一個span鏈表。這里考慮到后邊歸還內存需要找到對應頁歸還,方便插入,這里將spanlist設置成雙向帶頭循環(huán)鏈表。

Span結構

Span存儲的是大塊內存的信息,陪SpanList共同管理大塊內存,它的內存單位是頁(4K)。它的結構實際上就是一個個size大小的對象鏈接起來的鏈表。它同時也作為SpanList的節(jié)點,spanList是雙向循環(huán)鏈表,因此span中還有next和prev指針。

struct Span
{
PageID _pageId = 0; // 頁號
size_t _n = 0; // 頁的數量

Span* _next = nullptr;
Span* _prev = nullptr;

void* _list = nullptr; // 大塊內存切小鏈接起來,這樣回收回來的內存也方便鏈接
size_t _usecount = 0; // 使用計數,==0 說明所有對象都回來了

size_t _objsize = 0; // 切出來的單個對象的大小
};

當spanList中沒有內存時需要向PageCache申請內存,一次申請多少合適呢?

根據申請的對象的大小分配內存,也就是說單個對象大小越小分配的頁數越少,單個對象的大小越大分配到的內存越多。如何衡量多少?

這里我們是通過thread cache中從central control cache中獲取的內存對象的個數的上限來確定。也就是說,個數的上限*內存對象的大小就是我們要申請的內存的大小。在右移12位(1頁)就是需要申請的頁數。

//計算申請多少頁內存
static inline size_t NumMovePage(size_t memSize)
{
	//計算thread cache最多申請多少個對象,這里就給多少個對象
	size_t num = NumMoveSize(memSize);
	//此時的nPage表示的是獲取的內存大小
	size_t nPage = num*memSize;
	//當npage右移是PAGE_SHIFT時表示除2的PAGE_SHIFT次方,表示的就是頁數
	nPage >>= PAGE_SHIFT;
 
	//最少給一頁(體現了按頁申請的原則)
	if (nPage == 0)
		nPage = 1;
 
	return nPage;
}

向central control cache申請一塊內存,切割時如果最后產生一個碎片(不夠一個對象大小的內存)如何處理?

一旦產生這種情況,最后的碎片內存只能丟棄不使用。但是對于我們的程序來說是不會產生的,因為我們每次申請至少一頁,4096可以整除我們所對應的任何一個大小的對象。

central control cache何時將內存還給page cache?

thread cache將多余的內存會還給central control cache中的spanlist對應的span,span中有一個usecount用來統(tǒng)計該span中有多少個對象被申請走了,當usecount為0時,表示所有對象都還回來了,則將該span還給page cache,合并成更大的span。

3)Page Cache

當從一個大頁切出一個小頁內存時,剩余的內存如何掛在對應位置?

在Page cache中的span它是沒有切割的,都是一個整頁,也就是說這里的Span的list并沒有使用到。這里計算內存的地址都是按照頁號計算的,當一個Span中有多頁內存時保存的是第一頁的內存,那么就可以計算出剩余內存和切走內存的頁號,設置相應的頁號進行映射即可。

從一個大的Span中切時,采用頭切還是尾切?

Span中如何通過頁號計算地址?

每一頁大小都是固定的,當我們從系統(tǒng)申請一塊內存會返回該內存的首地址,申請內存時返回的都是一塊連續(xù)的內存,所以我們可以使用內存首地址/頁大小的方式計算出頁號,通過這種方式計算出來的一大塊內存的多個頁的頁號都是連續(xù)的。

Page Cache向系統(tǒng)申請內存

Page Cache向系統(tǒng)申請內存時,前邊我們說過每次直接申請128頁的內存。這里需要說明的是,我們的項目中不能出現任和STL中的數據結構和庫函數,因此這里申請內存直接采用系統(tǒng)調用VirtualAlloc。下面對VirtualAlloc詳細解釋:

VirtualAlloc是一個Windows API函數,該函數的功能是在調用進程的虛地址空間,預定或者提交一部分頁。簡單點的意思就是申請內存空間。

函數聲明如下:

LPVOID VirtualAlloc{
LPVOID lpAddress, // 要分配的內存區(qū)域的地址

DWORD dwSize, // 分配的大小

DWORD flAllocationType, // 分配的類型

DWORD flProtect // 該內存的初始保護屬性

};

參數說明:

  • LPVOID lpAddress, 分配內存區(qū)域的地址。當你使用VirtualAlloc來提交一塊以前保留的內存塊的時候,lpAddress參數可以用來識別以前保留的內存塊。如果這個參數是NULL,系統(tǒng)將會決定分配內存區(qū)域的位置,并且按64-KB向上取整(roundup)。
  • SIZE_T dwSize, 要分配或者保留的區(qū)域的大小。這個參數以字節(jié)為單位,而不是頁,系統(tǒng)會根據這個大小一直分配到下頁的邊界DWORD
  • flAllocationType, 分配類型 ,你可以指定或者合并以下標志:MEM_COMMIT,MEM_RESERVE和MEM_TOP_DOWN。
  • DWORD flProtect 指定了被分配區(qū)域的訪問保護方式

注:PageCache中有一個map用來存儲pageId和Span的映射。在釋放內存時,通過memSize計算出pageId,在通過PageId在map中查找對應的Span從而就可以獲得單個對象的大小,在根據單個對象的大小確定是要將內存還給page cache還是還給central control cache。

central control cache釋放回來的內存如何合并成大內存?

通過span中的頁號查找前一頁和后一頁,判斷前一頁和后一頁是否空閑(沒有被申請的內存),如果空閑就進行和并,合并完后重新在map中進行映射。

注意:將PageCache和CentralControlCache設置成單例模式,因為多個線程對同時使用一個page cache和central control cache進行內存管理。

單例模式簡單介紹

  • 單例模式,顧名思義只能創(chuàng)建一個實例。
  • 有兩種實現方式:懶漢實現和餓漢實現
  • 做法:將構造函數和拷貝構造函數定義成私有且不能默認生成,防止在類外構造對象;定義一個本身類型的成員,在類中構造一個對象,提供接口供外部調用。

4)加鎖問題

  • 在central control cache和page cache中都存在多個線程訪問同一臨界資源的情況,因此需要加鎖。
  • 在central control cache中,不同線程只要訪問的不是同一個大小的內存對象,則就不需要加鎖,可以提高程序的運行效率(加鎖后就有可能導致線程掛起等待),也就是說central control cache中是“桶鎖”。需要改freelist那個位置的內存,就對那個加鎖。
  • page cache中,需要對申請和合并內存進行加鎖。
  • 這里我們統(tǒng)一使用互斥鎖。

注意:使用map進行映射,雖然說我們對pagecache進行了加鎖,不會早成寫數據的沖突,但是我們還向外提供了查找的接口,就有可能導致一個線程在向map中寫而另一個線程又查找,出現線程安全問題,但是如果給查找位置加鎖,這個接口會被頻繁的調用,造成性能的損失。而在tcmalloc中采用基數樹來存儲pageId和span的映射關系,從而提高效率。

附:基數樹

三、測試

1、單元測試

void func1()
{
	for (size_t i = 0; i < 10; ++i)
	{
		hcAlloc(17);
	}
}
 
void func2()
{
	for (size_t i = 0; i < 20; ++i)
	{
		hcAlloc(5);
	}
}
 
//測試多線程
void TestThreads()
{
	std::thread t1(func1);
	std::thread t2(func2);
 
 
	t1.join();
	t2.join();
}
 
//計算索引
void TestSizeClass()
{
	cout << SizeClass::Index(1035) << endl;
	cout << SizeClass::Index(1025) << endl;
	cout << SizeClass::Index(1024) << endl;
}
 
//申請內存
void TestConcurrentAlloc()
{
	void* ptr0 = hcAlloc(5);
	void* ptr1 = hcAlloc(8);
	void* ptr2 = hcAlloc(8);
	void* ptr3 = hcAlloc(8);
 
	hcFree(ptr1);
	hcFree(ptr2);
	hcFree(ptr3);
}
 
//大塊內存的申請
void TestBigMemory()
{
	void* ptr1 = hcAlloc(65 * 1024);
	hcFree(ptr1);
 
	void* ptr2 = hcAlloc(129 * 4 * 1024);
	hcFree(ptr2);
}
 
//int main()
//{
//	//TestBigMemory();
//
//	//TestObjectPool();
//	//TestThreads();
//	//TestSizeClass();
//	//TestConcurrentAlloc();
//
//	return 0;
//}

2、性能測試

void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
	//創(chuàng)建nworks個線程
	std::vector<std::thread> vthread(nworks);
	size_t malloc_costtime = 0;
	size_t free_costtime = 0;
 
	//每個線程循環(huán)依次
	for (size_t k = 0; k < nworks; ++k)
	{
		//鋪貨k
		vthread[k] = std::thread([&, k]() {
			std::vector<void*> v;
			v.reserve(ntimes);
 
			//執(zhí)行rounds輪次
			for (size_t j = 0; j < rounds; ++j)
			{
				size_t begin1 = clock();
				//每輪次執(zhí)行ntimes次
				for (size_t i = 0; i < ntimes; i++)
				{
					v.push_back(malloc(16));
				}
				size_t end1 = clock();
 
				size_t begin2 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					free(v[i]);
				}
				size_t end2 = clock();
				v.clear();
 
				malloc_costtime += end1 - begin1;
				free_costtime += end2 - begin2;
			}
		});
	}
 
	for (auto& t : vthread)
	{
		t.join();
	}
 
	printf("%u個線程并發(fā)執(zhí)行%u輪次,每輪次malloc %u次: 花費:%u ms\n",
		nworks, rounds, ntimes, malloc_costtime);
 
	printf("%u個線程并發(fā)執(zhí)行%u輪次,每輪次free %u次: 花費:%u ms\n",
		nworks, rounds, ntimes, free_costtime);
 
	printf("%u個線程并發(fā)malloc&free %u次,總計花費:%u ms\n",
		nworks, nworks*rounds*ntimes, malloc_costtime + free_costtime);
}
 
 
// 單輪次申請釋放次數 線程數 輪次
void BenchmarkConcurrentMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
	std::vector<std::thread> vthread(nworks);
	size_t malloc_costtime = 0;
	size_t free_costtime = 0;
 
	for (size_t k = 0; k < nworks; ++k)
	{
		vthread[k] = std::thread([&]() {
			std::vector<void*> v;
			v.reserve(ntimes);
 
			for (size_t j = 0; j < rounds; ++j)
			{
				size_t begin1 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					v.push_back(hcAlloc(16));
				}
				size_t end1 = clock();
 
				size_t begin2 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					hcFree(v[i]);
				}
				size_t end2 = clock();
				v.clear();
 
				malloc_costtime += end1 - begin1;
				free_costtime += end2 - begin2;
			}
		});
	}
 
	for (auto& t : vthread)
	{
		t.join();
	}
 
	printf("%u個線程并發(fā)執(zhí)行%u輪次,每輪次concurrent alloc %u次: 花費:%u ms\n",
		nworks, rounds, ntimes, malloc_costtime);
 
	printf("%u個線程并發(fā)執(zhí)行%u輪次,每輪次concurrent dealloc %u次: 花費:%u ms\n",
		nworks, rounds, ntimes, free_costtime);
 
	printf("%u個線程并發(fā)concurrent alloc&dealloc %u次,總計花費:%u ms\n",
		nworks, nworks*rounds*ntimes, malloc_costtime + free_costtime);
}
 
int main()
{
	cout << "==========================================================" << endl;
	BenchmarkMalloc(100000, 4, 10);
	cout << endl << endl;
 
	BenchmarkConcurrentMalloc(100000, 4, 10);
	cout << "==========================================================" << endl;
 
	return 0;
}

結果比較

附1:完整代碼

到此這篇關于C++高并發(fā)內存池的整體設計和實現思路的文章就介紹到這了,更多相關C++高并發(fā)內存池內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C語言實現圖書館管理系統(tǒng)

    C語言實現圖書館管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現圖書館管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C++11如何實現無鎖隊列

    C++11如何實現無鎖隊列

    這篇文章主要介紹了C++11如何實現無鎖隊列,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • C++實現Linux下彈出U盤的方法

    C++實現Linux下彈出U盤的方法

    這篇文章主要介紹了C++實現Linux下彈出U盤的方法,實例分析了C++在Linux平臺上進行IO操作的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • C++中fstream,ifstream及ofstream用法淺析

    C++中fstream,ifstream及ofstream用法淺析

    這篇文章主要介紹了C++中fstream,ifstream及ofstream用法,適合C++初學者學習文件流的操作,需要的朋友可以參考下
    2014-08-08
  • 一文帶你學習C++中的虛函數

    一文帶你學習C++中的虛函數

    C++中的虛函數是一種非常重要的概念,它允許一個子類重寫基類的方法,并確保在調用基類指針或引用的方法時,調用正確的子類方法,本文將介紹C++虛函數的基本概念、語法、使用及其示例,需要的朋友可以參考下
    2023-05-05
  • VC6.0常用快捷鍵大全

    VC6.0常用快捷鍵大全

    這篇文章主要介紹了VC6.0常用快捷鍵大全,非常實用,需要的朋友可以參考下
    2014-08-08
  • Opencv基于CamShift算法實現目標跟蹤

    Opencv基于CamShift算法實現目標跟蹤

    這篇文章主要為大家詳細介紹了Opencv基于CamShift算法實現目標跟蹤,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • visual studio 2022中的scanf問題解決

    visual studio 2022中的scanf問題解決

    昨天在使用Visual Studio 2022編寫C語言程序時遇到了scanf問題,對于vs的編譯器來說scanf是不安全的,編譯器通過不了scanf,本文就來介紹一下解決方法,感興趣的可以了解一下
    2023-12-12
  • C語言函數棧幀詳解

    C語言函數棧幀詳解

    下面小編就為大家?guī)硪黄獪\談C語言函數調用參數壓棧的相關問題。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-10-10
  • C/C++ 函數原理傳參示例詳解

    C/C++ 函數原理傳參示例詳解

    這篇文章主要為大家介紹了C/C++ 函數原理傳參示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論