Реализовать собственный пул памяти

Я хочу выделить определенный объем памяти и использовать эту память для остальной части программы. Программа будет в основном выделять память для нескольких строк и структур. Как это реализовать? Какие структуры данных используются для хранения указателей и как их использовать, чтобы дать мне определенную сумму?

Например, если я malloc 1 МБ пространства и его в указателе p, как я могу вырезать из него 250 КБ?

Это просто быстрое и грязное выполнение.

Ответ 1

Если вы хотите вернуть память в пул, это становится более сложным. Однако для быстрого и не совсем грязного подхода вы можете захотеть реализовать код, который вы можете использовать снова...

typedef struct pool
{
  char * next;
  char * end;
} POOL;

POOL * pool_create( size_t size ) {
    POOL * p = (POOL*)malloc( size + sizeof(POOL) );
    p->next = (char*)&p[1];
    p->end = p->next + size;
    return p;
}

void pool_destroy( POOL *p ) {
    free(p);
}

size_t pool_available( POOL *p ) {
    return p->end - p->next;
}

void * pool_alloc( POOL *p, size_t size ) {
    if( pool_available(p) < size ) return NULL;
    void *mem = (void*)p->next;
    p->next += size;
    return mem;
}

По моему опыту, когда я использую такие пулы для выделения многих объектов, я хочу заранее рассчитать, сколько памяти потребуется, чтобы я не был расточительным, но я также не хочу делать какие-либо ошибки (например, не выделяя enoudh). Поэтому я помещаю весь код выделения внутри цикла и настраиваю функции распределения пула, чтобы принять флаг, который выполняет распределение 'dummy' в пустом пуле. Во второй раз по циклу я уже вычислил размер пула, чтобы я мог создать пул и делать реальные распределения с одинаковыми вызовами функций и не дублировать код. Вам нужно будет изменить мой предложенный код пула, потому что вы не можете сделать это с помощью арифметики указателя, если память не была выделена.

Ответ 2

Управление памятью с использованием пула памяти -

Пул памяти - это способ предварительного выделения блоков памяти того же размера. Например, различные объекты одного класса. Таким образом, речь идет о разработке "Модели памяти" для вашего программного обеспечения.

Пример. Анимированный gif имеет различные фреймы. Пусть говорят, что для каждого кадра требуется максимум 1024 КБ. Кроме того, если мы знаем, что мы можем иметь максимум два кадра, то мы можем избежать фрагментации, предварительно распределяя память для каждого кадра.

[note] - Пул памяти более применим, когда мы знаем поведение системы во время разработки. Таким образом, концепция пула памяти не применима повсеместно.   // ================================================ ============================   // Имя: MemoryPool.cpp   // Автор:   // Версия:   // Авторское право: SHREYAS JOSHI   // Описание:   // ================================================ ============================

#include <iostream>
#include <malloc.h>

struct memPool
{

private:

    char *m_poolPtr;
    char *m_nextAvailAddr;
    char *m_endAddr;

public:
    /** Methods for the structure **/
    void poolCreate(size_t size);
    void poolDestroy();
    void * poolAlloc(size_t size);

    memPool():m_poolPtr(NULL),m_nextAvailAddr(NULL),m_endAddr(NULL)
    {
        std::cout<<"memPool constructor Invoked"<<std::endl;
    }

    ~memPool()
    {
        std::cout<<"memPool Destructor Invoked"<<std::endl;
        m_poolPtr = NULL;
        m_nextAvailAddr = NULL;
        m_endAddr = NULL;
    }

};

/** Create a Pool of memory - makes a program hassle free of doing malloc multiple times **/
/** Also, Fragmentation can be avoided with the Memory Pool concept **/
/** A pool concept is useful, when you know at design time.how much memory is required for
the similar type of objects in total**/

void memPool::poolCreate(size_t size)
{
    m_poolPtr = (char *) malloc(size);

    if(m_poolPtr == NULL)
    {
        std::cout<<"Pool Create Failed"<<std::endl;
        //printf("Pool Create Failed \r\n");
    }

    m_nextAvailAddr = m_poolPtr;
    /** note the addressing starts from zero - thus you have already counted zero**/
    m_endAddr = m_poolPtr + size - 1;

    //printf("The Pool Head Pointer = %p \r\n",m_poolPtr);
    std::cout<<"Pool Head Pointer = "<<static_cast<void *>(m_poolPtr)<<std::endl;
    //printf("The Pool m_nextAvailAddr = %p \r\n",m_nextAvailAddr);
    std::cout<<"Pool m_nextAvailAddr = "<<static_cast<void *>(m_nextAvailAddr)<<std::endl;
    //printf("The Pool m_endAddr = %p \r\n",m_endAddr);
    std::cout<<"Pool m_endAddr = "<<static_cast<void *>(m_endAddr)<<std::endl;
}


