用户程序内存分配缓存简易实现

在应用程序中,有时分配或首次写内存时会卡顿耗时。此文欲尝试避免该问题,想到以下方式。

  • 内存片段 —— 以指定大小内存片段为单位管理
  • 满页管理 —— 满页部分内存以多页为单位管理

1 内存片段

以指定大小内存片段实现内存分配缓存较简单,即直接组织、分配、释放指定大小的内存片段即可。

伪码如下。

/**
 * memca.h
 * 内存片段内存分配 缓存接口声明及
 * 公共类型、代码实现
 */
#ifndef MEMCA_H
#define MEMCA_H

#include <inttypes.h>
#include <pthread.h>
#include <stdlib.h>

#define ma_max(a, b)    ((a) > (b) ? (a) : (b))
#define ma_lock(ma)     pthread_mutex_lock(&(ma)->lock)
#define ma_unlock(ma)   pthread_mutex_unlock(&(ma)->lock)
#define ma_lock_init(m) ({\
    (m) = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;\
})

struct memca {
    const char *name;
    uint16_t size;

    pthread_mutex_t lock;
    uint32_t max;
    uint32_t num;
    void *head;
};

void mca_init (struct memca *ma);
void mca_close(struct memca *ma);

void * mca_alloc(struct memca *ma);
void   mca_free (struct memca *ma, void *v);

static inline 
void * ma_alloc(struct memca *m, void **h, uint32_t dec)
{
    void *v;

    if ((v=*h)) {
        *h = *(void **)v;
        m->num -= dec;
    }
    return v;
}

static inline 
void ma_free(struct memca *m, void **h, void *v, int nochk)
{
    if (nochk || m->num < m->max) {
        *(void **)v = *h;
        m->num += !nochk;
        *h = v;
        v = NULL;
    }
    free(v);
    return ;
}
#endif
/**
 * memca.c
 * 内存片段分配 缓存管理实现
 */
#include "memca.h"
#include <stdlib.h>

void mca_init(struct memca *ma)
{
    uint16_t size = ma_max(ma->size, sizeof(void *));
    uint32_t max  = ma->max;
    void *last = NULL;

    ma_lock_init(ma->lock);
    ma->head = NULL;
    ma->size = size;
    ma->num = 0;

    while (max--) {
        void *v = malloc(size);
        if (!v)
            return ;
        if (!last) {
            ma->head = last = v;
        } else {
            *(void **) last = v;
            last = v;
        }
        *(void **)v = NULL;
        ma->num += 1;
    }
    return ;
}

void mca_close(struct memca *ma)
{
    void *v;

    while ((v=ma->head)) {
        ma->head = *(void **)v;
        ma->num -= 1;
        free(v);
    }
    return ;
}

void * mca_alloc(struct memca *ma)
{
    void *v;

    ma_lock(ma);
    v = ma_alloc(ma, &ma->head, 1);
    ma_unlock(ma);
    return v ?: malloc(ma->size);
}

void mca_free(struct memca *ma, void *v)
{
    ma_lock(ma);
    ma_free(ma, &ma->head, v, 0);
    ma_unlock(ma);
    return ;
}

2 满页管理

将满足 n n n 页的 x x x 个指定大小内存片段集中到页中进行管理,不满一页的内存片段采取上一节的管理方式。

伪码如下。

/**
 * pageca.h
 * 声明内存片段连续管理类型及接口
*/
#ifndef PAGECA_H
#define PAGECA_H

#include "memca.h"

struct pageca {
    struct memca ma;
    void *page;
    void *start, *end;
};

void pca_init (struct pageca *pa);
void pca_close(struct pageca *pa);

void * pca_alloc(struct pageca *pa);
void   pca_free (struct pageca *pa, void *v);

#endif

/**
 * pageca.c
 * 连续内存片段管理实现
 */
#include <unistd.h>
#include <string.h>
#include "memca.h"
#include "pageca.h"

static unsigned pagesize;
#define pagesize()  ({          \
    pagesize ?                  \
    pagesize :                  \
    (pagesize=getpagesize());   \
})
#define pa_to_ma(pa)     ((struct memca*)(pa))
#define pa_real_size(ma) ({ma_max(ma->size, sizeof(void*));})

void pca_init(struct pageca *pa)
{
    struct memca *ma = pa_to_ma(pa);
    uint16_t size = pa_real_size(ma );
    uint64_t pgnr = size * ma->max / pagesize();
    void *s, *e;

    ma->max -= pgnr * pagesize() / size;;
    mca_init(ma);

	if (!pngr) reurn ;
    s = malloc(pgnr * pagesize());
    pa->page = pa->start = s;
    e = pa->end = (char*)s + pgnr*pagesize();
    if (!s)
        return ;
    for (/* none */; s < e; s += size) {
        if (s + (size<<1) > pa->end)
            break;
        *(void**) s = s + size;
    }
    *(void**) s = NULL;
    return ;
}

void pca_close(struct pageca *pa)
{
    mca_close(pa_to_ma(pa));
    free(pa->start);
    pa->page =    \
    pa->start = pa->end = NULL;
    return ;
}

void * pca_alloc(struct pageca *pa)
{
    void *v;
    struct memca *ma = pa_to_ma(pa);

    ma_lock(ma);
    v = ma_alloc(ma, &pa->page, 0);
    ma_unlock(ma);

    return v ?: mca_alloc(ma);
}

void pca_free(struct pageca *pa, void *v)
{
    struct memca *ma = pa_to_ma(pa);

    if (pa->start <= v && v < pa->end) {
        ma_lock(ma);
        ma_free(ma, &pa->page, v, 1);
        ma_unlock(ma);
        return ;
    }
    mca_free(ma, v);
    return ;
}

void * pca_swap(struct pageca *pa, void *v)
{
    void *vp;
    struct memca *ma = pa_to_ma(pa);

    if (pa->start <= v && v < pa->end)
        return v;
    vp = ma_alloc(ma, &pa->page, 0);
    if (!vp)
        return v;
    memcpy(vp, v, ma->size);
    ma_free(ma, &ma->head, v, 1);
    return vp;
}

3 优劣分析

  • 上述两种方法皆不需目标内存片段额外提供内存用于其管理,由此内部会限制最小内存片段为指针大小
  • 以指定大小内存片段为单位缓存内存分配时,在诸如大并发做初始化操作即memca_init 时,memca 内部管理的内存片段可能会位于其他模块的物理页上,若 memca 内的内存片段一直处于缓存状态,则会导致未释放内存片段对应物理页不会被释放;可以在小并发阶段执行 mca_init 操作或者将 memca.max 的值设置得比欲用内存片段数小一些,由此在超缓存分配后调用释放即 mca_free 时刺激一些内存片段的真正释放,也可以尝试用满页管理方式代替代码片段策略
  • 当同时出现满页及零散内存片段管理时,若某阶段分配了超过缓存数 max 的内存,下一某阶段所释放内存都是整页管理范围内的内存,则会导致超过缓存数的内存片段不能释放,可以在内存片段释放即 pca_free 后通过尝试检查通过 pca_alloc 分配结点是否在满页范围内,不在该范围则尝试调用 pca_swap 来让满页内存中的内存片段代替,然后释放掉该零散内存,这样就可以释放掉超过缓存数的内存,不会让内存一直处于上升态的风险中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值