【STL】模拟实现二级空间配置器

二级空间配置器的实现原理

  二级空间配置器,当配置空间大于128字节时,调用一级空间配置器;当配置空间小于128字节时,采用了复杂的内存池整理方式,又称为次层配置:每次配置一大块内存,并维护16个自由链表(free list),下次再使用相同大小的内存时,则直接从free list中得到。如果用户释放了小块空间,free list还负责回收。同时,为了方便管理,16个自由链表维护的空间大小均是88的整数倍,8,16,24,32……

  1. 若free list中有区块,就拿来用;
  2. 如果没有可以用的空间,则调用refill()函数,准备为free list重新填充空间;
  3. 新的空间将取自内存池(由chunk_alloc()函数完成),缺省得到20个相同大小的空间,若不够20个但够至少1个,则将这些都拨出去;但是如果一个都不够时,则利用malloc()函数从heap中分配;
  4. 若malloc失败,则调用一级空间配置器,一级空间配置器中有针对空间不足的处理方式。
    这里写图片描述

以上便是二级空间配置器的大致原理了,如果没有看懂,没关系,下面我们用流程图的形式将每个函数的实现过程大致画出来。

二级空间配置器主要功能流程图

空间配置:
这里写图片描述
这里写图片描述
这里写图片描述
空间释放:
这里写图片描述
二级空间配置器的实现代码,见本文末。


二级空间配置器与一级空间配置器

这里写图片描述
为了使配置器可以符合STL的标准,SGI将其进行了封装,封装成simple_alloc接口:

template <class T, class Alloc>
class SimpleAlloc
{
public:
     static T* Allocate(size_t n)// 开辟n个大小为T的空间
     {
          return (0 == n) ? 0 : (T*)Alloc::Allocate(sizeof(T)*n);
     }
     static T* Allocate(void)// 开辟n个大小为T的空间
     {
          return (T*)Alloc::Allocate(sizeof(T));
     }
     static void DeAllocate(void* p, size_t n)
     {
          if(n != 0)
               Alloc::DeAllocate(p, n*sizeof(T));
     }
     static void DeAllocate(void* p)
     {
          return Alloc::DeAllocate(p, sizeof(T));
     }
};

对象的构造与析构

  之前我们说C++中内存的配置与释放分为两步:对于配置空间,第一步是申请空间,第二步是调用相应的构造函数;对于释放空间,第一步是调用析构函数,第二步是释放空间。而SGI将这两步分开进行,空间的配置与释放由alloc::allocate()alloc::deallocate()完成;对象的构造和析构由::construct()::destoty()完成。
  以上的一级空间配置器和二级空间配置器都是空间的配置与释放,下面便是::construct()和::destoty()的实现,这两个函数均是全局函数。
  
construct是使用了定位new表达式:

template <class T1, class T2>
void Construct(T1* p, const T2& value)
{
     new(p) T1(value);
}

destory有两个版本:
  一是接受一个指针,将指针所指之物析构掉
  二是接受一个迭代器区间,将区间之内的对象析构掉
区间内的对象(使用到了类型萃取,戳这里查看),如果是内置类型,则不需要做什么,如果是自定义类型(深拷贝的对象),则调用版本一的函数析构。

template <class T>
void Destory(T* p)
{
     p->~T();
}
template <class Iterator>
void Destory(Iterator first, Iterator end)
{
     _Destory(first, end, TypeTraits<Iterator::ValueType>::PODType);
}
template <class Iterator>
void _Destory(Iterator first, Iterator end, Falsetype)
{}
template <class Iterator>
void _Destory(Iterator first, Iterator end, Truetype)
{
     while (first != end)
     {
          Destory(&(*first));
          first++;
     }
}

二级空间配置器的实现函数

// 其中__TRACE_DEBUG(...)是测试函数,与函数的实现无关。
typedef Default_Alloc_Template<0> _Alloc;

