内存机制#

make_object#

template <typename T, typename... Args>
inline ObjectPtr<T> make_object(Args&&... args);

make_object,它的作用是使用默认的分配器来分配一个对象。

参数列表中包括了三个参数:

  • T:节点类型,是一个模板参数;

  • Args:构造函数的参数,也是一个模板参数;

  • args:可变参数,表示传递给构造函数的参数。

函数返回值类型为ObjectPtr<T>,即指向类型为 T 的对象的指针。

在函数体内部,使用了模板展开的方式传递参数给构造函数,最终调用了 TVM 中的默认分配器来分配新的对象,并返回该对象的指针。

template <typename T, typename... Args>
inline ObjectPtr<T> make_object(Args&&... args) {
  return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);
}

make_object 函数使用了名为 SimpleObjAllocator 的对象来进行对象的分配和释放操作。在函数内部,首先调用了 SimpleObjAllocator 的默认构造函数来创建 SimpleObjAllocator 对象。然后,通过调用 make_object<T>(std::forward<Args>(args)...) 来创建类型为 T 的对象,并将传入的参数进行完美转发(std::forward<Args>(args)...)传递给对象的构造函数。最后,将创建的对象指针作为函数的返回值返回。

这个函数的作用是简化对象的创建过程,通过传入类型和参数,可以快速创建指定类型的对象,并返回其指针。

make_inplace_array_object#

模板函数 make_inplace_array_object,它接受可变参数 size_t num_elems, Args&&... args,并返回 ObjectPtr<ArrayType> 类型的对象指针。

该函数使用了名为 SimpleObjAllocator 的对象来进行对象的分配和释放操作。在函数内部,首先调用了 SimpleObjAllocator 的默认构造函数来创建 SimpleObjAllocator 对象。然后,通过调用 make_inplace_array<ArrayType, ElemType>(num_elems, std::forward<Args>(args)...) 来创建指定元素个数的数组对象,并将传入的元素个数和参数进行完美转发(std::forward<Args>(args)...)传递给数组对象的构造函数。最后,将创建的数组对象指针作为函数的返回值返回。

这个函数的作用是简化数组对象的创建过程,通过传入元素个数和参数,可以快速创建一个指定元素个数的数组对象,并返回其指针。

内存分配器的设计说明

  1. 允许在必要时交换分配器模式,这意味着 TVM 的内存分配器可以根据不同的需求和场景进行灵活的切换。

  2. 一些可能的未来优化方向:

    • 使用 Arena 分配器,将内存所有权交给 Arena(deleter_= nullptr),这种分配器可以更好地管理内存块的大小和生命周期;

    • 线程本地对象池:为每个大小和对齐要求创建一个对象池,这种优化可以提高多线程程序的性能;

    • 通过对象的类型进行特化,为每个对象提供特定的分配器,这种优化可以更好地满足不同类型对象的内存需求。

ObjAllocatorBase#

ObjAllocatorBase 模板类,它是所有对象分配器的基类。该类使用了 Curiously recurring template pattern,即在派生类中实现与基类相同的模板函数。

该类中有两个模板函数:

  • make_object(Args&&... args):用于创建新的对象,其中 T 是要分配的类型,Args 是构造函数的参数类型,args 是实际传入的参数。该函数首先获取派生类中的 Handler 模板类,然后使用 Handler::New() 方法创建新的对象,并设置对象的 type_index_deleter_,最后返回指向该对象的指针。

  • make_inplace_array(size_t num_elems, Args&&... args):用于创建指定元素个数的数组,其中 ArrayType 是数组类型,ElemType 是数组元素的类型,num_elems 是数组元素的数量,args 是实际传入的参数。该函数首先获取派生类中的 ArrayHandler 模板类,然后使用 Handler::New() 方法创建新的对象数组,并设置数组的 type_index_deleter_,最后返回指向该对象数组的指针。

这两个函数都使用了静态断言(static_assert)来确保创建的对象类型为 Object 或其派生类。

SimpleObjAllocator#

SimpleObjAllocator,它继承自 ObjAllocatorBase<SimpleObjAllocator>(这是 C++ 的 CRTP 实现静态多态技巧)。该分配器使用 new/delete 来管理对象的内存分配和释放。

该分配器中定义了两个嵌套模板类:HandlerArrayHandlerHandler 用于创建单个对象,而 ArrayHandler 用于创建对象数组。这两个类都使用了静态断言(static_assert)来确保对象类型符合特定的要求。

Handler 类中,使用 new 运算符创建新的对象,并使用 std::aligned_storage 来分配具有特定对齐要求的存储空间。然后,使用 placement new 将新对象放置在已分配的存储空间中,并调用构造函数初始化对象。最后,返回指向新对象的指针。

ArrayHandler 类中,使用 new 运算符创建指定元素个数的新对象数组,并使用 std::aligned_storage 来分配具有特定对齐要求的存储空间。然后,使用 placement new 将新对象放置在已分配的存储空间中,并调用构造函数初始化数组。最后,返回指向新对象数组的指针。

Deleter 函数中,使用 delete 运算符释放对象或对象数组的内存。对于对象数组,需要使用 delete[] 运算符释放整个数组的内存。

总之,这段代码实现了简单的对象分配器,可以用于在程序中动态分配和释放对象或对象数组的内存。