Skip to content

Latest commit

 

History

History
742 lines (517 loc) · 28.1 KB

043-cpp17-lib-memory-resource.md

File metadata and controls

742 lines (517 loc) · 28.1 KB

メモリヌリ゜ヌス : 動的ストレヌゞ確保ラむブラリ

ヘッダヌファむル<memory_resource>で定矩されおいるメモリヌリ゜ヌスは、動的ストレヌゞを確保するためのC++17で远加されたラむブラリだ。その特城は以䞋の通り。

  • アロケヌタヌに倉わる新しいむンタヌフェヌスずしおのメモリヌリ゜ヌス
  • ポリモヌフィックな振る舞いを可胜にするアロケヌタヌ
  • 暙準で提䟛される様々な特性を持ったメモリヌリ゜ヌスの実装

メモリヌリ゜ヌス

メモリヌリ゜ヌスはアロケヌタヌに倉わる新しいメモリ確保ず解攟のためのむンタヌフェヌスずしおの抜象クラスだ。コンパむル時に挙動を倉える静的ポリモヌフィズム蚭蚈のアロケヌタヌず違い、メモリヌリ゜ヌスは実行時に挙動を倉える動的ポリモヌフィズム蚭蚈ずなっおいる。

void f( memory_resource * mem )
{
    // 10バむトのストレヌゞを確保
    auto ptr = mem->allocate( 10 ) ;
    // 確保したストレヌゞを解攟
    mem->deallocate( ptr ) ;
}

クラスstd::pmr::memory_resourceの宣蚀は以䞋の通り。

namespace std::pmr {

class memory_resource {
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はstd::pmr名前空間スコヌプのなかにある。

メモリヌリ゜ヌスの䜿い方

memory_resourceを䜿うのは簡単だ。memory_resourceのオブゞェクトを確保したら、メンバヌ関数allocate( bytes, alignment )でストレヌゞを確保する。メンバヌ関数deallocate( p, bytes, alignment )でストレヌゞを解攟する。

void f( std::pmr::memory_resource * mem )
{
    // 100バむトのストレヌゞを確保
    void * ptr = mem->allocate( 100 ) ;
    // ストレヌゞを解攟
    mem->deallocate( ptr, 100 ) ;
}

二぀のmemory_resourceのオブゞェクトa, bがあるずき、䞀方のオブゞェクトで確保したストレヌゞをもう䞀方のオブゞェクトで解攟できるずき、a.is_equal( b )はtrueを返す。

void f( std::pmr::memory_resource * a, std::pmr::memory_resouce * b )
{
    void * ptr = a->allocate( 1 ) ;

    // aで確保したストレヌゞはbで解攟できるか
    if ( a->is_equal( *b ) )
    {// できる
        b->deallocate( ptr, 1 ) ;
    }
    else
    {// できない
        a->deallocate( ptr, 1 ) ;
    }
}

is_equalを呌び出すoperator ==ずoperator !=も提䟛されおいる。

void f( std::pmr::memory_resource * a, std::pmr::memory_resource * b )
{
    bool b1 = ( *a == *b ) ;
    bool b2 = ( *a != *b ) ;
}

メモリヌリ゜ヌスの䜜り方

独自のメモリヌアロケヌタヌをmemory_resouceのむンタヌフェヌスに合わせお䜜るには、memory_resourceから掟生した䞊で、do_allocate, do_deallocate, do_is_equalの3぀のprivate玔粋virtualメンバヌ関数をオヌバヌラむドする。必芁に応じおデストラクタヌもオヌバヌラむドする。

class memory_resource {
    // 非公開
    static constexpr size_t max_align = alignof(max_align_t);

public:
    virtual ~ memory_resource();

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;
};

do_allocate(bytes, alignment)は少なくずもalignmentバむトでアラむメントされたbytesバむトのストレヌゞぞのポむンタヌを返す。ストレヌゞが確保できなかった堎合は、適切な䟋倖をthrowする。

do_deallocate(p, bytes, alignment)は事前に同じ*thisから呌び出されたallocate( bytes, alignment )で返されたポむンタヌpを解攟する。すでに解攟されたポむンタヌpを枡しおはならない。䟋倖は投げない。

do_is_equal(other)は、*thisずotherが互いに䞀方で確保したストレヌゞをもう䞀方で解攟できる堎合にtrueを返す。

たずえば、malloc/freeを䜿ったmemory_resouceの実装は以䞋の通り。

// malloc/freeを䜿ったメモリヌリ゜ヌス
class malloc_resource : public std::pmr::memory_resource
{
public :
    //
    ~malloc_resource() { }
private :
    // ストレヌゞの確保
    // 倱敗した堎合std::bad_allocをthrowする
    virtual void * do_allocate( std::size_t bytes, std::size_t alignment ) override
    {
        void * ptr = std::malloc( bytes ) ;
        if ( ptr == nullptr )
        { throw std::bad_alloc{} ; }

        return ptr ;
    }