template <int inst>
class Default_Alloc_Template
{
private:
    enum { __ALIGN = 8 };
    enum { __MAX_BYTES = 128 };
    enum { __NFREE_LISTS = __MAX_BYTES / __ALIGN };
    union OBJ
    {
        union OBJ* free_list_link;
        char client[1];
    };
    static OBJ* free_list[__NFREE_LISTS];
    static char* start_free;
    static char* end_free;
    static size_t heap_size;
    static size_t ROUND_UP(size_t bytes)
    {
        return ((bytes + __ALIGN - 1) & ~(__ALIGN - 1));// 向上调整至8的倍数
    }
    static size_t FREELIST_INDEX(size_t bytes)
    {
        return ((bytes + __ALIGN - 1) / __ALIGN - 1);// 计算相应的下标
    }
public:
    static void* Allocate(size_t n)
    {
        if (n > __MAX_BYTES)
        {
            __TRACE_DEBUG("一级空间配置申请%d字节内存\n", n);
            return MallocAllocTemplate<0>::Allocate(n);
        }
        int index = FREELIST_INDEX(n);
        OBJ* result = free_list[index];
        if (result == 0)
        {
            __TRACE_DEBUG("自由链表中没有%d的空间,去内存池申请内存\n", n);
            void* r = ReFill(ROUND_UP(n));
            __TRACE_DEBUG("内存池申请内存成功,返回给用户%d字节的内存\n", n);
            return r;
        }
        __TRACE_DEBUG("二级空间配置器有内存,申请到了%d内存\n", n);
        free_list[index] = result->free_list_link;
        return result;
    }
    static void DeAllocate(void* p, size_t n)
    {
        if (n > __MAX_BYTES)
        {
            __TRACE_DEBUG("一级空间配置器释放内存%d\n", n);
            MallocAllocTemplate<0>::DeAllocate(p, n);
            return;
        }
        int index = FREELIST_INDEX(n);
        __TRACE_DEBUG("二级空间配置器释放内存%d\n", n);
        ((OBJ*)p)->free_list_link = free_list[index];
        free_list[index] = ((OBJ*)p);
    }

    static void* ReFill(size_t n);
    static char* ChunkAlloc(size_t size, int &nonjs);
};

template <int inst>
char* Default_Alloc_Template<inst>::start_free = NULL;
template <int inst>
char* Default_Alloc_Template<inst>::end_free = NULL;
template <int inst>
size_t Default_Alloc_Template<inst>::heap_size = 0;
template <int inst>
typename Default_Alloc_Template<inst>::OBJ* Default_Alloc_Template<inst>::free_list[
    Default_Alloc_Template<inst>::__NFREE_LISTS] = { 0 };

