TVM Object类型系统

2024-04-10 23:44
文章标签 类型 系统 object tvm

本文主要是介绍TVM Object类型系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在TVM Object类型系统中最重要的是三个类:Object、ObjectPtr、ObjectRef

为什么需要这三个类?

设计目的:为了能够在不更改python前端的情况下扩展c++中的语言对象,且能够对任何语言对象序列化。

  • Object:编译器中所有的语言对象(命名一般以Node结尾)都是Object的子类,Object的子类保存了一般保存了数据成员变量
  • ObjectPtr:Object的子定义智能指针,用于进行内存管理
  • ObjectRef:Object的引用,ObjectRef的子类一般包含操作Object类的函数

代码实现

class TVM_DLL Object {protected:uint32_t type_index_{0};RefCounterType ref_counter_{0};FDeleter deleter_ = nullptr;inline void IncRef(); // 增加引用计数inline void DecRef(); // 减少引用计数private:inline int use_count() const; // 返回引用计数
};template <typename T>
class ObjectPtr {public:// 默认构造函数ObjectPtr() {}ObjectPtr(std::nullptr_t) {}// 拷贝构造,会调用构造函数从而增加引用数ObjectPtr(const ObjectPtr<T>& other) : ObjectPtr(other.data_) {}template <typename U>ObjectPtr(const ObjectPtr<U>& other) : ObjectPtr(other.data_) {static_assert(std::is_base_of<T, U>::value,"can only assign of child class ObjectPtr to parent");}// 移动构造函数,不调用构造函数,引用数不变ObjectPtr(ObjectPtr<T>&& other) : data_(other.data_) { other.data_ = nullptr; }template <typename Y>ObjectPtr(ObjectPtr<Y>&& other) : data_(other.data_) {static_assert(std::is_base_of<T, Y>::value, "can only assign of child class ObjectPtr to parent");other.data_ = nullptr;}// 析构函数~ObjectPtr() {if (data_ != nullptr) {data_->DecRef();data_ = nullptr;}// 使用计数int use_count() const { return data_ != nullptr ? data_->use_count() : 0; }// 访问成员变量T* get() const { return static_cast<T*>(data_); }T* operator->() const { return get(); }T& operator*() const { return *get(); }private:Object* data_{nullptr};// 构造函数,explicit意味着参数不能进行隐式转换explicit ObjectPtr(Object* data) : data_(data) {if (data != nullptr) {data_->IncRef();}}
};class ObjectRef {public:const Object* get() const { return data_.get(); }const Object* operator->() const { return get(); }using ContainerType = Object;protected:ObjectPtr<Object> data_;
};

ObjectPtr类中有Object*类型的data_成员变量,可以通过->操作符和get()函数返回Object*指针。
ObjectRef类中有ObjectPtr<Object>类型的data_成员变量,也可以通过->操作符和get()函数返回Object*指针(调用了data_成员变量的get()函数)。

我们以StringObj、String类为例来展示功能:

class StringObj : public Object {public:const char* data;uint64_t size;static constexpr const uint32_t _type_index = TypeIndex::kRuntimeString;static constexpr const char* _type_key = "runtime.String";TVM_DECLARE_FINAL_OBJECT_INFO(StringObj, Object);private:class FromStd; // 内部类,用于从std::string初始化data和size
};class StringObj::FromStd : public StringObj {public:explicit FromStd(std::string other) : data_container{other} {}private:std::string data_container;
};class String : public ObjectRef {public:String() : String(std::string()) {}String(std::string other);String(const char* other): String(std::string(other)) {}String(std::nullptr_t): ObjectRef(nullptr) {}const char* c_str() const { return get()->data; }const char* data() const { return get()->data; }// 类型转换运算符重载operator std::string() const { return std::string{get()->data, size()}; }size_t size() const {const auto* ptr = get();return ptr->size;}size_t length() const { return size(); }bool empty() const { return size() == 0; }char at(size_t pos) const {...}TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(String, ObjectRef, StringObj);
};

StringObj类中定义了字符串的首字符的指针和字符串的长度,定义了类型键和类型索引,并使用了TVM_DECLARE_FINAL_OBJECT_INFO宏,定义如下:

#define TVM_DECLARE_FINAL_OBJECT_INFO(TypeName, ParentType) \static const constexpr bool _type_final = true;           \static const constexpr int _type_child_slots = 0;         \TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)#define TVM_DECLARE_BASE_OBJECT_INFO(TypeName, ParentType)                                     \static_assert(!ParentType::_type_final, "ParentObj marked as final");                        \static uint32_t RuntimeTypeIndex() {                                                         \static_assert(TypeName::_type_child_slots == 0 || ParentType::_type_child_slots == 0 ||    \TypeName::_type_child_slots < ParentType::_type_child_slots,             \"Need to set _type_child_slots when parent specifies it.");                  \if (TypeName::_type_index != ::tvm::runtime::TypeIndex::kDynamic) {                        \return TypeName::_type_index;                                                            \}                                                                                          \return _GetOrAllocRuntimeTypeIndex();                                                      \}                                                                                            \static uint32_t _GetOrAllocRuntimeTypeIndex() {                                              \static uint32_t tindex = Object::GetOrAllocRuntimeTypeIndex(                               \TypeName::_type_key, TypeName::_type_index, ParentType::_GetOrAllocRuntimeTypeIndex(), \TypeName::_type_child_slots, TypeName::_type_child_slots_can_overflow);                \return tindex;                                                                             \}

