4、線程關(guān)聯(lián)的內(nèi)存池
每每想到單線程下內(nèi)存池飛一般的速度和多線程下蝸牛一般的速度我就不能原諒自己,為什么差這么多,就不能讓多線程下內(nèi)存分配更快一點(diǎn)嗎?解決方法有了,那就是讓緩存線程化,各個(gè)線程有自己私有的緩存,分配的時(shí)候預(yù)先從當(dāng)前線程私有緩存分配,分配空了的時(shí)候去全局free表取一組freeunit或直接向系統(tǒng)申請(qǐng)一大塊緩存(各個(gè)線程緩存完全獨(dú)立),不管具體采用什么方式,速度都大幅度的提高了,雖然還是比單線程下內(nèi)存池慢了許多,不過比前面提到的多線程內(nèi)存池以及nedmalloc都要快很多,我的實(shí)現(xiàn)大概比nedmalloc快1.6 ~ 2倍,離單線程下內(nèi)存池速度也很近了,只是多了些查找線程id比較線程id等動(dòng)作而已,基本上達(dá)到了自己的目標(biāo)。
看看第一版線程關(guān)聯(lián)內(nèi)存池的一些代碼:
struct tm_bufunit
{
tm_pool *pool; //pool指針
union
{
tm_bufunit *next; //下一個(gè)塊指針
char data[4]; //數(shù)據(jù)區(qū)域
};
};
struct tm_gcontrol
{
tm_bufunit *gfree;
CRITICAL_SECTION gcs;
tm_gcontrol() : gfree(NULL) { InitializeCriticalSection(&gcs); }
~tm_gcontrol() { DeleteCriticalSection(&gcs); }
Inline void lock() { EnterCriticalSection(&gcs); }
Inline void unlock() { LeaveCriticalSection(&gcs); }
void free(tm_bufunit *buf)
{
lock();
buf->next = gfree;
gfree = buf;
unlock();
}
};
struct tm_memblock
{
tm_memblock *next;
};
class tm_pool
{
private:
size_t bksize; //一個(gè)分配塊大小
size_t onebknum; //一次分配多少個(gè)bksize
DWORD thid; //線程id
tm_bufunit *next; //pool中自由塊鏈
tm_memblock *mbk; //trunk表
tm_gcontrol gcontrol; //全局free表
friend tm_poolset;
private:
void expand();
public:
tm_pool(size_t size, size_t bknum);
~tm_pool();
void destroy();
void *newobj();
static void delobj(void *pbuf);
};
class tm_poolset
{
public:
tm_poolset();
virtual ~tm_poolset();
//添加分配池
bool addpool(size_t size, size_t allocnum);
void *newobj(size_t size, size_t *osize=NULL);
void delobj(void *pbuf, size_t size);
void destroy();
tm_pool *findpool(size_t size)
{
TMPOOLS::iterator it = tmpools.lower_bound(size);
if(it != tmpools.end())
return it->second;
return NULL;
}
protected:
typedef std::map<size_t, tm_pool *> TMPOOLS;
TMPOOLS tmpools;
};
//公開的數(shù)據(jù)及函數(shù)
extern DWORD tm_tlsindex; //tls索引
//app初始化,分配index
void tm_init();
void tm_free();
//關(guān)聯(lián)到該線程
void tm_attach();
void tm_detach();
tm_poolset *tm_getpoolset();
//添加trunk
bool tm_addtrunk(size_t size, size_t allocnum);
//tls相關(guān)分配
void *tm_new(size_t size, size_t *osize=NULL);
//tls相關(guān)釋放
void tm_del(void *buf, size_t size);
.cpp代碼如下:
tm_pool::tm_pool(size_t size, size_t bknum) :
next(NULL), mbk(NULL),
bksize(size), onebknum(bknum)
{
thid = GetCurrentThreadId();
}
tm_pool::~tm_pool()
{
destroy();
}
void tm_pool::destroy()
{
for(tm_memblock *p = mbk; p; )
{
tm_memblock *q = p->next;
free((char *)p);
p = q;
}
mbk = NULL;
next = NULL;
}
void *tm_pool::newobj()
{
if(! next)
{
gcontrol.lock();
if(gcontrol.gfree)
{
next = gcontrol.gfree;
gcontrol.gfree = NULL;
}
gcontrol.unlock();
}
if(! next)
{
expand();
}
tm_bufunit *head = next;
next = head->next;
// return (void *)head;
return (void *)head->data;
}
void tm_pool::delobj(void *pbuf)
{
// tm_bufunit *head = (tm_bufunit*)(pbuf);
tm_bufunit *head = (tm_bufunit *)((char *)pbuf-offsetof(tm_bufunit, data));
tm_pool *pool = head->pool;
if(pool->thid == GetCurrentThreadId())
{
head->next = pool->next;
pool->next = head;
}
else
{
pool->gcontrol.free(head);
}
}
void tm_pool::expand()
{
size_t unitsize = offsetof(tm_bufunit, data) + bksize;
size_t size = (unitsize * onebknum + sizeof(tm_memblock));
tm_memblock *pbk = (tm_memblock *)malloc(size);
pbk->next = mbk;
mbk = pbk;
tm_bufunit *p = (tm_bufunit*)((char *)pbk+sizeof(tm_memblock));
p->pool = this;
next = p;
for(size_t i=0; i<onebknum-1; ++i)
{
p->next = (tm_bufunit *)((char *)p+unitsize);
p = p->next;
p->pool = this;
}
p->next = NULL;
}
…
這一版基本實(shí)現(xiàn)了第一步的提速目標(biāo),并且每個(gè)分配塊還記錄了來自哪個(gè)pool,這樣free的時(shí)候就省去了查找pool的動(dòng)作,只是還有一些問題,如何判斷一個(gè)內(nèi)存是來源于malloc的分配還是來源于pool的分配沒有做終結(jié)的判斷,而且還留下了一個(gè)bug,對(duì)于a線程來說,可能只有256,512兩個(gè)塊的緩存,b線程可能多一個(gè)塊1024,這樣a線程分配的1024字節(jié)的內(nèi)存是用malloc分配,到b線程釋放的時(shí)候會(huì)調(diào)用pool釋放,這個(gè)bug將在下一章解決。