【TVM源码学习笔记】附录2 TVM的Object家族

TVM的Object类是很多类的基类,详细的分析资料可以参考

深入理解TVM:Object家族 - 知乎

深入理解TVM:Object家族(二) - 知乎

TVM源码品读:万物基石——Object类(1) - 知乎

TVM源码品读:万物基石——Object(2) - 知乎

1. Object/ObjectPtr/ObjectRef的数据包含关系

在阅读TVM C++代码的时候,有很多Object的派生类的类型转换需要追溯到Object/ObjectPtr/ObjectRef,所以这里着重分析三者之间的关系。我们可以只保留三者的包含关系代码:

class TVM_DLL Object {
public:
 
    ...
protected:
 
    ...
private:
    ...
    friend class ObjectPtr;
    ...
};


template <typename T>
class ObjectPtr {
public:
    ...

private:
    Object* data_{nullptr};
    ...
    friend class Object;
    friend class ObjectRef;
    ...
};


class ObjectRef {
public:
    ...  

protected:

    ObjectPtr<Object> data_;
  
};

 从上面的代码可以看到,ObjectPtr的数据成员data_是一个Object指针,ObjectRef的数据成员data_是一个 ObjectPtr实例。

下面分析下几个频繁出现的类方法和接口函数

2. Object::IsInstance()方法

  template <typename TargetType>
  inline bool IsInstance() const;

判断当前实例的是不是目标类型TaregtType的实例,返回true有以下几种场景:

1.当前实例类型和TargetType是同一种类型;

2.如果当前类型是TargetType的子类;

3.如果当前实例的类型和TargetType有共同的祖先,并且当前类型和祖先的距离更远。一个形象的比喻就是,一个人的子孙是这个人的兄弟的实例,这个人的兄弟不是这个人的子孙的实例。

例如Function类型的继承链(从左到右,是从子孙到祖先):

Function,  BaseFunc, RelayExpr, BaseExpr, ObjectRef

FunctionNode,  BaseFuncNode, RelayExprNode, BaseExprNode, Object

现在有一个变量 BaseFuncNode base_func_node, 那么base_func_node.as<BaseExprNode>将返回一个true,base_func_node.as<FunctionNode>返回false

3. ObjectRef::as()方法

template <typename ObjectType>
inline const ObjectType* ObjectRef::as() const {
  if (data_ != nullptr && data_->IsInstance<ObjectType>()) {
    return static_cast<ObjectType*>(data_.get());
  } else {
    return nullptr;
  }
}

分析代码:

1. 这个方法的实现是在ObjectRef类中,各子类在调用的时候都是调用的这个实现。所以后面分析中只说Object、ObjectPtr、ObjectRef,但是同样适用子类;

2. ObjectRef::data_是ObjectPtr<Object>类型,代码中data_是ObjectPtr<Object>类型;

3. data_.get()是ObjectPtr::get(),方法返回的是ObjectPtr的data_成员(注意不是2中的那个data_),该成员是一个Object指针。所以data_.get()返回的是一个Object(或者Object的子类的)指针;

4. 使用static_cast强转Object指针到ObjectType*,那么这个Object指针指向的类必须是ObjectType或者ObjectType的子类在。即要求ObjectRef指向的是ObjectType,或者是ObjectType的子类。

简单的说,就是把当前对象实例(是一个引用类型)转换为祖先类指针。

例如Function类型的继承链(从左到右,是从子孙到祖先):

Function,  BaseFunc, RelayExpr, BaseExpr, ObjectRef

FunctionNode,  BaseFuncNode, RelayExprNode, BaseExprNode, Object

现在有一个变量 BaseFunc base_func, 那么base_func.as<RelayExprNode>将返回一个RelayExprNode指针,base_func.as<FunctionNode>返回nullptr

4. GetRef()函数

GetRef<ObjectTypeRef>(ObjectType* obj)是将obj包装为对应的引用类型实例, 其中ObjectType继承自class Object, ObjectTypeRef继承自class ObjectRef, 为ObjectType对应的引用类型。GetRef转换得到的引用类型指向的数据仍然是obj,也就是转换结果和参数指向同一片数据。

接口代码实现:

template <typename RefType, typename ObjType>
inline RefType GetRef(const ObjType* ptr) {
    static_assert(std::is_base_of<typename RefType::ContainerType, ObjType>::value,
                "Can only cast to the ref of same container type");
    if (!RefType::_type_is_nullable) {
      ICHECK(ptr != nullptr);
    }
    return RefType(ObjectPtr<Object>(const_cast<Object*>(static_cast<const Object*>(ptr))));
}

这里我们忽略GetRef函数一开始的检查,只看最后return的那一句。

const_cast<Object*>(static_cast<const Object*>(ptr)) 是将一个ObjType类型的指针强转为Object类型并强制去掉const属性。在TVM的Object家族中,所有以Node为结尾的类名都是继承自Object, 不以Node结尾的类名都是继承自ObjectRef。所以某个以Node结尾的类实例,是可以强转为Object*类型的。

ObjectPtr<object>(xxx)这个就调用了ObjectPtr的构造函数生成一个ObjectPtr实例:

  explicit ObjectPtr(Object* data) : data_(data) {
    if (data != nullptr) {
      data_->IncRef();
    }
  }

 IncRef是增加Object的引用次数,这里不细究

接下来RefType(xxx)是调用RefType的构造函数,参数为 ObjectPtr类型,生成一个RefType类实例。如果这个RefType就是ObjectRef,看下对应的构造函数:

explicit ObjectRef(ObjectPtr<Object> data) : data_(data) {}

这样就生成了一个ObjectRef类型。如果RefType是ObjectRef的子类,并且是ObjType对应的类型(比如IRModule和IRModuleNode),那么就可以由某个类型的指针类型,得到对应的Ref类型了。

这种转换在代码中很多,比如说IRModule::FromExprInContext中:

if (auto* func_node = expr.as<BaseFuncNode>()) {
    func = GetRef<BaseFunc>(func_node);

这里将BaseFuncNode* func_node包装为引用类型BaseFunc 

5. Downcast()函数 

这个函数是将一个较为基础的类型的引用类型,包装为它的某个子类型。

template <typename SubRef, typename BaseRef>
inline SubRef Downcast(BaseRef ref) {
  if (ref.defined()) {
    ICHECK(ref->template IsInstance<typename SubRef::ContainerType>())
        << "Downcast from " << ref->GetTypeKey() << " to " << SubRef::ContainerType::_type_key
        << " failed.";
  } else {
    ICHECK(SubRef::_type_is_nullable) << "Downcast from nullptr to not nullable reference of "
                                      << SubRef::ContainerType::_type_key;
  }
  return SubRef(std::move(ref.data_));
}

我们先假定SubRef和BaseRef都是ObjectRef类型或者ObjectRef的子类。这里首先要求ref类型是SubRef类型的实例(IsInstance), 然后返回ref.data_的SubRef类型实例。ref.data_是BaseRef的Ptr类型。这里我们可以先看下ObjectRef的构造函数:

explicit ObjectRef(ObjectPtr<Object> data) : data_(data) {}

这里直接给 data_赋值,没有其他操作。

也就是这种转换,输入的实例虽然被看作是一个较为基础的类型,但是实际上它是SubRef的类型或者SubRef的子类实例?否则这种Downcast不太合理吧。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值