template <int inst>
void* Default_Alloc_Template<inst>::ReFill(size_t n)// 向内存池要内存
{
    int nobjs = 20;
    __TRACE_DEBUG("向内存池申请20个%d大小的内存\n", n);
    char* chunk = ChunkAlloc(n, nobjs);// 向内存池要20个n个字节的内存,其中1个给用户,19个给链表挂上

    if (nobjs == 1)
    {
        __TRACE_DEBUG("向内存池只申请到了1个%d的内存\n", n);
        return chunk;
    }
    int index = FREELIST_INDEX(n);
    OBJ* cur = (OBJ*)(chunk + n);
    OBJ* next = (OBJ*)((char*)cur + n);
    __TRACE_DEBUG("向内存池申请到了%d个%d大小的内存,将一个返回给用户,其余%d个挂到自由链表中\n", nobjs, n, nobjs-1);
    while (--nobjs)
    {
        cur->free_list_link = free_list[index];
        free_list[index] = cur;
        cur = next;
        next = (OBJ*)((char*)next + n);
    }
    return chunk;
}
template <int inst>
char* Default_Alloc_Template<inst>::ChunkAlloc(size_t size , int &nobjs)
{
    char* result = NULL;
    size_t total_bytes = size * nobjs;  // 需要nobjs个size大小的内存
    size_t bytes_left = end_free - start_free; // 内存池剩余内存

    if (bytes_left >= total_bytes)// 内存池的内存足够
    {
        result = start_free;
        start_free += total_bytes;
        __TRACE_DEBUG("内存池有足够的内存:%d\n", total_bytes);
        return result;
    }
    else if (bytes_left >= size)// 内存池只够一个或以上个size大小的内存
    {
        result = start_free;
        nobjs = bytes_left / size;
        total_bytes = nobjs * size;
        start_free += total_bytes;
        __TRACE_DEBUG("内存池有%d个%d大小的内存:共%d字节\n", nobjs, size, total_bytes);
        return result;
    }
    else // 内存池一个size大小的内存都没有
    {
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); //内存池的内存不够,需要向系统要这么多内存
        // 先将内存池的剩余内存放置链表中
        if (bytes_left > 0)
        {
            __TRACE_DEBUG("内存池一个%d的空间都没有,先将内存池剩余内存放置自由链表\n", size);
            ((OBJ*)start_free)->free_list_link = free_list[bytes_left];
            free_list[bytes_left] = (OBJ*)start_free;
        }
        start_free = (char*)malloc(bytes_to_get); // 向系统要内存
        __TRACE_DEBUG("内存池向系统要%d内存\n", bytes_to_get);
        if (start_free == 0)// 系统内存不够
        {
            // 去链表里找能用的内存
            for (int i = size; i < __MAX_BYTES; i += __ALIGN)
            {
                if (free_list[i] != NULL)
                {
                    start_free = (char*)free_list[i];
                    end_free = start_free + i;
                    free_list[i] = free_list[i]->free_list_link;
                }
                __TRACE_DEBUG("系统没有足够内存,在自由链表找内存\n");
            }
            end_free = 0; // 如果底下的调用抛出异常,start_free返回0,end_free不置0就会出问题
            __TRACE_DEBUG("系统没有足够内存,自由链表没有足够内存,去以及空间配置器找内存\n");
            start_free = (char*)MallocAllocTemplate<0>::Allocate(bytes_to_get); // 山穷水尽,采用一级空间配置器
        }
        heap_size += bytes_to_get; // 调整heap_size
        end_free = start_free + bytes_to_get; // 一般至此,内存池已经有内存了,递归调用,给内存池分配内存,更新nobjs
        return ChunkAlloc(size, nobjs);
    }
}
参考资源链接:[Rheolef环境下的C++间断有限元编程教程与高效计算](https://wenku.csdn.net/doc/1r9anwegrx) 要在Rheolef环境中使用C++实现一个二维间断有限元求解,首先需要熟悉C++编程、间断有限元(Discontinuous Galerkin, DG)方法以及Rheolef平台的集成。以下是一些关键步骤和建议,以帮助您构建一个高效的二维求解。 1. **C++编程基础**:掌握C++基本语法和高级特性,例如模板编程和STL(标准模板库),这些将有助于您构建复杂的数据结构和算法。 2. **DG方法理论**:理解DG方法在处理流体动力学问题时的优势。这包括如何在元素界面处理函数的不连续性,以及如何将偏微分方程离散化为方程组。 3. **Rheolef环境集成**:熟悉Rheolef的API和数据结构,了解如何在Rheolef平台上定义网格、配置边界条件和初始化求解。 4. **代码实现**:编写C++代码来实现DG方法的关键步骤,例如局部空间离散化、时间积分以及线性系统的求解。可以利用Rheolef提供的函数库来简化这一步骤。 5. **网格操作与边界条件**:在Rheolef环境中定义二维网格,并根据流体动力学问题的特性设置恰当的边界条件。 6. **性能优化与调试**:使用C++的性能分析工具监控程序运行效率,对关键算法进行优化,例如使用Eigen库进行矩阵运算。同时,准备好调试程序中的潜在问题。 7. **测试与验证**:在开发过程中不断进行单元测试和性能测试,确保求解的正确性和效率。 通过以上步骤,您可以构建一个适用于流体动力学问题的间断有限元求解。为了更好地掌握这些技术,建议深入阅读《Rheolef环境下的C++间断有限元编程教程与高效计算》一书,其中包含了丰富的代码示例和实践知识,能够帮助您从基础到进阶逐步掌握DG方法和Rheolef平台的使用。 参考资源链接:[Rheolef环境下的C++间断有限元编程教程与高效计算](https://wenku.csdn.net/doc/1r9anwegrx)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值