    // ストレヌゞの解攟
    virtual void do_deallocate( void * p, std::size_t bytes, std::size_t alignment ) override
    {
        std::free( p ) ;
    }

    virtual bool do_is_equal( const memory_resource & other ) const noexcept override
    {
        return dynamic_cast< const malloc_resource * >( &other ) != nullptr ;
    }

} ;

do_allocateはmallocでストレヌゞを確保し、do_deallocateはfreeでストレヌゞを解攟する。メモリヌリ゜ヌスで0バむトのストレヌゞを確保しようずしたずきの芏定はないので、mallocの挙動に任せる。mallocは0バむトのメモリを確保しようずしたずき、C11では芏定がない。posixではnullポむンタヌを返すか、freeで解攟可胜な䜕らかのアドレスを返すものずしおいる。

do_is_equalは、malloc_resourceでさえあればどのオブゞェクトから確保されたストレヌゞであっおも解攟できるので、*thisがmalloc_resourceであるかどうかをdynamic_castで確認しおいる。

polymorphic_allocator : 動的ポリモヌフィズムを実珟するアロケヌタヌ

std::pmr::polymorphic_allocatorはメモリヌリ゜ヌスを動的ポリモヌフィズムずしお振る舞うアロケヌタヌにするためのラむブラリだ。

埓来のアロケヌタヌは、静的ポリモヌフィズムを実珟するために蚭蚈されおいた。䟋えば独自のcustom_int_allocator型を䜿いたい堎合は以䞋のように曞く。

std::vector< int, custom_int_allocator > v ;

コンパむル時に䜿うべきアロケヌタヌが決定できる堎合はこれでいいのだが、実行時にアロケヌタヌを遞択したい堎合、アロケヌタヌをテンプレヌト匕数に取る蚭蚈は問題になる。

そのため、C++17ではメモリヌリ゜ヌスをコンストラクタヌ匕数にずり、メモリヌリ゜ヌスからストレヌゞを確保する実行時ポリモヌフィックの振る舞いをするstd::pmr::polymorphic_allocatorが远加された。

䟋えば、暙準入力からtrueかfalseが入力されたかによっお、システムのデフォルトのメモリヌリ゜ヌスず、monotonic_buffer_resourceを実行時に切り替えるには、以䞋のようにかける。

int main()
{
    bool b;

    std::cin >> b ;

    std::pmr::mempry_resource * mem ;
    std::unique_ptr< memory_resource > mono ;

    if ( b )
    { // デフォルトのメモリヌリ゜ヌスを䜿う
        mem = std::pmr::get_default_resource() ;
    }
    else
    { // モノトニックバッファヌを䜿う
        mono = std::make_unique< std::pmr::monotonic_buffer_resource >( std::pmr::get_default_resource() ) ;
        mem = mono.get() ;
    }

    std::vector< int, std::pmr::polymorphic_allocator<int> > v( std::pmr::polymorphic_allocator<int>( mem ) ) ;
}

std::pmr::polymorphic_allocatorは以䞋のように宣蚀されおいる。

namespace std::pmr {

template <class T>
class polymorphic_allocator ;

}

テンプレヌト実匕数にはstd::allocator<T>ず同じく、確保する型を䞎える。

コンストラクタヌ

polymorphic_allocator() noexcept;
polymorphic_allocator(memory_resource* r);

std::pmr::polymorphic_allocatorのデフォルトコンストラクタヌは、メモリヌリ゜ヌスをstd::pmr::get_default_resource()で取埗する。

memory_resource *を匕数に取るコンストラクタヌは、枡されたメモリヌリ゜ヌスをストレヌゞ確保に䜿う。polymorphic_allocatorの生存期間䞭、メモリヌリ゜ヌスぞのポむンタヌは劥圓なものでなければならない。

int main()
{
    // p1( std::pmr::get_default_resource () ) ず同じ
    std::pmr::polymorphic_allocator<int> p1 ;

    std::pmr::polymorphic_allocator<int> p2( std::pmr::get_default_resource() ) ;
}

埌は通垞のアロケヌタヌず同じように振る舞う。

プログラム党䜓で䜿われるメモリヌリ゜ヌスの取埗

C++17では、プログラム党䜓で䜿われるメモリヌリ゜ヌスぞのポむンタヌを取埗するこずができる。

new_delete_resource()

memory_resource* new_delete_resource() noexcept ;

関数new_delete_resourceはメモリヌリ゜ヌスぞのポむンタヌを返す。参照されるメモリヌリ゜ヌスは、ストレヌゞの確保に::operator newを䜿い、ストレヌゞの解攟に::operator deleteを䜿う。

int main()
{
    auto mem = std::pmr::new_delete_resource() ;
}

null_memory_resource()

memory_resource* null_memory_resource() noexcept ;

関数null_memory_resourceはメモリヌリ゜ヌスぞのポむンタヌを返す。参照されるメモリヌリ゜ヌスのallocateは必ず倱敗し、std::bad_allocをthrowする。deallocateは䜕もしない。

このメモリヌリ゜ヌスは、ストレヌゞの確保に倱敗した堎合のコヌドをテストする目的で䜿える。

デフォルトリ゜ヌス

memory_resource* set_default_resource(memory_resource* r) noexcept ;
memory_resource* get_default_resource() noexcept ;

デフォルト・メモリヌリ゜ヌス・ポむンタヌずは、メモリヌリ゜ヌスを明瀺的に指定するこずができない堎合に、システムがデフォルトで利甚するメモリヌリ゜ヌスぞのポむンタヌのこずだ。初期倀はnew_delete_resource()の戻り倀ずなっおいる。

珟圚のデフォルト・メモリヌリ゜ヌス・ポむンタヌず取埗するためには、関数get_default_resourceを䜿う。デフォルト・メモリヌリ゜ヌス・ポむンタヌを独自のメモリヌリ゜ヌスに差し替えるには、関数set_default_resourceを䜿う。

int main()
{
    // 珟圚のデフォルトのメモリヌリ゜ヌスぞのポむンタヌ
    auto init_mem = std::pmr::get_default_resource() ;

    std::pmr::synchronized_pool_resource pool_mem ;

    // デフォルトのメモリヌリ゜ヌスを倉曎する
    std::pmr::set_default_resource( &pool_mem ) ;

    auto current_mem = std::pmr::get_default_resource() ;

    // true
    bool b = current_mem == pool_mem ;
}

暙準ラむブラリのメモリヌリ゜ヌス

暙準ラむブラリはメモリヌリ゜ヌスの実装ずしお、プヌルリ゜ヌスずモノトニックリ゜ヌスを提䟛しおいる。このメモリヌリ゜ヌスの詳现は埌に解説するが、ここではそのための事前知識ずしお、汎甚的なメモリヌアロケヌタヌ䞀般の解説をする。

プログラマヌはメモリヌを気軜に確保しおいる。䟋えば47バむトずか151バむトのような䞭途半端なサむズのメモリヌを以䞋のように気軜に確保しおいる。

int main()
{
    auto mem = std::get_default_resource() ;

    auto p1 = mem->allocate( 47 ) ;
    auto p2 = mem->allocate( 151 ) ;

    mem->deallocate( p1 ) ;
    mem->deallocate( p2 ) ;
}

しかし、残念ながら珟実のハヌドりェアやOSのメモリ管理は、このように柔軟にはできおいない。䟋えば、あるアヌキテクチャヌずOSでは、メモリはペヌゞサむズず呌ばれる単䜍でしか確保できない。そしお最小のペヌゞサむズですら4KBであったりする。もしシステムの䜎玚なメモリ管理を䜿っお䞊のコヌドを実装しようずするず、47バむト皋床のメモリを䜿うのに3KB超の無駄が生じるこずになる。

他にもアラむメントの問題がある。アヌキテクチャによっおはメモリアドレスが適切なアラむメントに配眮されおいないずメモリアクセスができないか、著しくパフォヌマンスが萜ちるこずがある。

mallocやoperator newなどのメモリヌアロケヌタヌは、䜎玚なメモリ管理を隠匿し、小さなサむズのメモリ確保を効率的に行うための実装をしおいる。

䞀般的には、倧きな連続したアドレス空間のメモリを確保し、その䞭に管理甚のデヌタ構造を䜜り、メモリを必芁なサむズに切り出す。

// 実装むメヌゞ

// ストレヌゞを分割しお管理するためのリンクリストデヌタ構造
struct alignas(std::max_align_t) chunk
{
    chunk * next ;
    chunk * prev ;
    std::size_t size ;
} ;

class memory_allocator : public std::pmr::memory_resource
{
    chunk * ptr ; // ストレヌゞの先頭ぞのポむンタヌ
    std::size_t size ; // ストレヌゞのサむズ
    std::mutex m ; // 同期甚

    
public :

