简单的内存池实现gko_alloc

在用gpreftools优化gko_pool的时候我发现一个问题,malloc竟然成了性能瓶颈

由于在每个连接建立的时候gko_pool默认会为读写各分配2KB的buf备用,这个是比较固定的

每个连接的的生命周期会伴随着4KB大小的内存malloc & free

正好可以写个只能分配固定大小内存的“内存池”,基本思路就是每次分配一个大内存bucket(64MB),需要4KB的块的时候就从bucket中取,当bucket没有可用slot就再分配一个新的bucket,当bucket的所有slot都空闲就把bucket整体free。bucket中寻找槽位用的二分查找实现,每个bit对应一个slot是否被占用,废话不说 上代码:

memory.h

/*
 * memory.h
 *
 *  Created on: Jun 8, 2012
 *      Author: auxten
 */

#ifndef GKO_MEMORY_H_
#define GKO_MEMORY_H_

#include <sys/types.h>
#include <pthread.h>

static const u_int32_t      SLOT_SIZE       =   4 * 1024;                   /// one page is good
static const u_int32_t      SLOT_COUNT      =   1 * 1024 * 1024;
static const u_int32_t      M_MAP_SIZE      =   SLOT_COUNT / sizeof(u_int8_t);  /// bitmap
static const u_int32_t      BUCKET_SIZE     =   64 * 1024 * 1024;
static const int32_t        BUCKET_CAPACITY =   BUCKET_SIZE / SLOT_SIZE; /// capacity, 4096
static const int32_t        BUCKET_COUNT    =   SLOT_COUNT / BUCKET_CAPACITY; /// 256
static const int            INVILID_BLOCK   =   -1;

class gkoAlloc
{
private:
    pthread_mutex_t alloc_lock;
    u_int8_t m_map[M_MAP_SIZE]; /// 1MB can fit L2 cache
    void *  bucket_s[BUCKET_COUNT];
    int16_t bucket_used[BUCKET_COUNT];
    int latest_bucket;
    int get_bit(u_int8_t * b);
    int free_bit(u_int8_t * b, int index);

public:
    gkoAlloc(void);
    int get_block(void);
    int get_clear_block(void);
    int get2x_block(int block_id);
    void free_block(int block_id);
    int clear_block(void *block, int c, size_t size);
    void * id2addr(int block_id);
};

#endif /* GKO_MEMORY_H_ */

memory.cpp

/*
 * memory.cpp
 *
 *  Created on: Jun 8, 2012
 *      Author: auxten
 *
 *  only support little endian : x86
 */

//#define MEM_TEST
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>

#include "memory.h"

gkoAlloc::gkoAlloc(void)
{
    pthread_mutex_init(&alloc_lock, NULL);
    memset((void *) m_map, 0, M_MAP_SIZE * sizeof(u_int8_t));
    memset((void *) bucket_s, 0, BUCKET_COUNT * sizeof(void *));
    memset((void *) bucket_used, 0, BUCKET_COUNT * sizeof(int16_t));
    latest_bucket = 0;
}

void * gkoAlloc::id2addr(int block_id)
{
    if (block_id < 0)
        return NULL;

    int bucket_no = block_id / BUCKET_CAPACITY;
    int bucket_offset = SLOT_SIZE * (block_id % BUCKET_CAPACITY);

    return ((char *)bucket_s[bucket_no]) + bucket_offset;
}

int gkoAlloc::get_bit(u_int8_t * b)
{
    /**
     *  idx     01234567
     *  byte    11111010
     *
     *  return 5 and
     *  byte    11111110
     */

    int i;
    for (i = 0; i < 8; i++)
    {
        if ((u_int8_t)((*b >> 7 - i) << 7) == (u_int8_t)0u)
            break;
    }

    *b |= (u_int8_t)( 1u << 7 - i);

    return i;
}

int gkoAlloc::free_bit(u_int8_t * b, int index)
{
    /**
     *  idx     01234567
     *  byte    11111110
     *
     *  return 5 and
     *  byte    11111010
     */

    *b ^= (u_int8_t)( 1u << 7 - index);

    return index;
}

