Как на самом деле работает synchronized_pool_resource?

#c #c 17 #allocator #c pmr

#c #c 17 #распределитель #c pmr

Вопрос:

Я изучаю полиморфное распределение памяти в C 17. Я изменил пример, который использует monotonic_buffer_resource для выделения векторов, чтобы использовать synchronized_pool_resource. Я обнаружил странное поведение. В частности, выделено много памяти, только для двух дополнений в векторе. Я не запускал тесты, но я думаю, что это огромный штраф за производительность

Программа была скомпилирована с использованием O2 g -std=c 17 -O2 -Wall -pedantic

Ниже приведен код

 class debug_resource : public std::pmr::memory_resource {

public:
    explicit debug_resource(std::string name,
        std::pmr::memory_resource* up = std::pmr::get_default_resource())
        : _name{ std::move(name) }, _upstream{ up }
    { }

    void* do_allocate(size_t bytes, size_t alignment) override {
        std::cout << _name << " do_allocate(): " << bytes << 'n';
        void* ret = _upstream->allocate(bytes, alignment);
        return ret;
    }
    void do_deallocate(void* ptr, size_t bytes, size_t alignment) override {
        std::cout << _name << " do_deallocate(): " << bytes << 'n';
        _upstream->deallocate(ptr, bytes, alignment);
    }
    bool do_is_equal(const std::pmr::memory_resourceamp; other) const noexcept override {
        return this == amp;other;
    }

private:
    std::string _name;
    std::pmr::memory_resource* _upstream;
};
int main()
{
  
    debug_resource default_dbg{ "default" };
    std::pmr::synchronized_pool_resource pool(amp;default_dbg);
  //  debug_resource dbg{ "pool", amp;pool };
    std::pmr::vector<std::string> strings{ amp;pool };

   strings.emplace_back("Hello Short String");
   strings.emplace_back("Hello Short String 2");
}
  

Вывод консоли следующий

по умолчанию do_allocate(): 32
по умолчанию do_allocate(): 528
по умолчанию do_allocate(): 32
по умолчанию do_allocate(): 528
по умолчанию do_allocate(): 1000
по умолчанию do_allocate(): 192
по умолчанию do_allocate(): 968
по умолчанию do_allocate(): 192

по умолчанию do_deallocate(): 528
по умолчанию do_deallocate(): 32
по умолчанию do_deallocate(): 1000
по умолчанию do_deallocate(): 192
по умолчанию do_deallocate(): 968
по умолчанию do_deallocate(): 192
по умолчанию do_deallocate(): 528
по умолчанию do_deallocate(): 32

Комментарии:

1. чтобы увидеть дополнительные выделения для строк, вы должны использовать std::pmr::string в качестве типа для элементов, а не std::string

Ответ №1:

Ответ содержится в описании функции:https://en.cppreference.com/w/cpp/memory/synchronized_pool_resource

Он состоит из коллекции пулов, которые обслуживают запросы для блоков разного размера. Каждый пул управляет коллекцией блоков, которые затем разделяются на блоки одинакового размера.

Вызовы do_allocate отправляются в пул, обслуживающий наименьшие блоки, соответствующие запрошенному размеру.

Исчерпание памяти в пуле приводит к тому, что следующий запрос на выделение для этого пула выделяет дополнительный фрагмент памяти из вышестоящего распределителя для пополнения пула. Полученный размер блока увеличивается геометрически.

Наибольший размер блока и максимальный размер блока могут быть настроены путем передачи структуры std::pmr::pool_options в его конструктор.

Итак, пул на самом деле представляет собой набор блоков памяти. И эта коллекция увеличивается при необходимости. Отсюда множественные выделения.

Чтобы уменьшить количество выделений, вы можете попробовать поиграть с std::pmr::pool_options.