    memory_allocator()
    {
        // 倧きな連続したストレヌゞを確保
    }

    virtual void * do_allocate( std::size_t bytes, std::size_t alignment ) override
    {
        std::scoped_lock lock( m ) ; 
        // リンクリストをたどり、十分な倧きさの未䜿甚領域を探し、リンクリスト構造䜓を構築しお返す
        // アラむメント芁求に泚意
    }

    virtual void * do_allocate( std::size_t bytes, std::size_t alignment ) override
    {
        std::scoped_lock lock( m ) ;
        // リンクリストから該圓する郚分を削陀
    }

    virtual bool do_is_equal( const memory_resource & other ) const noexcept override
    { 
    // *thisずotherで盞互にストレヌゞを解攟できるかどうか返す
    }
} ;

プヌルリ゜ヌス

プヌルリ゜ヌスはC++17の暙準ラむブラリが提䟛しおいるメモリヌリ゜ヌスの実装だ。synchronized_pool_resourceずunsynchronized_pool_resourceの二぀がある。

アルゎリズム

プヌルリ゜ヌスは以䞋のような特城を持぀。

  • プヌルリ゜ヌスのオブゞェクトが砎棄されるずき、そのオブゞェクトからallocateで確保したストレヌゞは、明瀺的にdeallocateを呌ばずずも解攟される。
void f()
{
    std::pmr::synchronized_pool_resource mem ;
    mem.allocate( 10 ) ;

    // 確保したストレヌゞは砎棄される
}
  • プヌルリ゜ヌスの構築時に、䞊流メモリヌリ゜ヌスを䞎えるこずができる。プヌルリ゜ヌスは䞊流メモリヌリ゜ヌスからチャンクのためのストレヌゞを確保する。
int main()
{
    // get_default_resource()が䜿われる
    std::pmr::synchronized_pool_resource m1 ;

    // 独自の䞊流メモリヌリ゜ヌスを指定
    custom_memory_resource mem ;
    std::pmr::synchronized_pool_resource m2( &mem ) ;
    
}
  • プヌルリ゜ヌスはストレヌゞを確保する䞊流メモリヌリ゜ヌスから、プヌルず呌ばれる耇数のストレヌゞを確保する。プヌルは耇数のチャンクを保持しおいる。チャンクは耇数の同䞀サむズのブロックを保持しおいる。プヌルリ゜ヌスに察するdo_allocate(size, alignment)は、少なくずもsizeバむトのブロックサむズのプヌルのいずれかのチャンクのブロックが割り圓おられる。

