首页 > 其他分享 >allocator、polymorphic allocator 与 memory_resource

allocator、polymorphic allocator 与 memory_resource

时间:2024-07-05 11:42:32浏览次数:18  
标签:std resource 内存 memory polymorphic allocator

allocator、polymorphic allocator 与 memory_resource
cpp allocator

Created: 2024-07-04T10:59+08:00
Published: 2024-07-05T11:27+08:00
Categories: C-CPP

custom allocator

std::allocator 是无状态的,实测最简单的 allocator 只需要:

  1. value_type
  2. allocate
  3. deallocate

rebind 目的是实现 rebind(allocator<TypeA>, TypeB) == allocator<TypeB>
C++11 已经使用 allocator_traits 实现了这种想法[1],并且 C++17 就抛弃了以前把 rebind 放到 allocator 内部实现的方法。

还有两个函数 constructdestroy,如果提供,就会使用我们自定义的,不提供也没有问题,allocator_traits 提供了默认的实现,
container 本身就是通过 allocator_traits 来使用 construct 和 destroy。

  1. construct:在 allocate 后调用,在分配的内存上初始化对象
  2. destroy: 在 deallocate 前调用,在内存上析构分配的对象

以下是一个非常简单的 allocator 实现。

#include <vector>
#include <iostream>
#include <memory>

using std::vector;

struct point
{
    int x{1};

    point(int x_) : x(x_) {}

    ~point()
    {
        std::cout << "detor, x:" << x << std::endl;
    }
};

template <typename T>
class MyAllocator
{
public:
    using value_type = T;
    T *allocate(size_t n)
    {
        std::cout << "myallocator.allocate: " << n << std::endl;
        return (T *)::operator new(n * sizeof(T));
    }

    void deallocate(T *p, size_t n)
    {
        std::cout << "myallocator.deallocate: " << n << std::endl;
        return ::operator delete(p);
    }

    // template <typename _Up, typename... _Args>
    // void construct(_Up *__p, _Args &&...__args) noexcept(noexcept(::new((void *)__p) _Up(std::forward<_Args>(__args)...)))
    // {
    //     std::cout << "construct called" << std::endl;
    //     // 表示在 地址 _p 上调用对象 _Up的构造函数
    //     // 其中,__args是构造函数的参数
    //     ::new ((void *)__p) _Up(std::forward<_Args>(__args)...);
    // }

    // template <typename _Up>
    // void destroy(_Up *__p) noexcept(noexcept(__p->~_Up()))
    // {
    //     std::cout << "destroy called" << std::endl;
    //     __p->~_Up();
    // }
};

int main()
{
    point a{2};
    vector<point, MyAllocator<point>> vp{3,4,5};
    vp[0].x = 100;

    for (auto& i: vp) {
        std::cout << i.x << std::endl;
    }

    return 0;
}

多态内存资源(PMR, polymorphic memory resource)

使用 PMR 原因

为什么需要 PMR 呢,因为[2]

  1. allocator 是模板签名的一部分。不同 allocator 的容器,无法混用。
  2. c++11 以前,allocator 无状态;c++11 以后,可以有状态,然而 allocator 类型复杂难用。
  3. allocator 内存对齐无法控制,需要传入自定义 allocator。

以上三点、特别是第一点,造成 stl 无法成为软件接口 (interface) 的一部分。
难以将 memory arena、memory pool 用于 stl 容器。

比如自定义的 allocator 没法和 stl 默认的 allocator 通用:

vector<int, MyAllocator<int>> vi {1,2,3};
vector<int, std::allocator<int>> vi_copy = vi; // error!

memory_resource 和 polymorphic_allocator

PMR 就说,好吧,那我们把 allocator 固定下来,全都使用 polymorphic_allocator<T>polymorphic_allocator<T> 持有一根 memory_resource 的指针[3],分配策略通过 memory_resource 实现。

The class template std::pmr::polymorphic_allocator is an Allocator which exhibits different allocation behavior depending upon the std::pmr::memory_resource from which it is constructed. Since memory_resource uses runtime polymorphism to manage allocations, different container instances with polymorphic_allocator as their static allocator type are interoperable, but can behave as if they had different allocator types.
All specializations of polymorphic_allocator meet the allocator completeness requirements.
std::pmr::polymorphic_allocator - cppreference.com

memory_resource 提供对原始内存的管理接口,类似 malloc 和 free:

memory_resource:
+ allocate: 提供给 polymorphic_allocator,调用 do_allocate
+ deallocate:提供给 polymorphic_allocator,调用 do_deallocate
# do_allocate: 内部分配内存的方法,像 malloc
# do_deallocate: 内部回收内存的方法,像 free

polymorphic_allocator<T> 只是在原始内存 memory_resource 上提供具体类型的抽象,比如需要 n 个类型为 T 的对象,底层调用 memory_resource 获取原始内存。

std::pmr::polymorphic_allocator<T>::allocate:
Allocates storage for n objects of type T using the underlying memory resource. Equivalent to return static_cast<T*>(resource()->allocate(n * sizeof(T), alignof(T)));.
std::pmr::polymorphic_allocator::allocate - cppreference.com

construct 和 destroy 通过 allocator_traits 实现:

/// Partial specialization for std::pmr::polymorphic_allocator
  template<typename _Tp>
    struct allocator_traits<pmr::polymorphic_allocator<_Tp>>

现成的 memory_resource

上面提到的分离接口可以实现不同 allocator 之间的通用,但是具体要让内存分配快起来需要高效的 memory_resource 实现,C++17 提供了五种[4]:

内存资源 行为
new_delete_resource() 返回一个调用newdelete的内存资源的指针
synchronized_pool_resource 创建更少碎片化的、线程安全的内存资源的类
unsynchronized_pool_resource 创建更少碎片化的、线程不安全的内存资源的类
monotonic_buffer_resource 创建从不释放、可以传递一个可选的缓冲区、线程不安全的类
null_memory_resource() 返回一个每次分配都会失败的内存资源的指针

这三种比较重要:

  1. std::pmr::monotonic_buffer_resource - cppreference.com
  2. std::pmr::synchronized_pool_resource - cppreference.com
  3. std::pmr::unsynchronized_pool_resource - cppreference.com

在接口调用中提到了 upstream 的概念:如果当前 memory_resource 内存不足,则调用 upstream memory_resource 的 allocate 方法[5]
其实是有一个默认的 memory_resource 的,就是默认的 ::operator new::operator delete 管理内存。
而且 memory_resource 必须要有 upstream,看 monotonic_buffer_resource 的源码:

    monotonic_buffer_resource(memory_resource* __upstream) noexcept
    __attribute__((__nonnull__))
    : _M_upstream(__upstream)
    { _GLIBCXX_DEBUG_ASSERT(__upstream != nullptr); }

用法

std::pmr::monotonic_buffer_resource - cppreference.com
Cpp17/markdown/src/ch29.md at master · MeouSker77/Cpp17

总结

  1. stl 容器需要 allocator<T>
  2. allocator_traits 规定访问 allocator 成员的标准接口[6]。可以实现 rebind 等操作,以及对象的 construct 和 destroy 也在 traits 中有默认实现
  3. 但是不同的 allocator 没法通用、无状态、难以实现自定义的分配策略,所以用 polymorphic_allocator 和 memory_resource 出现了
  4. polymorphic_allocator<T> 内部持有 memory_resource 指针,统一了 allocator 接口,只封装了一层要管理的类型 T
  5. memory_resource 内部来实现分配策略,和分配的具体对象无关
  6. 提供了五种特别的 memory_resource 实现

  1. std::allocator_traits - cppreference.com ↩︎

  2. 游戏引擎开发新感觉!(6) c++17 内存管理 - 知乎 ↩︎

  3. std::pmr::memory_resource - cppreference.com ↩︎

  4. Cpp17/markdown/src/ch29.md at master · MeouSker77/Cpp17 ↩︎

  5. std::pmr::monotonic_buffer_resource - cppreference.com ↩︎

  6. std::allocator_traits - cppreference.com ↩︎

标签:std,resource,内存,memory,polymorphic,allocator
From: https://www.cnblogs.com/ticlab/p/18285521

相关文章

  • Windows11系统System.Windows.Forms.DataVisualization.resources.dll文件丢失问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个System.Windows.Forms.DataVisualization.re......
  • 本地资源(local resource)与项目资源文件(project resource)的区别
    导入“本地资源”的图片,会在Form文件下面的Form.resx文件里面,不可以在多个Form界面引用,不可以在里面修改图片的名称;导入“项目资源文件”的图片,会保存在Properties文件夹下面的Resources.resx文件夹里面,可以在多个form界面引用,可以在里面修改图片的名称。注意:Resources.rexs......
  • wpf关于Resource,Style的定义与引用,滑动按钮
    当我们使用wpf框架去搭建自己的程序时,一般都会重写wpf原生的一些样式,以达到软件风格的统一于美化,以下介绍一下常见的几种添加Style的方式我们以一个滑动按钮为例1.对当前控件的样式进行更改<ToggleButton><ToggleButton.Style><StyleTarg......
  • IntelliJ IDEA java maven项目读取配置文件信息 java.util.ResourceBundle 方式
    一、在main目录下新建resources目录并将其设为资源文件目录  创建config.properties文件二、在pom.xml中添加下面代码 只这样打包后jar才能有配置文件<resources><resource><filtering>true</filtering><directory>src/main/......
  • WPF ResourceDictionary ResourceDictionary.MergedDictionaries
    1.Addresourcedictionary,Brushes.xaml<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><LinearGradientBrush......
  • Windows11系统System.Workflow.Activities.resources.dll文件丢失问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个System.Workflow.Activities.resources.dll......
  • Windows11系统System.Windows.Controls.Ribbon.resources.dll文件丢失问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个System.Windows.Controls.Ribbon.resources.......
  • YARN集群的高可用性秘诀:ResourceManager故障转移全指南
    标题:YARN集群的高可用性秘诀:ResourceManager故障转移全指南引言在大数据时代,YARN作为Hadoop生态系统中的资源管理器,其稳定性和可靠性对于整个数据处理流程至关重要。当ResourceManager(RM)遇到故障时,如何快速恢复并继续执行任务,是衡量YARN集群健壮性的重要指标。本文将深入......
  • Malloc Lab: Writing a Dynamic Storage Allocator
    18-213/15-613,Summer2024MallocLab:WritingaDynamicStorageAllocatorAssigned:Thursday,June20,2024Thislabrequiressubmittingtwoversionsofyourcode:oneasaninitialcheckpoint,andthesecondasyourfinalversion.Theduedatesofeachp......
  • 在使用InputStream templateStream = this.getClass().getClassLoader().getResourceA
    在使用 this.getClass().getClassLoader().getResourceAsStream()读取项目目录路径下的文件需要注意在pom.xml文件中加入<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-......