int gkoAlloc::get_block(void)
{
    int i;
    int the_bucket;
    int idx = INVILID_BLOCK;
    u_int8_t * p_idx;
    u_int64_t * bucket_start_idx;
    u_int64_t * bucket_end_idx;
    u_int64_t * bucket_idx;

    pthread_mutex_lock(&alloc_lock);
    for (i = 0; i < BUCKET_COUNT; i++)
    {
        the_bucket = (latest_bucket + i) % BUCKET_COUNT;
        if (bucket_used[the_bucket] < BUCKET_CAPACITY)
        {
            latest_bucket = the_bucket;
            break;
        }
    }

    if (i == BUCKET_COUNT)
    {
        fprintf(stderr, "out of memory in pool\n");
//        GKOLOG(FATAL, "out of memory in pool");
        idx = INVILID_BLOCK;
        goto GET_BLOCK_RET;
    }

    if (!bucket_s[the_bucket])
    {
        void * ptr;
        if (!posix_memalign(&ptr, SLOT_SIZE, BUCKET_SIZE))
        {
            bucket_s[the_bucket] = ptr;
            bucket_used[the_bucket] = 0;
        }
        else
        {
            fprintf(stderr, "posix_memalign fail\n");
//            GKOLOG(FATAL, "posix_memalign fail");
            idx = INVILID_BLOCK;
            goto GET_BLOCK_RET;
        }
    }

    bucket_start_idx = (u_int64_t *) &(this->m_map[the_bucket * BUCKET_CAPACITY / 8]);
    bucket_end_idx = (u_int64_t *) &(this->m_map[(the_bucket + 1) * BUCKET_CAPACITY / 8]);
    for (bucket_idx = bucket_start_idx;
            bucket_idx < bucket_end_idx;
            bucket_idx++)
    {
        if (*(u_int64_t *) bucket_idx != ~0uLL)
        {
            if (*(u_int32_t *) bucket_idx != ~0u)
            {
                if (*((u_int16_t *) bucket_idx) != (u_int16_t) ~0u)
                {
                    if (*(u_int8_t *) bucket_idx != (u_int8_t) ~0u)
                    {
                        p_idx = (u_int8_t *) bucket_idx + 0;
                    }
                    else
                    {
                        p_idx = (u_int8_t *) bucket_idx + 1;
                    }
                }
                else
                {
                    if (*((u_int8_t *) bucket_idx + 2) != (u_int8_t) ~0u)
                    {
                        p_idx = (u_int8_t *) bucket_idx + 2;
                    }
                    else
                    {
                        p_idx = (u_int8_t *) bucket_idx + 3;
                    }

                }
            }
            else
            {
                if (*((u_int16_t *) bucket_idx + 2) != (u_int16_t) ~0u)
                {
                    if (*((u_int8_t *) bucket_idx + 4) != (u_int8_t) ~0u)
                    {
                        p_idx = (u_int8_t *) bucket_idx + 4;
                    }
                    else
                    {
                        p_idx = (u_int8_t *) bucket_idx + 5;
                    }
                }
                else
                {
                    if (*((u_int8_t *) bucket_idx + 6) != (u_int8_t) ~0u)
                    {
                        p_idx = (u_int8_t *) bucket_idx + 6;
                    }
                    else
                    {
                        p_idx = (u_int8_t *) bucket_idx + 7;
                    }

                }
            }
            idx = get_bit(p_idx) +
                    8 * (p_idx - (u_int8_t *) bucket_start_idx) +
                    the_bucket * BUCKET_CAPACITY;
            bucket_used[the_bucket] ++;
            break;
        }
        else
        {
            continue;
        }
    }

GET_BLOCK_RET:
    pthread_mutex_unlock(&alloc_lock);
    return idx;
}

void gkoAlloc::free_block(int block_id)
{
    if (block_id < 0)
        return;

    int bucket_no = block_id / BUCKET_CAPACITY;

    pthread_mutex_lock(&alloc_lock);
    free_bit(&m_map[block_id / 8], block_id % 8);

    if(--bucket_used[bucket_no] == 0)
    {
        free(bucket_s[bucket_no]);
        bucket_s[bucket_no] = NULL;
    }
    else
    {
        latest_bucket = bucket_no;
    }

    pthread_mutex_unlock(&alloc_lock);

}

#ifdef MEM_TEST
int main()
{
    gkoAlloc mem;
    for (int i = 0; i < BUCKET_CAPACITY - 1; i++)
    {
        int k = mem.get_block();
        printf("%d, %d\n", i, k);
        if (i != k)
        {
            break;
        }
    }
    int blk1 = mem.get_block();
    int blk2 = mem.get_block();
    int blk3 = mem.get_block();
    printf("%p\n", mem.id2addr(blk1));
    printf("%p\n", mem.id2addr(blk2));
    printf("%p\n", mem.id2addr(blk3));
    mem.free_block(blk1);
    mem.free_block(blk2);
    mem.free_block(blk3);
    return 0;
}
#endif