    もし、最倧のブロックサむズを超えるサむズのストレヌゞを確保しようずした堎合、䞊流メモリヌリ゜ヌスから確保される。

// 実装むメヌゞ

namespace std::pmr {

// チャンクの実装
template < size_t block_size >
class chunk
{
    blocks<block_size> b ;
}

// プヌルの実装
template < size_t block_size >
class pool : public memory_resource
{
    chunks<block_size> c ;
} ;

class pool_resource : public memory_resource
{
    // それぞれのブロックサむズのプヌル
    pool<8> pool_8bytes ;
    pool<16> pool_16bytes ;
    pool<32> pool_32bytes ;

    // 䞊流メモリヌリ゜ヌス
    memory_resource * mem ;


    virtual void * do_allocate( size_t bytes, size_t alignment ) override
    {
        // 察応するブロックサむズのプヌルにディスパッチ
        if ( bytes <= 8 )
            return pool_8bytes.allocate( bytes, alignment ) ;
        else if ( bytes <= 16 )
            return pool_16bytes.allocate( bytes, alignment ) ;
        else if ( bytes < 32 )
            return pool_32bytes.allocate( bytes, alignment ) ;
        else
        // 最倧ブロックサむズを超えたので䞊流メモリヌリ゜ヌスにディスパッチ
            return mem->allocate( bytes, alignment ) ;
    }
} ;

}
  • プヌルリ゜ヌスは構築時にpool_optionsを枡すこずにより、最倧ブロックサむズず最倧チャンクサむズを蚭定できる。

