• TVM:Object家族


    Object.h概述

    命名空间:

    TVM::runtime

    文件中包含的结构:

    • 1.结构体TypeIndex
    • 2.类Object
    • 3.类ObjectPtr
    • 4.类ObjectRef
    • 5.结构体ObjectPtrHash
    • 6.结构体ObjectPtrEqual
    • 7.宏

    结构体TypeIndex

    该结构体内部仅包含一个枚举类型,通过下面的代码可以看到,Object类存在一个TypeIndex类型的成员变量。

    /*!
     * \brief Namespace for the list of type index.
     * \note Use struct so that we have to use TypeIndex::ENumName to refer to
     *       the constant, but still able to use enum.
     */
    struct TypeIndex {
      enum {
        /*! \brief Root object type. */
        kRoot = 0,
        // Standard static index assignments,
        // Frontends can take benefit of these constants.
        /*! \brief runtime::Module. */
        kRuntimeModule = 1,
        /*! \brief runtime::NDArray. */
        kRuntimeNDArray = 2,
        /*! \brief runtime::String. */
        kRuntimeString = 3,
        /*! \brief runtime::Array. */
        kRuntimeArray = 4,
        /*! \brief runtime::Map. */
        kRuntimeMap = 5,
        /*! \brief runtime::ShapeTuple. */
        kRuntimeShapeTuple = 6,
        /*! \brief runtime::PackedFunc. */
        kRuntimePackedFunc = 7,
        // static assignments that may subject to change.
        kRuntimeClosure,
        kRuntimeADT,
        kStaticIndexEnd,
        /*! \brief Type index is allocated during runtime. */
        kDynamic = kStaticIndexEnd
      };
    };  // namespace TypeIndex
    
    

    为什么要将enum类型放在一个struct结构体中呢?

    避免命名冲突
    包含在struct的{}中,相当于在一个命名空间下,这样已经能够避免命名冲突的问题,而且使用起来比较方便TypeIndex::ENumName。同样也可以将其放在类中,但由于enum中的类型信息本就是对外部开放的,因此struct相较于class更为简洁方便。
    参考:C++ enum枚举型

    在TypeIndex中可以看到,共有四种object type:

    • 1.kRoot:root object type
    • 2.前端可以获取的常量:

    1.runtime::Module
    2.runtime::NDArray
    3.runtime::String
    4.runtime::Array
    5.runtime::Map
    6.runtime::ShapeTuple
    7.runtime::PackedFunc

    • 3.static assignments that may subject to change 可能发生变化的静态分配
    • 4.Type index is allocated during runtime.运行时分配的Type Index

    kDynamic

    Class Object

    成员变量:

    tvm::runtime::Object
    + _type_key
    + _type_final
    + _type_child_slots
    + _type_child_slots_can_overflow
    + _type_has_method_visit_attrs
    + _type_has_method_sequal_reduce
    + _type_has_method_shash_reduce
    + _type_index
    # type_index_
    # ref_counter_
    # deleter_
    

    可以看到,除了类的回收器deleter_和引用计数ref_counter外,其余成员变量都是关于type_index的标志位或子类插槽(slots)等信息。由于作为多有容器对象的基类(base class of all object containers.)所以主要提供了一系列的操作接口。但最重要的成员变量是type_index_。

    成员函数:
    成员函数主要是类相关的构造函数,拷贝构造、拷贝复制、move构造、move赋值函数
    其次是type_index_相关的函数

    剩下的为引用计数相关函数。

    类相关

    // 构造函数
    // Default constructor and copy constructor
    Object() {}
    ​
    // 拷贝构造
    // Override the copy and assign constructors to do nothing.
    // This is to make sure only contents, but not deleter and ref_counter
    // are copied when a child class copies itself.
    // This will enable us to use make_object<ObjectClass>(*obj_ptr)
    // to copy an existing object.
    Object(const Object& other) {  // NOLINT(*)
    }
    ​
    // 移动构造(move)
    Object(Object&& other) {  // NOLINT(*)
    }
    ​
    // 拷贝赋值
    Object& operator=(const Object& other) {  // NOLINT(*)
        return *this;
    }
    ​
    // 移动构造(move)
    Object& operator=(Object&& other) {  // NOLINT(*)
        return *this;
    }
    ​
    // 析构器(FDeleter函数指针)
    typedef void (*FDeleter)(Object* self);
    

    type_index_相关

    uint32_t type_index() const { return type_index_; }
    
    std::string GetTypeKey() const { return TypeIndex2Key(type_index_); }
    
    std::string GetTypeKey() const { return TypeIndex2Key(type_index_); }
    
    static std::string TypeIndex2Key(uint32_t tindex);
    
    static size_t TypeIndex2KeyHash(uint32_t tindex);
    
    static uint32_t TypeKey2Index(const std::string& key);
    
    template <typename TargetType>
        inline bool IsInstance() const;
    
    static uint32_t _GetOrAllocRuntimeTypeIndex() { return TypeIndex::kRoot; }
    
    static uint32_t RuntimeTypeIndex() { return TypeIndex::kRoot; }
    
    static uint32_t GetOrAllocRuntimeTypeIndex(const std::string& key, uint32_t static_tindex,
                                               uint32_t parent_tindex, uint32_t type_child_slots,
                                               bool type_child_slots_can_overflow);
    

    继承、引用计数相关

    void IncRef();
    
    void DecRef();
    
    int use_count() const;
    
    DerivedFrom(uint32_t parent_tindex) const;
    

    友元类

    template <typename>
        friend class ObjAllocatorBase;
    
    template <typename>
        friend class ObjectPtr;
    
    friend class TVMRetValue;
    
    friend class ObjectInternal;
    

    class ObjectPtr

    注释中将这个类描述为 A custom smart pointer for Object.一个用户自定义的指向Object类的智能指针。

    其中最重要的成员变量和成员函数为:

     private:
      /*! \brief internal pointer field */  
      Object* data_{nullptr};
      /*!
       * \brief constructor from Object
       * \param data The data pointer
       */
      explicit ObjectPtr(Object* data) : data_(data) {
        if (data != nullptr) {
          data_->IncRef();
        }
      }
    

    可以看到,该类的成员变量中包含一个指向Object对象的指针 data_,以及一个私有的构造函数,接收一个指向Object对象的指针,初始化data_并增加 data_的引用计数
    该类中的其余函数主要是类的多种构造函数,以及重载了多个运算符,供用户将该类的对象作为指针使用。

    总结:因此可以将ObjectPtr看作是一个指向Object对象的指针的包装器,其本质依旧是Object类型的指针。经过包装后我们可以根据其提供的各种接口,更加方便地对指针进行操作。值得注意的是,该类是一个模板类,因此该类的对象包含一个描述Object对象的类型

    class ObjectRef

    注释中将该类描述为 Base class of all object reference所有对象引用的基类。

    其中最重要的成员变量和成员函数为:

     protected:
      /*! \brief Internal pointer that backs the reference. */
      ObjectPtr<Object> data_;
    

    该类中包含了一个ObjectPtr的对象。因此可以看做是ObjectPtr的包装器。除该对象外,ObjectRef类也提供了多种函数接口,方便用户操作。
    image

    Object、ObjectPtr、ObjectRef关系

    image

    ObjectPtr类是封装的、指向Object类的一个智能指针类

    /*!
     * \brief A custom smart pointer for Object.
     * \tparam T the content data type.
     * \sa make_object
     */
    template <typename T>
    class ObjectPtr {
     public:
      ObjectPtr() {}
      ObjectPtr(std::nullptr_t) {}  // NOLINT(*)
      ObjectPtr(const ObjectPtr<T>& other)  // NOLINT(*)
          : ObjectPtr(other.data_) {}
    
      template <typename U>
      ObjectPtr(const ObjectPtr<U>& other)  // NOLINT(*)
          : ObjectPtr(other.data_) {
        static_assert(std::is_base_of<T, U>::value,
                      "can only assign of child class ObjectPtr to parent");
      }
      ......
      // 关键数据成员,一个指向Object对象的指针
    Object *data_{nullptr};
    // 几个关键的运算符重载接口,用于获取指向Object的指针或者引用
    T* get() const { return static_cast<T*>(data_); }
    T* operator->() const { return get(); }
    T& operator*() const { return *get(); }
    

    包含了智能指针应该有的:拷贝构造、移动构造、swap函数、运算符重载等的实现,可参考C++智能指针
    使用自定义的智能指针管理Object对象生命周期,便于管理,防止内存泄漏

    ObjectRef类为所有对象引用的基类( Base class of all object reference)
    它的关键数据成员和几个运算符重载接口如下:

    // 关键数据成员
    ObjectPtr<Object> data_;
    // 关键运算符重载接口
    const Object* get() const { return data_.get(); }
    const Object* operator->() const { return get(); }
    

    在具体的使用中,Object机制有两套继承体系,一套继承自Object用于表示实际的类, 一套继承自ObjectRef,它就像是指向实际类对象的智能指针,用于操作实际的类对象,虽然实际上没有shared_ptr,但我们可以用下图来帮助理解这两套继承体系:
    image

    Object的所有子类基本上都遵循了 NameNode 继承 Object,Name继承 ObjectRef;,例如:

    /*! \brief Base node of all statements. */
    class StmtNode : public Object
    
    /*! \brief Container of all statements */
    class Stmt : public ObjectRef
    

    而 ObjectPtr作为模板类,记录有指向Object类对象的类型,在使用过程中,会出现指针的向下转型(downcast)以及类型转换等操作,因此其作用主要做转型和检查。
    使用场景:对Object的子类做修改

    SumExpr ToSumExpr(PrimExpr expr) {
        // Ref对象调用.as<>()函数返回一个类型为<SumExprNode>的Ptr
        if (const auto* op = expr.as<SumExprNode>()) {
            // 通过Ptr构造Ref
            return GetRef<SumExpr>(op);
        }
        // 通过make_object创建<>类型对象,并返回指向该对象的指针。
        ObjectPtr<SumExprNode> n = make_object<SumExprNode>();
        n->dtype = expr.dtype();
        // Ref对象调用.as<>()函数返回一个类型为<>的Ptr
        if (const auto* op = expr.as<IntImmNode>()) {
            // 通过Ref对内容进行操作
            n->base = op->value;
            // 通过Ptr构造Ref对象
            return SumExpr(n);
        } else {
            n->args.emplace_back(ToSplitExpr(expr));
            return SumExpr(n);
        }
    }
    

    ObjectPtr:用于记录类型,类型检查(.as()),对Object实体内容进行操作,还可以作为构造引用的参数。

    Object:为对象实体。

    ObjectRef:作为函数间传递和处理的形式。

    如果拿Object、ObjectPtr、ObjectRef这三个类和shared_ptr类比的话:

    • Object相当于控制块,可以通过引用计数ref_counter_来控制对象的生命周期,对象的析构函数也可以通过delete_这个函数指针指定

    • Object的子类的除去Object基类的部分相当于数据块,里面保存有类的真实数据

    • ObjectRef就像是shared_ptr这个wrapper,自身不包含实际数据,但是可以操作实际的数据

    • ObjectPtr的作用在使用的角度有点类似ObjectRef,不同的是数据类型,ObjectPtr<T>是一个模板

    参考:
    深入理解TVM:Object家族(二)
    TVM源码品读:万物基石——Object类(1)

  • 相关阅读:
    高并发计算服务器数量
    Java子线程中操作主线程Private级别数据
    Java线程并发中常见的锁--自旋锁 偏向锁
    Java锁之自旋锁详解
    Java多线程:向线程传递参数的三种方法
    TPS、并发用户数、吞吐量关系
    C/C++中near和far的区别
    qt的pos()和globalpos()(globalpos是相对于桌面的)
    如何获得 Qt窗口部件在主窗口中的位置--确定鼠标是否在某一控件上与在控件上的位置
    QComboBox实现复选功能(三种方法:嵌套QListWidget, 设置QStandardItemModel, 设置Delegate)
  • 原文地址:https://www.cnblogs.com/whiteBear/p/16753997.html
Copyright © 2020-2023  润新知