编译测试:

g++ -DMEM_TEST memory.cpp -o memory && ./memory

需要的拿去

github:https://github.com/auxten/gkoAlloc

时间: 2024-10-12 20:13:42

简单的内存池实现gko_alloc的相关文章

简单实现内存池

#include "common.h" #include "pool.h" #include <assert.h> static inline void *objmem_to_obj(void *objmem) { return objmem + sizeof(pool_obj_head_t); } static inline void *obj_to_objmem(void *obj) { return obj - sizeof(pool_obj_he

C++实现的简单的内存池

用空闲链表的方式组织一连串的分配的空间,且在此程序中仅支持内置类型.只是实现了简单的分配和回收. #include<iostream> #include<assert.h> #include<stdlib.h> using namespace std; int const MAX=100; struct block{ block *next; block *addr; explicit block(int _size):next(NULL){addr=(block*)m

简单的内存池实现

1 #ifndef _MEMPOOL_H_ 2 #define _MEMPOOL_H_ 3 #include<iostream> 4 template<typename T> 5 class CMemPool{ 6 private: 7 CMemPool<T>* m_pFreeList; 8 public: 9 enum{EAXPANSION = 32}; 10 CMemPool(unsigned int nItemCount = EAXPANSION){ 11 Exp

重写boost内存池

最近在写游戏服务器网络模块的时候,需要用到内存池.大量玩家通过tcp连接到服务器,通过大量的消息包与服务器进行交互.因此要给每个tcp分配收发两块缓冲区.那么这缓冲区多大呢?通常游戏操作的消息包都很小,大概几十字节.但是在玩家登录时或者卡牌游戏发战报(将整场战斗打完,生成一个消息包),包的大小可能达到30k或者更大,取决于游戏设定.这些缓冲区不可能使用glibc原始的new.delete来分配,这样可能会造成严重的内存碎片,并且效率也不高. 于是我们要使用内存池.并且是等长内存池,即每次分配的内

一个简易内存池(C++)

做这个内存池主要是为了完成一道面试题,题目在代码中. 代码 1 #include <iostream> 2 #include<string> 3 #include <list> 4 using namespace std; 5 6 //一个简单的内存池,池中保存3块内存分别为1k,2k,4k 7 //实现池子的malloc(size)和free(void*)操作 8 //不考虑跨块申请内存情况 9 10 class node 11 { 12 public: 13 int

内存池的实现(一)

1.引言 C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存.追踪内存的分配.在不需要的时候释放内存——这个任务相当复杂.而直接使用系统调用malloc/free.new/delete进行内存分配和释放,有以下弊端: A.调用malloc/new,系统需要根据“最先匹配”.“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销 B.频繁使用时会产生大量内存碎片,从而降低程序运行效率 C.容易造成内存泄漏

简单内存池实现

#ifndef __MEM_POOL_H__ #define __MEM_POOL_H__ typedef struct tagMemItem { int nSize; int nUsed; void* addr; }MEM_ITEM_S; /***************************************************************************************************** * 4M memory pool * +------

【源码剖析】MemoryPool —— 简单高效的内存池 allocator 实现

什么是内存池?什么是 C++ 的 allocator? 内存池简单说,是为了减少频繁使用 malloc/free new/delete 等系统调用而造成的性能损耗而设计的.当我们的程序需要频繁地申请和释放内存时,频繁地使用内存管理的系统调用可能会造成性能的瓶颈,嗯,是可能,毕竟操作系统的设计也不是盖的(麻麻说把话说太满会被打脸的(⊙v⊙)).内存池的思想是申请较大的一块内存(不够时继续申请),之后把内存管理放在应用层执行,减少系统调用的开销. 那么,allocator 呢?它默默的工作在 C++

简单内存池

System Call 先测试系统调用new/delete的用时. #include <iostream> #include <time.h> using namespace std; timespec diff(timespec start, timespec end) { timespec temp; if((end.tv_nsec-start.tv_nsec)<0) { temp.tv_sec = end.tv_sec-start.tv_sec-1; temp.tv_n