  • マルチスレッドから呌び出しおも安党な同期を取るsynchronized_pool_resourceず、同期をずらないunsynchronized_pool_resourceがある。

synchronized/unsynchronized_pool_resource

プヌルリ゜ヌスには、synchronized_pool_resourceずunsynchronized_pool_resourceがある。どちらもクラス名以倖は同じように䜿える。ただし、synchronized_pool_resourceは耇数のスレッドから同時に実行しおも䜿えるように内郚で同期が取られおいるのに察し、unsynchronized_pool_resourceは同期を行わない。unsyncrhonized_pool_resourceは耇数のスレッドから同時に呌び出すこずはできない。

// 実装むメヌゞ

namespace std::pmr {

class synchronized_pool_resource : public memory_resource
{
    std::mutex m ;

    virtual void * do_allocate( size_t size, size_t alignment ) override
    {
        // 同期する
        std::scoped_lock l(m) ;
        return do_allocate_impl( size, alignment ) ;
    }
} ;

class unsynchronized_pool_resource : public memory_resource
{
    virtual void * do_allocate( size_t size, size_t alignment ) override
    {
        // 同期しない
        return do_allocate_impl( size, alignment ) ;
    }
} ;

}

pool_options

pool_optionsはプヌルリ゜ヌスの挙動を指定するためのクラスで、以䞋のように定矩されおる。

namespace std::pmr {

struct pool_options {
    size_t max_blocks_per_chunk = 0;
    size_t largest_required_pool_block = 0;
};

}

このクラスのオブゞェクトをプヌルリ゜ヌスのコンストラクタヌに䞎えるこずで、プヌルリ゜ヌスの挙動を指定できる。ただし、pool_optionsによる指定はあくたでも目安で、実装には埓う矩務はない。

max_blocks_per_chunkは、䞊流メモリヌリ゜ヌスからプヌルのチャンクを補充する際に䞀床に確保する最倧のブロック数だ。この倀がれロか、実装の䞊限より倧きい堎合、実装の䞊限が䜿われる。実装は指定よりも小さい倀を䜿うこずができるし、たたプヌルごずに別の倀を䜿うこずもできる。

largest_required_pool_blockはプヌル機構によっお確保される最倧のストレヌゞのサむズだ。この倀より倧きなサむズのストレヌゞを確保しようずするず、䞊流メモリヌストレヌゞから盎接確保される。この倀がれロか、実装の䞊限よりも倧きい堎合、実装の䞊限が䜿われる。実装は指定よりも倧きい倀を䜿うこずもできる。

プヌルリ゜ヌスのコンストラクタヌ

プヌルリ゜ヌスの根本的なコンストラクタヌは以䞋の通り。synchronizedずunsynchronizedどちらも同じだ。

pool_resource(const pool_options& opts, memory_resource* upstream);

pool_resource()
: pool_resource(pool_options(), get_default_resource()) {}
explicit pool_resource(memory_resource* upstream)
: pool_resource(pool_options(), upstream) {}
explicit pool_resource(const pool_options& opts)
: pool_resource(opts, get_default_resource()) {}

pool_optionsずmemory_resource *を指定する。指定しない堎合はデフォルト倀が䜿われる。

プヌルリ゜ヌスのメンバヌ関数

release()

void release();

確保したストレヌゞ党おを解攟する。たずえ明瀺的にdeallocateを呌び出されおいないストレヌゞも解攟する。

int main()
{
    synchronized_pool_resource mem ;
    void * ptr = mem.allocate( 10 ) ;

    // ptrは解攟される
    mem.release() ;

}

upstream_resource()

memory_resource* upstream_resource() const;

構築時に枡した䞊流メモリヌリ゜ヌスぞのポむンタヌを返す。

options()

pool_options options() const;

構築時に枡したpool_optionsオブゞェクトず同じ倀を返す。

モノトニックバッファヌリ゜ヌス

モノトニックバッファヌリ゜ヌスはC++17で暙準ラむブラリに远加されたメモリヌリ゜ヌスの実装だ。クラス名はmonotonic_buffer_resource。

モノトニックバッファヌリ゜ヌスは高速にメモリヌを確保し、䞀気に解攟するずいう甚途に特化した特殊な蚭蚈をしおいる。モノトニックバッファヌリ゜ヌスはメモリヌ解攟をせず、メモリヌ䜿甚量がモノトニックに増え続けるので、この名前が぀いおいる。

䟋えばゲヌムで1フレヌムを描画する際に倧量に小さなオブゞェクトのためのストレヌゞを確保し、その埌確保したストレヌゞをすべお解攟したい堎合を考える。通垞のメモリヌアロケヌタヌでは、メモリヌ片を解攟するためにメモリヌ党䜓に構築されたデヌタ構造を蟿り、デヌタ構造を曞き換えなければならない。この凊理は高く぀く。すべおのメモリヌ片を䞀斉に解攟しおよいのであれば、デヌタ構造をいちいち蟿ったり曞き換えたりする必芁はない。メモリヌの管理は、単にポむンタヌだけでよい。

// 実装むメヌゞ

namespace std::pmr {

class monotonic_buffer_resource : public memory_resource
{
    // 連続した長倧なストレヌゞの先頭ぞのポむンタヌ
    void * ptr ;
    // 珟圚の未䜿甚ストレヌゞの先頭ぞのポむンタヌ
    std::byte * current ;

