Leon's Blog

分享一点有趣的技术

0%

MLIR-basics-deepdive-part2

image-20250422105012239

本篇文章主要基于mlir的公开talk,深入理解一下mlir中的operation,attributes以及properties机制的底层实现。本文为此系列的第二篇,着重解读mlir的attirbutes特性。

Attribute初探

首先,我们需要先简要理解一下Attribute在MLIR编译器中的作用。根据MLIR的官方文档资料,Attribute是在一个operation中用来表达常量数据的机制。MLIR中的每一operation都关联一个属性字典,其中包含属性名称和属性值的映射。

  • 属性按照所属方言划分,可以分为内嵌属性和方言属性。
    • 内嵌属性:mlir的内嵌方言提供的属性,比如我们常用的StringAttr,DenseArryAttr等。
    • 方言属性:允许方言中定义自己的属性
  • 属性按照字典键是否又方言前缀,可以分为inherent attributes和discardable attributes。
    • inherent属性:该属性由operation的语义信息来决定
    • discardable属性:属性自带语义,但要和operation相一致

如下图所示就是两种attribute的典型示例。cmpi操作的谓词属性和操作严格捆绑。

image-20250426205915933

Attribute 底层原理

image-20250426210418663

上图是编写mlir的时候,经常用到的创建新的attribute。该逻辑是,先构造构造一个vector,然后调用get() API获取一个属性的指针。这里的get() api的特点是,如果已有DenseI64ArrayAttr的内容是offsetsVec,则返回已有的属性,否则创建返回。

Attribute的recap知识

首先来总结一下attribute的一些特点:

image-20250426210700572

其最核心的一个特点是属性是不可变的,因此其一系列底层机制都围绕不可变性展开。获取一个内容是offsetsVec的DenseI64ArrayAttr,有如下几步骤:

  • 在operation所在的MLIRContext中获取DenseI64Array的ParametricStorageUniquer(每个属性类型都由一个存储器管理)
  • 在该storageuniqure中,通过hash的方式查找(key是<Type, int64, llvm::ArrayRef>>)。

Attribute的get()方法底层逻辑链条

首先我们写梳理整个逻辑链条相关的数据结构:

get方法返回什么

get()方法返回的是DenseArrayAttrStorage指针,该指针包含实际存储内容和一个hash值。

  • 实际存储内容包含:存储数据类型,存储数据长度,数组内容。
  • KeyTy表示hash值,是type,数据长度和实际数组的一个元组。

image-20250426221014974

通过属性类型:DenseI64ArrayAttr找寻到该类型的底层storage

image-20250426221323084

如上图所示,一个context上下文中,每个属性都有对应的storage。

image-20250426221410708

用一个densemap存储,每个attribute都有一个type id,通过type id检索到对应的ParametricStorageUniqer指针。

从ParametricStorageUniqer中通过hash值创建/返回属性指针

image-20250426225725235

如下是一个完整的get流程:

image-20250426225801721

值得注意的是,底层的存储BumpPtrAllocator类似于内存池,只能开辟,然后空间释放是整个Allocator管理的所有存储都释放。

Operation Accessors

image-20250426225942938

上图中是一个operation常用的api,其中,Operand,Result和region都是可变的,而attr是不可变的。因此看似api都是get,set,但实际底层机制attr会复杂的多。

具体复杂性体现在如下slide中:

image-20250426230139141

由于不可变,因此set attr的时候,针对DictionaryAttr中添加一组name和attrvalue,底层也是十分复杂的(DictionaryAttr也是属性)。

  • 首先将已有的dict拷贝入数组
  • 在数组中变化,添加新的value(inplace)
  • 用get方法生成新的Dictionary

一个简单的setAttr,底层流程分为如上三步,attr的immutable特性,导致其效率较低。

image-20250426230550008

上述slide是一个很好地总结。

如下是一个检测理解的例子:

image-20250426230628810

image-20250426230655927

显而易见,这里存在大量的vector拷贝, 以及可能的存储空间浪费。