TVM_DECLARE_BASE_OBJECT_INFO宏定义了两个静态变量以及两个静态函数,RuntimeTypeIndex()用于获取运行时类型索引,在StringObj构造时type_index_成员变量的值通过该函数得到。

tvm::runtime::String类中定义了对StringObj进行操作的函数,如size(),并使用了TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS宏,定义如下:

#define TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS(TypeName, ParentType, ObjectName)            \explicit TypeName(::tvm::runtime::ObjectPtr<::tvm::runtime::Object> n) : ParentType(n) {}    \TVM_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN(TypeName);                                           \const ObjectName* operator->() const { return static_cast<const ObjectName*>(data_.get()); } \const ObjectName* get() const { return operator->(); }                                       \static constexpr bool _type_is_nullable = false;                                             \using ContainerType = ObjectName;

TVM_DEFINE_NOTNULLABLE_OBJECT_REF_METHODS宏定义了一个构造函数,重载了->运算符和get()函数(返回值类型不再是Object*而是StringObj*),标记该类型是不可为空的,定义了容器类型。

构造过程

构造函数String(std::string other);的实现如下:

inline String::String(std::string other) {auto ptr = make_object<StringObj::FromStd>(std::move(other));ptr->size = ptr->data_container.size();ptr->data = ptr->data_container.data();data_ = std::move(ptr);
}

可以看到在构造tvm::runtime::String时,首先使用分配器由std::string参数创建一个ObjectPtr<StringObj::FromStd>类型的对象。分配器创建完成后将StringObj::FromStddata_container的size和data直接赋值给StringObj的size和data,然后将ptr移动到tvm::runtime::Stringdata_


// include/tvm/runtime/memory.h
template <typename T, typename... Args>
inline ObjectPtr<T> make_object(Args&&... args) {return SimpleObjAllocator().make_object<T>(std::forward<Args>(args)...);
}template <typename Derived>
class ObjAllocatorBase {public:template <typename T, typename... Args>inline ObjectPtr<T> make_object(Args&&... args) {using Handler = typename Derived::template Handler<T>;static_assert(std::is_base_of<Object, T>::value, "make can only be used to create Object");T* ptr = Handler::New(static_cast<Derived*>(this), std::forward<Args>(args)...);ptr->type_index_ = T::RuntimeTypeIndex();ptr->deleter_ = Handler::Deleter();return ObjectPtr<T>(ptr);}
}

使用分配器创建时把std::string移动到了data_container中,这时调用了StringObj的构造函数,然后设置了StringObjtype_index_deleter_,最后构造(通过ObjectPtr(Object* data))并返回了ObjectPtr,在构造时增加了Object的引用数。

总结

构造ObjectRef的过程中对Object进行了构造,主要使用Object保存数据,ObjectPtr用于内存管理,ObjectRef表示引用并对数据进行操作。

这篇关于TVM Object类型系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/892486

相关文章

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

Linux系统之dns域名解析全过程

《Linux系统之dns域名解析全过程》:本文主要介绍Linux系统之dns域名解析全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、dns域名解析介绍1、DNS核心概念1.1 区域 zone1.2 记录 record二、DNS服务的配置1、正向解析的配置

Linux系统中配置静态IP地址的详细步骤

《Linux系统中配置静态IP地址的详细步骤》本文详细介绍了在Linux系统中配置静态IP地址的五个步骤,包括打开终端、编辑网络配置文件、配置IP地址、保存并重启网络服务,这对于系统管理员和新手都极具... 目录步骤一:打开终端步骤二:编辑网络配置文件步骤三:配置静态IP地址步骤四:保存并关闭文件步骤五:重

Python如何查看数据的类型

《Python如何查看数据的类型》:本文主要介绍Python如何查看数据的类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python查看数据的类型1. 使用 type()2. 使用 isinstance()3. 检查对象的 __class__ 属性4.

Python容器类型之列表/字典/元组/集合方式

《Python容器类型之列表/字典/元组/集合方式》:本文主要介绍Python容器类型之列表/字典/元组/集合方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 列表(List) - 有序可变序列1.1 基本特性1.2 核心操作1.3 应用场景2. 字典(D

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想

Python如何在Word中生成多种不同类型的图表

《Python如何在Word中生成多种不同类型的图表》Word文档中插入图表不仅能直观呈现数据,还能提升文档的可读性和专业性,本文将介绍如何使用Python在Word文档中创建和自定义各种图表,需要的... 目录在Word中创建柱形图在Word中创建条形图在Word中创建折线图在Word中创建饼图在Word