Пользовательский распределитель памяти в пуле с realloc

Я создаю pooled_allocator для выделения памяти для моих компонентов определенного типа в одном блоке памяти. Как я могу обновить уже выделенные PTR, хранящиеся в каком-либо контейнере?

Я реализовал ECS для своей игры с общим подходом. я использовал

std::unordered_map<TypeIndex, vector<BaseComponent*>>

для хранения моих компонентов по типу.

И именно так я сейчас распределяю память для компонентов.

template<typename T, typename... Args>
T* Entity::assign(Args&&... args)
{
    //....
    // if there are no components of this type yet
    std::allocator<T> alloc;
    T* comp = std::allocator_traits<std::allocator<T>>::allocate(alloc, 1);

    std::allocator_traits<std::allocator<T>>::construct(alloc, comp, T(args...));

    // get container for type and add pointer to newly allocated component, that has to be stored in pool
    container.push_back(comp);
    components.insert({ getTypeIndex<T>(), container });
}

Итак, теперь я хочу реализовать некоторый pooled_allocator, который должен соответствовать всем требованиям std::allocator_traits, чтобы использовать его в качестве распределителя для моих компонентов. Но я хочу сделать его динамическим, чтобы он мог расширять свой блок внутренней памяти.

Что у меня есть на данный момент?

template<typename T, unsigned int Size = 10>
class pooled_allocator
{
    // some typedefs
    typedef value_type * pointer;

    static void* m_memoryBlock; // malloc(SIZE*Size) or nullptr to reproduce problem
    static std::list<MyBlockNode> m_allocated;
    static const size_t SIZE; // sizeof(T)

    // some methods

    static pointer allocate(const size_t amount)
    {
        if (!m_memoryBlock)
            m_memoryBlock = malloc(amount * SIZE);
        else
        {
            // here realloc can return another pointer
            m_memoryBlock = realloc(m_memoryBlock, m_allocated.size() * SIZE + amount * SIZE);         
        }

        int index = m_allocated.size();
        m_allocated.push_back(MyBlockNode { });

        pointer elPointer = (pointer)((pointer)m_memoryBlock + index * SIZE);

        return elPointer;
    }

    template <class Up, class... Args>
    void construct(Up* p, Args&&... args)
    {
       new((void*)p) Up(std::forward<Args>(args)...);
    }
}

Проблема заключается в том, что realloc возвращает указатель на другой выделенный блок (другой указатель), уже выделенные объекты скопированы в новое место, и все ptrs, хранящиеся внутри компонентов [typeIndex()], становятся недействительными (

Как я могу это исправить? Один из вариантов — вернуть какой-нибудь ComponentHandle вместо T* и, если блок внутренней памяти переместился, обновить все уже возвращенные дескрипторы новыми указателями, но это снизит скорость выделения и наложит некоторые ограничения.

И я знаю о boost::pool_allocator, но моя игра слишком мала для интеграции таких библиотек.


person Alex May    schedule 25.01.2019    source источник


Ответы (1)


Вы создали свою проблему, определяя собственное распределение памяти таким образом.

Решение состоит в том, чтобы не перераспределять, а выделять новый блок, когда это необходимо, и управлять всеми этими блоками.

Но главный вопрос заключается в том, почему вы это делаете? Вы, вероятно, выбрали этот путь, поэтому решите другую проблему, какую именно?

person bruno    schedule 25.01.2019
comment
- уменьшить количество новых, удалить вызовы. - иметь один блок, в котором компоненты размещаются один за другим в памяти для быстрой итерации и уменьшения промахов кеша - person Alex May; 25.01.2019
comment
Вы пытались использовать new/delete ? Вы уверены, что ваша проблема с производительностью исходит от них? - person bruno; 25.01.2019
comment
конечно я их пробовал. в настоящее время я использую std::allocator_traits‹std::allocator‹T›› - person Alex May; 25.01.2019