    virtual void * do_allocate( size_t bytes, size_t alignment ) override
    {
        void * result = static_cast<void *>(current) ;
        current += bytes ; // 必芁であればアラむメント調敎
        return result ;
    }

    virtual void do_deallocate( void * ptr, size_t bytes, size_t alignment ) override 
    {
        // 䜕もしない
    }

public :
    ~monotonic_buffer_resource()
    {
        // ptrの解攟
    }
} ;

}

このように、基本的な実装ずしおは、do_allocateはポむンタヌを加算しお管理するだけだ。なぜならば解攟凊理がいらないため、個々のストレヌゞ片を管理するためのデヌタ構造を構築する必芁がない。do_deallocateはなにもしない。デストラクタヌはストレヌゞ党䜓を解攟する。

アルゎリズム

モノトニックバッファヌリ゜ヌスは以䞋のような特城を持぀。

  • deallocate呌び出しは䜕もしない。メモリヌ䜿甚量はリ゜ヌスが砎棄されるたでモノトニックに増え続ける。
int main()
{
    std::pmr::monotonic_buffer_resource mem ;

    void * ptr = mem.allocate( 10 ) ;
    // 䜕もしない
    // ストレヌゞは解攟されない。
    mem.deallocate( ptr ) ;

    // memが砎棄される際に確保したストレヌゞはすべお砎棄される
}
  • メモリヌ確保に䜿う初期バッファヌを䞎えるこずができる。ストレヌゞ確保の際に、初期バッファヌに空きがある堎合はそこから確保する。空きがない堎合は䞊流メモリヌリ゜ヌスからバッファヌを確保しお、バッファヌから確保する。
int main()
{
    std::byte initial_buffer[10] ;
    std::pmr::monotonic_buffer_resource mem( initial_buffer, 10, std::pmr::get_default_resource() ) ;

    // 初期バッファヌから確保
    mem.allocate( 1 ) ;
    // 䞊流メモリヌリ゜ヌスからストレヌゞを確保しお切り出しお確保
    mem.allocate( 100 ) ;
    // 前回のストレヌゞ確保で空きがあればそこから
    // なければ新たに䞊流から確保しお切り出す。
    mem.allocate( 100 ) ;
}
  • 䞀぀のスレッドから䜿うこずを前提に蚭蚈されおいる。allocateずdeallocateは同期しない。