/** Destroy the entire pool in one shot ********/
void memPool::poolDestroy()
{
    free(m_poolPtr);
    /** Remember free first then assign to NULL **/
    m_poolPtr = NULL;

    /** Update Housekeeping--data structure **/
    m_nextAvailAddr = NULL;
    m_endAddr = NULL;
}

/** Allocate some space from the pool ********/
/** Check if the space is available or not **/
/** Do the housekeeping - update the nextAvail Addr in the structure**/
void * memPool::poolAlloc(size_t size)
{
    void *mem = NULL;

    if( (m_endAddr != NULL) && (m_nextAvailAddr != NULL))
    {

        /** This is according to fencing problem - add 1 when you are find a difference of sequence to calculate the space within **/
        size_t availableSize = m_endAddr - m_nextAvailAddr + 1;

        /** check for the availability **/
        if(size > availableSize )
        {
            //std::cout<<"Warning!! the available size = "<<availableSize<< "requested size = "<<size<<std::endl;
            printf("Warning!! the available size = %u and requested size = %u \r\n",availableSize, size);
            mem = NULL;
        }
        else
        {
            /** store the available pointer to the user**/
            mem = m_nextAvailAddr;
            //printf("The user return pointer is = %p \r\n ",mem);
            std::cout<<"The user return pointer is = "<<static_cast <void *>(mem)<<std::endl;
            /*** advance the next available pointer **/
            m_nextAvailAddr += size;
            //printf("The next available pointer is = %p \r\n ",m_nextAvailAddr);
            std::cout<<"The next available pointer is = "<<static_cast<void *>(m_nextAvailAddr)<<std::endl;
        }

    }

    return mem;
}

int main(int argc, char *argv[])
{
    memPool gifAnimatedImageFramesBlk;

    /** Let say each frame needs 512 kb **/
    char *gifFrame1 = NULL;
    char *gifFrame2 = NULL;

    char *gifFrame3 = NULL;

    /** 1 MB Pool for the GIF IMAGE FRAMES **/
    gifAnimatedImageFramesBlk.poolCreate(1024*1024*1024);
    /*** 512 KB **/
    gifFrame1 = (char *)gifAnimatedImageFramesBlk.poolAlloc(512*1024*1024);
    //printf("Got the gifFrame1..pointer- == %p \r\n ",gifFrame1);
    std::cout<<"Got the gifFrame1..pointer- == "<<static_cast<void *>(gifFrame1)<<std::endl;


    /** again 512 MB **/
    gifFrame2 = (char *)gifAnimatedImageFramesBlk.poolAlloc(512*1024*1024);

    std::cout<<"Got the gifFrame2..pointer- == "<<static_cast<void *>(gifFrame2)<<std::endl;

    //printf("Got the gifFrame2..pointer- == %p \r\n ",gifFrame2);

    /*************Exhausted the pool memory now **************/

    /** This will fail ****************/
    gifFrame3 = (char *)gifAnimatedImageFramesBlk.poolAlloc(1);

    std::cout<<"Got the gifFrame3..pointer- == "<<static_cast<void *>(gifFrame3)<<std::endl;
    //printf("Got the gifFrame3..pointer- == %p \r\n ",gifFrame3);

    /*****Destroy the Pool now *****************/
    gifAnimatedImageFramesBlk.poolDestroy();

    gifFrame3 = (char *)gifAnimatedImageFramesBlk.poolAlloc(1);

    std::cout<<"Got the gifFrame3..pointer- == "<<static_cast<void *>(gifFrame3)<<std::endl;

    //printf("Got the gifFrame3..pointer- == %p \r\n ",gifFrame3);

    gifFrame3 = (char *)gifAnimatedImageFramesBlk.poolAlloc(1);

    std::cout<<"Got the gifFrame3..pointer- == "<<static_cast<void *>(gifFrame3)<<std::endl;
    //printf("Got the gifFrame3..pointer- == %p \r\n ",gifFrame3);


    return 0;
}

[note] - Чтобы напечатать значение char * в С++ с помощью ostream:: operator < < < < < w980 > *, должно быть typecasted для void * using static_cast (pointer_Name). Проблема в том, что если компилятор С++ видит char *, то он ищет NULL-терминатор - '\ 0'. В этом случае нет NULL-терминатора '\ 0'. Итак, вы увидите поведение undefined.

Преимущества пула памяти

  • Вы можете избежать фрагментации памяти. Даже если система имеет требуемое пространство памяти, malloc() не будет работать, если желаемый размер смежного блока недоступен.
  • Пространство зарезервировано, и избегаются частые функции malloc() и free(). Это позволит сэкономить время.
  • Когда malloc() вызывается для многих подблоков, административные/метаданные связаны с каждым выделенным подблоком. Это будет потребляют ненужное пространство. Вместо этого одно большое выделение блоков позволит избежать множества административных/метаданных.
  • Если пространство памяти ограничено, тогда легко обнаружить утечки памяти. Если память пущена в пул, тогда пул памяти будет return NULL. Таким образом, вы можете легко изолировать проблему утечки памяти.