打造先進的內存KV數據庫-4 夥伴內存管理系統

夥伴算法

大概是這樣的,monkey儲存引擎(今年猴年了嘛~取個名字,重了再換)的內存管理使用夥伴算法,大概原理是這樣的:
1.多個空閒內存塊的鏈表,分別是不同大小的內存塊,1K,2K,4K,8K,…,,按照這樣的情況一直到4M,當程序申請內存時,選擇一個稍大於其所要申請的塊,比如申請1023字節,給1K,1024字節也給1K(爲什麼呢– 後文會講),2056字節給4K。
2.如果要給的內存塊沒有,比如要分配4K,但是4K大小的內存塊用光了,那麼向上,將8K大小的分割掉,拿一個4K的分配掉,剩餘的4K插入到4K的鏈表裏面去,當一直分割,4M的最大塊也沒有的情況,就向操作系統申請4M的塊。
3.不用的內存如何回收呢?因爲給的內存塊大小大於所儲存的內容,所以將第一個字節用來記錄內存塊大小,回收的時候根據第一字節的大小扔進鏈表就可以啦~
PS:良好的代碼結構,可以讓代碼重用,並減少耦合,分情況將各個功能寫成短小精悍的小函數,互相調用,既增加可讀性,又增加可維護性,還不容易出bug~這就是UNIX的組織思想吧~

代碼實現

//storage.h
#ifndef STORAGE_H_INCLUDED
#define STORAGE_H_INCLUDED
#include "link.h"

#define SEGMENT_SIZE 1024
#define INIT_SEGMENT_NUM 32
#define MAX_FREE_PART 13    //最大2^13大小的塊

typedef struct {    //夥伴系統
    LinkNode partner[MAX_FREE_PART];   //2^0~2^13=4096 空頭鏈表
}FreeList;

FreeList freeList;
//初始時初始化INIT_SEGMENT_NUM個段,如果新插入的記錄能夠放進段中,則放入,每個段最多放一個,如果放不進去,則放入頁中

void InitStorage(); //初始化儲存引擎
void* InsertToFreeList(unsigned int size,void * pData);  //向儲存引擎插入數據
void Free(void* pData); //回收內存

#endif // STORAGE_H_INCLUDED
//link.h
#ifndef LINK_H_INCLUDED
#define LINK_H_INCLUDED

typedef struct _LinkNode{
    void* pData;    //鏈表內容
    struct _LinkNode * next;    //下一個節點
} LinkNode;

#endif // LINK_H_INCLUDED
//storage.c
#include "storage.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void* GetFree(unsigned int);

void InsertFree(unsigned n,void *pData) //向2^n*seg區插入一段空閒空間
{
    LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));  //插入鏈表第一個節點
    p->pData = pData;
    p->next = freeList.partner[n].next;
    freeList.partner[n].next = p;
    return;
}

void* Splice(unsigned int  n)   //分裂節點並返回分裂出的一個節點的指向內存,另一個節點插入鏈表相應位置
{
    if(n > MAX_FREE_PART)   //請求大於最大支持的內存塊
    {
        LinkNode* p = &freeList.partner[MAX_FREE_PART - 1]; //是否是最大的塊內存不足
        if(p->next) //最大的塊還有剩餘~說明請求的內存太大了,不支持
        {
            fprintf(stderr,"Max support memory part is 2 ^ %d!\n",MAX_FREE_PART);
            exit(-1);
        }
        //分裂超出最大塊出現在最大塊已經不足的情況下,那麼返回新申請的最大塊給他即可
        return malloc(SEGMENT_SIZE << MAX_FREE_PART);
    }
    else
    {
        void* pBig = GetFree(n);    //索取n的空間並分裂
        void* pHalf = pBig + (SEGMENT_SIZE << (n-1));
        InsertFree(n-1,pHalf);
        return pBig;
    }
}

void* GetFree(unsigned int n)   //向空閒鏈表索取n*segment的空間
{
    LinkNode* p = &freeList.partner[n];
    if(p->next) //第一個節點就有空間
    {
        LinkNode *tn = p->next;
         void *t = tn->pData;
        p->next = p->next->next;    //取出並返回
        free(tn);
        return t;
    }
    else
    {
        return Splice(n+1); //要求分裂更大的塊
    }
}

void* InsertToFreeList(unsigned int size,void * pData)   //使用空閒空間鏈表的一小段空間
{
    unsigned int n = size / SEGMENT_SIZE;
    unsigned int i = 0;
    while(n)
    {
        n >>= 1;
        i++;
    }
    void *p = GetFree(n);   //獲取空間
    memset(p,i,1);  //空間第一個字節寫入空間大小,爲了垃圾回收
    memcpy(p+1,pData,size); //其餘空間寫入數據
    return p;
}

void Free(void* pData)
{
    unsigned int n = *((char*)pData);   //讀出第一個字節表示的該段內存大小
    InsertFree(n,pData);    //回收內存
}

void InitStorage()
{
    //TODO:初始化一些小的內存片段供使用
}

經測試,緩存池分配10000次內存比直接調用malloc分配10000次快120倍左右
另外補充將小塊內存合併的代碼:

void InsertFree(unsigned n,void *pData) //向2^n*seg區插入一段空閒空間
{
    if(n > MAX_FREE_PART)
    {
        free(pData);
        return;
    }
    LinkNode* t = &freeList.partner[n];
    LinkNode* parent = t;
    t = t->next;
    while(t && t->pData < pData)            // 尋找相鄰塊併合並
    {
        if(t->pData + (SEGMENT_SIZE << n) == pData)
        {
            parent->next = t->next;
            //printf("combine");
            InsertFree(n+1,t->pData);   //Combine
            free(t);
            return;
        }
        parent = t;
        t = t->next;
    }
    if(t && t->pData == pData + (SEGMENT_SIZE << n))
    {
        //printf("combine");
        InsertFree(n+1,pData);  //合併並插入上個鏈表
    }
    else
    {
        if(!t)
        {
            LinkNode* p = malloc(sizeof(LinkNode));
            p->next = NULL;
            p->pData = pData;
           parent->next = p;
        }
        else
        {
            LinkNode* p = malloc(sizeof(LinkNode));
            p->next = t->next;
            p->pData = pData;
            t->next = p;
        }
    }
    return;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章