namespace std::pmr { // [mem.res.class], class memory_resource class memory_resource; bool operator==(const memory_resource& a, const memory_resource& b) noexcept; bool operator!=(const memory_resource& a, const memory_resource& b) noexcept; // [mem.poly.allocator.class], class template polymorphic_allocator template <class Tp> class polymorphic_allocator; template <class T1, class T2> bool operator==(const polymorphic_allocator<T1>& a, const polymorphic_allocator<T2>& b) noexcept; template <class T1, class T2> bool operator!=(const polymorphic_allocator<T1>& a, const polymorphic_allocator<T2>& b) noexcept; // [mem.res.global], global memory resources memory_resource* new_delete_resource() noexcept; memory_resource* null_memory_resource() noexcept; memory_resource* set_default_resource(memory_resource* r) noexcept; memory_resource* get_default_resource() noexcept; // [mem.res.pool], pool resource classes struct pool_options; class synchronized_pool_resource; class unsynchronized_pool_resource; class monotonic_buffer_resource; }
memory_resource Класс является абстрактным интерфейсом для неограниченного набора классов , заключающих ресурсы памяти.
class memory_resource {
static constexpr size_t max_align = alignof(max_align_t); // exposition only
public:
virtual ~memory_resource();
void* allocate(size_t bytes, size_t alignment = max_align);
void deallocate(void* p, size_t bytes, size_t alignment = max_align);
bool is_equal(const memory_resource& other) const noexcept;
private:
virtual void* do_allocate(size_t bytes, size_t alignment) = 0;
virtual void do_deallocate(void* p, size_t bytes, size_t alignment) = 0;
virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
};
~memory_resource();
void* allocate(size_t bytes, size_t alignment = max_align);
void deallocate(void* p, size_t bytes, size_t alignment = max_align);
bool is_equal(const memory_resource& other) const noexcept;
virtual void* do_allocate(size_t bytes, size_t alignment) = 0;
Returns: Производный класс должен реализовать эту функцию для возврата указателя на выделенный storage ([basic.stc.dynamic.deallocation]) размером не менее bytes. Возвращенное хранилище выравнивается по указанному выравниванию, если такое выравнивание поддерживается ([basic.align]); в противном случае он выравнивается по max_align.
Throws: Реализация производного класса должна генерировать соответствующее исключение, если не может выделить память с запрошенным размером и выравниванием.
virtual void do_deallocate(void* p, size_t bytes, size_t alignment) = 0;
Requires: p должен быть возвращен из предыдущего обращения к allocate(bytes, alignment) ресурсу памяти, равному *this, и память в p еще не должна быть освобождена.
virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
Returns: Производный класс должен реализовать эту функцию для возврата, true если память, выделенная из, this может быть освобождена, other и наоборот, в противном случае false. [ Note: Самый производный тип other может не соответствовать типу this. Для производного класса Dтипичная реализация этой функции немедленно вернет false if dynamic_cast<const D*>(&other) == nullptr. ] — end note
Специализация шаблона класса pmr::polymorphic_allocator соответствует стандарту Allocator requirements. Созданные с использованием разных ресурсов памяти, разные экземпляры одной и той же специализации pmr::polymorphic_allocator могут демонстрировать совершенно разное поведение распределения. Этот полиморфизм времени выполнения позволяет объектам, которые используют, polymorphic_allocator вести себя так, как если бы они использовали разные типы распределителей во время выполнения, даже если они используют один и тот же тип статического распределителя.
template <class Tp> class polymorphic_allocator { memory_resource* memory_rsrc; // exposition only public: using value_type = Tp; // [mem.poly.allocator.ctor], constructors polymorphic_allocator() noexcept; polymorphic_allocator(memory_resource* r); polymorphic_allocator(const polymorphic_allocator& other) = default; template <class U> polymorphic_allocator(const polymorphic_allocator<U>& other) noexcept; polymorphic_allocator& operator=(const polymorphic_allocator& rhs) = delete; // [mem.poly.allocator.mem], member functions Tp* allocate(size_t n); void deallocate(Tp* p, size_t n); template <class T, class... Args> void construct(T* p, Args&&... args); template <class T1, class T2, class... Args1, class... Args2> void construct(pair<T1,T2>* p, piecewise_construct_t, tuple<Args1...> x, tuple<Args2...> y); template <class T1, class T2> void construct(pair<T1,T2>* p); template <class T1, class T2, class U, class V> void construct(pair<T1,T2>* p, U&& x, V&& y); template <class T1, class T2, class U, class V> void construct(pair<T1,T2>* p, const pair<U, V>& pr); template <class T1, class T2, class U, class V> void construct(pair<T1,T2>* p, pair<U, V>&& pr); template <class T> void destroy(T* p); polymorphic_allocator select_on_container_copy_construction() const; memory_resource* resource() const; };
polymorphic_allocator() noexcept;
polymorphic_allocator(memory_resource* r);
template <class U>
polymorphic_allocator(const polymorphic_allocator<U>& other) noexcept;
Tp* allocate(size_t n);
void deallocate(Tp* p, size_t n);
Requires: p был выделен из ресурса памяти x, равного *memory_rsrc, с использованием x.allocate(n * sizeof(Tp), alignof(Tp)).
template <class T, class... Args>
void construct(T* p, Args&&... args);
Requires: Конструкция Uses-allocator T с распределителем resource() (см. [allocator.uses.construction]) И аргументами конструктора std::forward<Args>(args)... сформирована правильно. [ Note: Конструкция Uses-allocator всегда хорошо сформирована для типов, которые не используют распределители. ] — end note
Effects: Создайте T объект в хранилище, адрес которого представлен конструкцией p uses-allocator с resource() аргументами распределителя и конструктора std::forward<Args>(args)....
template <class T1, class T2, class... Args1, class... Args2>
void construct(pair<T1,T2>* p, piecewise_construct_t,
tuple<Args1...> x, tuple<Args2...> y);
[ Note: Этот метод и construct последующие методы являются перегрузками для кусочного построения пар ([pairs.pair]). ] — end note
Effects: Позвольте xprime быть tuple построенным из x согласно соответствующему правилу из следующего списка. [ Note: Следующее описание можно резюмировать как создание pair<T1, T2> объекта в хранилище, адрес которого представлен p, как если бы с помощью отдельной конструкции uses-allocator с allocator resource() ([allocator.uses.construction]), p->first использующей элементы x и p->second элементы y. ] — end note
Если uses_allocator_v<T1,memory_resource*> есть
и
есть ,
значит, есть .
false
is_constructible_v<T1,Args1...> true
xprime x
В противном случае, если uses_allocator_v<T1,memory_resource*> есть
и
есть ,
то есть .
true
is_constructible_v<T1,allocator_arg_t,memory_resource*,Args1...> true
xprime tuple_cat(make_tuple(allocator_arg, resource()), std::move(x))
В противном случае, если uses_allocator_v<T1,memory_resource*> есть
и
есть ,
то есть .
true
is_constructible_v<T1,Args1...,memory_resource*> true
xprime tuple_cat(std::move(x), make_tuple(resource()))
В противном случае программа плохо сформирована.
Позвольте yprime быть кортежем, построенным y согласно соответствующему правилу из следующего списка:
Если uses_allocator_v<T2,memory_resource*> есть
и
есть ,
значит, есть .
false
is_constructible_v<T2,Args2...> true
yprime y
В противном случае, если uses_allocator_v<T2,memory_resource*> есть
и
есть ,
то есть .
true
is_constructible_v<T2,allocator_arg_t,memory_resource*,Args2...> true
yprime tuple_cat(make_tuple(allocator_arg, resource()), std::move(y))
В противном случае, если uses_allocator_v<T2,memory_resource*> есть
и
есть ,
то
есть .
true
is_constructible_v<T2,Args2...,memory_resource*> true
yprime tuple_cat(std::move(y), make_tuple(resource()))
В противном случае программа плохо сформирована.
Затем, используя piecewise_construct, xprimeи yprime в качестве аргументов конструктора, эта функция создает pair<T1, T2> объект в хранилище, адрес которого представлен как p.
template <class T1, class T2>
void construct(pair<T1,T2>* p);
template <class T1, class T2, class U, class V>
void construct(pair<T1,T2>* p, U&& x, V&& y);
Effects: Эквивалентен:
construct(p, piecewise_construct, forward_as_tuple(std::forward<U>(x)), forward_as_tuple(std::forward<V>(y)));
template <class T1, class T2, class U, class V>
void construct(pair<T1,T2>* p, const pair<U, V>& pr);
Effects: Эквивалентен:
construct(p, piecewise_construct, forward_as_tuple(pr.first), forward_as_tuple(pr.second));
template <class T1, class T2, class U, class V>
void construct(pair<T1,T2>* p, pair<U, V>&& pr);
Effects: Эквивалентен:
construct(p, piecewise_construct, forward_as_tuple(std::forward<U>(pr.first)), forward_as_tuple(std::forward<V>(pr.second)));
template <class T>
void destroy(T* p);
polymorphic_allocator select_on_container_copy_construction() const;
memory_resource* resource() const;
template <class T1, class T2>
bool operator==(const polymorphic_allocator<T1>& a,
const polymorphic_allocator<T2>& b) noexcept;
template <class T1, class T2>
bool operator!=(const polymorphic_allocator<T1>& a,
const polymorphic_allocator<T2>& b) noexcept;
memory_resource* new_delete_resource() noexcept;
Returns: Указатель на объект статической продолжительности типа, производного от memory_resource этого, может служить ресурсом для выделения памяти с помощью ::operator new и ::operator delete. При каждом вызове этой функции возвращается одно и то же значение. Для возвращаемого значения p и ресурс памяти r, p->is_equal(r) возвращается &r == p.
memory_resource* null_memory_resource() noexcept;
Returns: Указатель на объект статической продолжительности типа, производного от memory_resource которого allocate() всегда выбрасывает bad_alloc и для которого deallocate() не действует. При каждом вызове этой функции возвращается одно и то же значение. Для возвращаемого значения p и ресурс памяти r, p->is_equal(r) возвращается &r == p.
Это default memory resource pointer указатель на ресурс памяти, который используется некоторыми средствами, когда явный ресурс памяти не предоставляется через интерфейс. Его начальное значение - это возвращаемое значение new_delete_resource().
memory_resource* set_default_resource(memory_resource* r) noexcept;
Effects: Если не r равно нулю, устанавливает значение указателя ресурса памяти по умолчанию на r, в противном случае устанавливает указатель ресурса памяти по умолчанию на new_delete_resource().
Remarks: Вызов set_default_resource и get_default_resource функция не влечет за собой гонку данных. Вызов к set_default_resource функции будет синхронизировать с последующими вызовами set_default_resource и get_default_resource функциями.
memory_resource* get_default_resource() noexcept;
В synchronized_pool_resource и unsynchronized_pool_resource классах (вместе называемые pool resource classes) являются ресурсами памяти общего назначения , имеющие следующие свойства:
Каждый ресурс освобождает свою выделенную память при уничтожении, даже если deallocate не был вызван для некоторых из выделенных блоков.
Ресурс пула состоит из набора pools, обслуживающего запросы для блоков разного размера. Каждый отдельный пул управляет коллекцией, chunks которая, в свою очередь, разделена на блоки одинакового размера, которые возвращаются через вызовы do_allocate. Каждый вызов do_allocate(size, alignment) отправляется в пул, обслуживающий самые маленькие блоки, вмещающие не менее size байтов.
Когда конкретный пул исчерпан, выделение блока из этого пула приводит к выделению дополнительного фрагмента памяти из upstream allocator (предоставленного при построении), таким образом пополняя пул. При каждом последующем пополнении размер получаемого блока увеличивается геометрически. [ Note: Распределяя память по частям, стратегия объединения увеличивает вероятность того, что последовательные распределения будут близки друг к другу в памяти. ] — end note
Запросы на выделение, превышающие самый большой размер блока любого пула, выполняются непосредственно из вышестоящего распределителя.
pool_options Структура может быть передана в конструкторах пул ресурсов для настройки самого большого размера блока и максимальный размер порции.
synchronized_pool_resource Доступ к A может осуществляться из нескольких потоков без внешней синхронизации, и он может иметь пулы, зависящие от потока, для снижения затрат на синхронизацию. К unsynchronized_pool_resource классу нельзя получить доступ из нескольких потоков одновременно, что позволяет полностью избежать затрат на синхронизацию в однопоточных приложениях.
struct pool_options { size_t max_blocks_per_chunk = 0; size_t largest_required_pool_block = 0; }; class synchronized_pool_resource : public memory_resource { public: synchronized_pool_resource(const pool_options& opts, memory_resource* upstream); synchronized_pool_resource() : synchronized_pool_resource(pool_options(), get_default_resource()) {} explicit synchronized_pool_resource(memory_resource* upstream) : synchronized_pool_resource(pool_options(), upstream) {} explicit synchronized_pool_resource(const pool_options& opts) : synchronized_pool_resource(opts, get_default_resource()) {} synchronized_pool_resource(const synchronized_pool_resource&) = delete; virtual ~synchronized_pool_resource(); synchronized_pool_resource& operator=(const synchronized_pool_resource&) = delete; void release(); memory_resource* upstream_resource() const; pool_options options() const; protected: void *do_allocate(size_t bytes, size_t alignment) override; void do_deallocate(void *p, size_t bytes, size_t alignment) override; bool do_is_equal(const memory_resource& other) const noexcept override; }; class unsynchronized_pool_resource : public memory_resource { public: unsynchronized_pool_resource(const pool_options& opts, memory_resource* upstream); unsynchronized_pool_resource() : unsynchronized_pool_resource(pool_options(), get_default_resource()) {} explicit unsynchronized_pool_resource(memory_resource* upstream) : unsynchronized_pool_resource(pool_options(), upstream) {} explicit unsynchronized_pool_resource(const pool_options& opts) : unsynchronized_pool_resource(opts, get_default_resource()) {} unsynchronized_pool_resource(const unsynchronized_pool_resource&) = delete; virtual ~unsynchronized_pool_resource(); unsynchronized_pool_resource& operator=(const unsynchronized_pool_resource&) = delete; void release(); memory_resource *upstream_resource() const; pool_options options() const; protected: void* do_allocate(size_t bytes, size_t alignment) override; void do_deallocate(void* p, size_t bytes, size_t alignment) override; bool do_is_equal(const memory_resource& other) const noexcept override; };
Члены pool_options составляют набор параметров конструктора для ресурсов пула. Влияние каждой опции на поведение ресурсов пула описано ниже:
size_t max_blocks_per_chunk;
Максимальное количество блоков, которые будут одновременно выделены из восходящего ресурса памяти ([mem.res.monotonic.buffer]) для пополнения пула. Если значение max_blocks_per_chunk равно нулю или превышает предел, определенный реализацией, вместо него используется этот предел. Реализация может выбрать использование меньшего значения, чем указано в этом поле, и может использовать разные значения для разных пулов.
size_t largest_required_pool_block;
Наибольший размер выделения, который требуется выполнить с помощью механизма объединения. Попытки выделить один блок, размер которого превышает этот порог, будут выделяться непосредственно из ресурса памяти восходящего потока. Если он largest_required_pool_block равен нулю или превышает предел, определенный реализацией, вместо него используется этот предел. Реализация может выбрать порог прохождения больше, чем указано в этом поле.
synchronized_pool_resource(const pool_options& opts, memory_resource* upstream);
unsynchronized_pool_resource(const pool_options& opts, memory_resource* upstream);
Effects: Создает объект ресурса пула, который будет получать память upstream всякий раз, когда ресурс пула не может удовлетворить запрос памяти из своих собственных внутренних структур данных. Результирующий объект будет содержать копию upstream, но не будет владеть ресурсом, на который upstream указывает. [ Note: Предполагается, что в большинстве случаев звонков на upstream->allocate() адрес будет значительно меньше, чем на this->allocate()адрес. ] Поведение механизма объединения настраивается в соответствии со значением аргумента. — end note opts
Throws: Ничего, разве что upstream->allocate() кидает. Не указано, вызывает ли этот конструктор и при каких условиях upstream->allocate().
virtual ~synchronized_pool_resource();
virtual ~unsynchronized_pool_resource();
void release();
Effects: Вызывает по upstream_resource()->deallocate() мере необходимости для освобождения всей выделенной памяти. [ Note: Память возвращается обратно, upstream_resource() даже если deallocate не была вызвана для некоторых из выделенных блоков. ] — end note
memory_resource* upstream_resource() const;
pool_options options() const;
Returns: Параметры, управляющие поведением пула этого ресурса. Значения в возвращаемой структуре могут отличаться от тех, которые предоставлены конструктору ресурсов пула, поскольку нулевые значения будут заменены значениями по умолчанию, определяемыми реализацией, а размеры могут быть округлены до неопределенной степени детализации.
void* do_allocate(size_t bytes, size_t alignment) override;
Returns: Указатель на выделенное хранилище ([basic.stc.dynamic.deallocation]) размером не менее bytes. Размер и выравнивание выделенной памяти должны соответствовать требованиям для класса, производного от memory_resource.
Effects: Если пул, выбранный для блока размера bytes , не может удовлетворить запрос памяти из своих собственных внутренних структур данных, он вызовет upstream_resource()->allocate() для получения дополнительной памяти. Если bytes он больше, чем тот, который может обработать самый большой пул, то память будет выделена с использованием upstream_resource()->allocate().
void do_deallocate(void* p, size_t bytes, size_t alignment) override;
Effects: Возвращает память в p пул. Не указано, приведет ли эта операция к вызову или при каких обстоятельствах upstream_resource()->deallocate().
bool synchronized_pool_resource::do_is_equal(
const memory_resource& other) const noexcept override;
bool unsynchronized_pool_resource::do_is_equal(
const memory_resource& other) const noexcept override;
A monotonic_buffer_resource - это ресурс памяти специального назначения, предназначенный для очень быстрого выделения памяти в ситуациях, когда память используется для создания нескольких объектов, а затем освобождается сразу после уничтожения объекта ресурса памяти. Обладает следующими качествами:
Обращение к не deallocate имеет никакого эффекта, поэтому объем потребляемой памяти монотонно увеличивается до тех пор, пока ресурс не будет уничтожен.
Программа может предоставить начальный буфер, который распределитель использует для удовлетворения запросов памяти.
Когда исходный буфер (если он есть) исчерпан, он получает дополнительные буферы из upstream ресурса памяти, предоставленного при построении. Каждый дополнительный буфер больше предыдущего в геометрической прогрессии.
Он предназначен для доступа из одного потока управления за раз. В частности, вызовы allocate и deallocate не синхронизируются друг с другом.
Он освобождает выделенную память при уничтожении, даже если deallocate не был вызван для некоторых из выделенных блоков.
class monotonic_buffer_resource : public memory_resource { memory_resource *upstream_rsrc; // exposition only void *current_buffer; // exposition only size_t next_buffer_size; // exposition only public: explicit monotonic_buffer_resource(memory_resource *upstream); monotonic_buffer_resource(size_t initial_size, memory_resource *upstream); monotonic_buffer_resource(void *buffer, size_t buffer_size, memory_resource *upstream); monotonic_buffer_resource() : monotonic_buffer_resource(get_default_resource()) {} explicit monotonic_buffer_resource(size_t initial_size) : monotonic_buffer_resource(initial_size, get_default_resource()) {} monotonic_buffer_resource(void *buffer, size_t buffer_size) : monotonic_buffer_resource(buffer, buffer_size, get_default_resource()) {} monotonic_buffer_resource(const monotonic_buffer_resource&) = delete; virtual ~monotonic_buffer_resource(); monotonic_buffer_resource operator=(const monotonic_buffer_resource&) = delete; void release(); memory_resource* upstream_resource() const; protected: void* do_allocate(size_t bytes, size_t alignment) override; void do_deallocate(void* p, size_t bytes, size_t alignment) override; bool do_is_equal(const memory_resource& other) const noexcept override; };
explicit monotonic_buffer_resource(memory_resource* upstream);
monotonic_buffer_resource(size_t initial_size, memory_resource* upstream);
Requires: upstream должен быть адресом допустимого ресурса памяти. initial_size, если указано, должно быть больше нуля.
monotonic_buffer_resource(void* buffer, size_t buffer_size, memory_resource* upstream);
Requires: upstream должен быть адресом допустимого ресурса памяти. buffer_size не должно быть больше, чем количество байтов в buffer.
~monotonic_buffer_resource();
void release();
Effects: Вызывает по upstream_rsrc->deallocate() мере необходимости для освобождения всей выделенной памяти.
[ Note: Память возвращается обратно, upstream_rsrc даже если некоторые блоки, которые были выделены из this , не были освобождены this. ] — end note
memory_resource* upstream_resource() const;
void* do_allocate(size_t bytes, size_t alignment) override;
Returns: Указатель на выделенное хранилище ([basic.stc.dynamic.deallocation]) размером не менее bytes. Размер и выравнивание выделенной памяти должны соответствовать требованиям для класса, производного от memory_resource.
Effects: Если неиспользуемое пространство в current_buffer может уместиться в блоке с указанными bytes и alignment, то выделите возвращаемый блок из current_buffer; в противном случае устанавливается current_buffer значение upstream_rsrc->allocate(n, m), где n не меньше max(bytes, next_buffer_size) и m не меньше alignment, и увеличивается next_buffer_size на коэффициент роста, определяемый реализацией (который не обязательно должен быть целым), затем выделять блок возврата из вновь выделенного current_buffer.
void do_deallocate(void* p, size_t bytes, size_t alignment) override;
Remarks: Память, используемая этим ресурсом, монотонно увеличивается до тех пор, пока не будет уничтожена.
bool do_is_equal(const memory_resource& other) const noexcept override;