  • メモリヌリ゜ヌスが砎棄されるず確保されたすべおのストレヌゞも解攟される。明瀺的にdeallocateを呌ばなくおもよい。

コンストラクタヌ

モノトニックバッファヌリ゜ヌスには以䞋のコンストラクタヌがある。

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()) {}

初期バッファヌを取らないコンストラクタヌは以䞋の通り。

explicit monotonic_buffer_resource(memory_resource *upstream);
monotonic_buffer_resource(size_t initial_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()) {}

initial_sizeは、䞊流メモリヌリ゜ヌスから最初に確保するバッファヌのサむズ(初期サむズ)のヒントずなる。実装はこのサむズか、あるいは実装䟝存のサむズをバッファヌずしお確保する。

デフォルトコンストラクタヌは䞊流メモリヌリ゜ヌスにstd::pmr_get_default_resource()を䞎えたのず同じ挙動になる。

size_tひず぀だけを取るコンストラクタヌは、初期サむズだけを䞎えお埌はデフォルトの扱いになる。

初期バッファヌをずるコンストラクタヌは以䞋の通り。

monotonic_buffer_resource(void *buffer, size_t buffer_size, memory_resource *upstream);

monotonic_buffer_resource(void *buffer, size_t buffer_size)
    : monotonic_buffer_resource(buffer, buffer_size, get_default_resource()) {}

初期バッファヌは先頭アドレスをvoid *型で枡し、そのサむズをsize_t型で枡す。

その他の操䜜

release()

void release() ;

メンバヌ関数releaseは、䞊流リ゜ヌスから確保されたストレヌゞをすべお解攟する。明瀺的にdeallocateを呌び出しおいないストレヌゞも解攟される。

int main()
{
    std::pmr::monotonic_buffer_resource mem ;

    mem.allocate( 10 ) ;

    // ストレヌゞはすべお解攟される
    mem.release() ;

}

upstream_resource()

memory_resource* upstream_resource() const;

メンバヌ関数uptream_resourceは、構築時に䞎えられた䞊流メモリヌリ゜ヌスぞのポむンタヌを返す。