Pass Infrastructure基础架构(上)

pass代表了转换和优化的基本基础架构。本文档概述了MLIR中的pass基础结构以及如何使用它。

有关MLIR 及其核心方面(如IR结构和算子)的更多信息,请参见 MLIR规范

有关 在MLIR中进行图形重写的快速入门,请参见 MLIR Rewrites。如果转换涉及模式匹配算子DAG,那么这是一个很好的起点。

算子pass 

在MLIR中,抽象和转换的主要单元是一个 运算 。这样,pass管理器被设计为处理不同嵌套级别的算子实例。pass通行管理器的结构 和嵌套的概念将在下面详细介绍。MLIR中的所有pass均源自OperationPass,并遵守以下限制;在多线程和其它高级方案中,任何不符合都会导致有问题的行为:

  • 修改正在算子的当前之外引用或依赖的任何状态。这包括在父块中添加或删除算子,更改属性(取决于当前算子的规定)/当前算子数/结果/后继对象。
  • 修改另一个未嵌套在当前算子的状态。
    • 其它线程可能同时在这些算子上运行。
  • 检查同级算子的状态。
    • 其它线程可能正在并行修改这些算子。
    • 允许检查祖先/父项算子的状态。
  • 步长调用pass状态runOnOperation保持可变的。传递可能在许多不同的算子上运行,而不能保证执行顺序。
    • 在进行多线程处理时,特定的传递实例甚至可能不会在IR内的所有算子上执行。因此,传递不应该依赖于所有算子上的运行。
  • 保持任何全局可变状态,例如源文件中的静态变量。所有可变状态都应pass传递实例来维护。
  • 必须是可复制的
    • 流程管理器可以创建流程的多个实例,以并行处理算子。

创建算子传递时,根据使用情况,有两种不同的类型可供选择:

OperationPass:特定于算子 

在op-specific算子上传递给定算子类型明确的算子。此算子类型必须遵守pass管理器设置的限制,以执行pass。

要定义特定于算子通道,派生类必须遵守以下规定:

  • 从CRTP类继承,OperationPass并提供算子类型作为附加模板参数。
  • 覆盖void runOnOperation()虚拟方法。

一个简单的过程可能看起来像:

namespace {

/// Here we utilize the CRTP `PassWrapper` utility class to provide some

/// necessary utility hooks. This is only necessary for passes defined directly

/// in C++. Passes defined declaratively use a cleaner mechanism for providing

/// these utilities.

struct MyFunctionPass : public PassWrapper<OperationPass<FuncOp>,

MyFunctionPass> {

void runOnOperation() override {

// Get the current FuncOp operation being operated on.

FuncOp f = getOperation();

// Walk the operations within the function.

f.walk([](Operation *inst) {

....

});

}

};

} // end anonymous namespace

/// Register this pass so that it can be built via from a textual pass pipeline.

/// (Pass registration is discussed more below)

void registerMyPass() {

PassRegistration<MyFunctionPass>(

"flag-name-to-invoke-pass-via-mlir-opt", "Pass description here");

}

OperationPass:不可知 

op-agnostic pass,添加到通管理器的算子类型进行运算。这意味着这种类型的pass通道可以在几种不同的算子类型上进行运算。通常使用算子接口 和 特征来存储此类pass 。此类pass传递的示例包括“ 常见子表达式消除” 和“ 内联” 。

要创建算子pass传递,派生类必须遵守以下规定:

  • 从CRTP类继承OperationPass。
  • 覆盖虚拟void runOnOperation()方法。

一个简单的过程可能看起来像:

/// Here we utilize the CRTP `PassWrapper` utility class to provide some

/// necessary utility hooks. This is only necessary for passes defined directly

/// in C++. Passes defined declaratively use a cleaner mechanism for providing

/// these utilities.

struct MyOperationPass : public PassWrapper<OperationPass<>, MyOperationPass> {

void runOnOperation() override {

// Get the current operation being operated on.

Operation *op = getOperation();

...

}

};

从属语言 

必须先在MLIRContext中加载语言,然后才能从这些语言创建实体(算子,类型,属性等)。在开始执行多线程传递管道之前,还必须加载语言。为此,可能无法保证已从其加载的语言创建实体的过程,必须pass重写getDependentDialects() 方法并明确声明此语言列表来表达。

分析管理 

一个重要的概念,以及转换过程,都是分析。这些在概念上与转换过程相似,不同之处在于不修改特定算子的情况下计算信息。在MLIR中,分析不是pass而是pass独立式类,这些类是按需延迟计算并缓存的,以避免不必要的重新计算。MLIR中的分析必须遵循以下条件:

  • 提供一个采用Operation*的有效构造函数。
  • 不得修改给定的算子。

分析可能会提供其它钩子来控制各种行为:

  • bool isInvalidated(const AnalysisManager::PreservedAnalyses &)

给定一个保留的分析集,如果该分析确实无效,则该分析返回true。如果没有明确标记保留分析,但可以根据其它属性(例如分析集)保留(或使之无效),则可以进行更精细的失效。

查询分析 

基OperationPass类提供用于查询和保留当前正在处理的算子的分析的实用程序。

  • OperationPass自动提供以下实用程序来查询分析:
    • getAnalysis<>
      • 获得当前算子的分析,并在必要时进行构建。
    • getCachedAnalysis<>
      • 获取当前算子的分析(如果已经存在)。
    • getCachedParentAnalysis<>
      • 获取给定父算子的分析(如果存在)。
    • getCachedChildAnalysis<>
      • 对给定的子算子(如果存在)进行分析。
    • getChildAnalysis<>
      • 对给定的子算子进行分析,并在必要时进行构造。

使用上面定义的示例pass传递,看一些示例:

/// An interesting analysis.

struct MyOperationAnalysis {

// Compute this analysis with the provided operation.

MyOperationAnalysis(Operation *op);

};

void MyOperationPass::runOnOperation() {

// Query MyOperationAnalysis for the current operation.

MyOperationAnalysis &myAnalysis = getAnalysis<MyOperationAnalysis>();

// Query a cached instance of MyOperationAnalysis for the current operation.

// It will not be computed if it doesn't exist.

auto optionalAnalysis = getCachedAnalysis<MyOperationAnalysis>();

if (optionalAnalysis)

...

// Query a cached instance of MyOperationAnalysis for the parent operation of

// the current operation. It will not be computed if it doesn't exist.

auto optionalAnalysis = getCachedParentAnalysis<MyOperationAnalysis>();

if (optionalAnalysis)

...

}

保存分析 

在pass查询后构造的分析将被缓存,以避免不必要的计算(如果稍后再次请求)。为避免过时的分析,假定所有分析均pass传递而无效。为避免无效,过程必须专门标记已知保留的分析。

  • 所有Pass类都会自动提供以下用于保留分析的实用程序:
    • markAllAnalysesPreserved
    • markAnalysesPreserved<>

void MyOperationPass::runOnOperation() {

// Mark all analyses as preserved. This is useful if a pass can guarantee

// that no transformation was performed.

markAllAnalysesPreserved();

// Mark specific analyses as preserved. This is used if some transformation

// was performed, but some analyses were either unaffected or explicitly

// preserved.

markAnalysesPreserved<MyAnalysis, MyAnalyses...>();

}

pass失败 

允许MLIR中的传递正常失败。如果pass的某些不变性被破坏,可能会使IR处于某种无效状态,则可能发生这种情况。如果发生这种情况,signalPassFailure方法直接向pass管理器发出故障信号。如果pass指示执行时失败,则管道中不会执行其它任何传递,并且顶级调用PassManager::run将返回failure。

void MyOperationPass::runOnOperation() {

// Signal failure on a broken invariant.

if (some_broken_invariant)

return signalPassFailure();

}

pass管理器 

以上各节介绍了pass的不同类型及其不变性。本节介绍PassManager的概念,以及如何将其用于配置和计划pass管道。与pass管理相关的主要类别有两个,PassManager和和OpPassManager。 PassManager类作为顶层的入口点,并包含用于整个管道的各种配置。OpPassManager用于调度类会将以嵌套的一个特定的水平上运行。顶级 PassManager还用作OpPassManager。

OpPassManager  

AnOpPassManager本质上是要在特定类型的算子上执行的过程的集合。此算子类型必须符合以下要求:

  • 必须注册并标记 IsolatedFromAbove 。
    • 预期传递不会修改正在处理的当前算子或更高的算子。如果算子不是孤立的,则可能会无意间修改或遍历不应该执行的算子的SSA使用列表。

将pass添加到pass管理器addPass。该pass必须是采用 op-specific与相同算子类型OpPassManager的op-agnosticpass,或者是pass。

一个OpPassManager通常被cretedOpPassManagerpassnest<>方法明确嵌套内其它现有管道。此方法采用嵌套通行管理器将要算子的算子类型。在顶层,a PassManager充当OpPassManager。从这个意义上讲,嵌套对应 于 IR区域内的 结构嵌套 。

例如,以下content.mlir:

module {

spv.module "Logical" "GLSL450" {

func @foo() {

...

}

}

}

具有以下嵌套结构:

`module`

`spv.module`

`function`

下面是构造在上述结构上运行的管道的示例:

// Create a top-level `PassManager` class. If an operation type is not

// explicitly specific, the default is the builtin `module` operation.

PassManager pm(ctx);

// Note: We could also create the above `PassManager` this way.

PassManager pm(ctx, /*operationName=*/"module");

// Add a pass on the top-level module operation.

pm.addPass(std::make_unique<MyModulePass>());

// Nest a pass manager that operates on `spirv.module` operations nested

// directly under the top-level module.

OpPassManager &nestedModulePM = pm.nest<spirv::ModuleOp>();

nestedModulePM.addPass(std::make_unique<MySPIRVModulePass>());

// Nest a pass manager that operates on functions within the nested SPIRV

// module.

OpPassManager &nestedFunctionPM = nestedModulePM.nest<FuncOp>();

nestedFunctionPM.addPass(std::make_unique<MyFunctionPass>());

// Run the pass manager on the top-level module.

ModuleOp m = ...;

if (failed(pm.run(m)))

... // One of the passes signaled a failure.

上面的过程管理器包含以下管道结构:

OpPassManager<ModuleOp>

MyModulePass

OpPassManager<spirv::ModuleOp>

MySPIRVModulePass

OpPassManager<FuncOp>

MyFunctionPass

然后,这些管道一次运行一次。这意味着,例如,给定FuncOp上的一系列连续传递,它将在第一个函数上全部执行,然后在第二个函数上全部执行,依此类推,直到整个程序都pass传递为止。这提供了几个好处:

  • 这改善了编译器的缓存行为,因为它一次只连接一个功能函数,而不遍历整个程序。
  • pass减少需要调度的作业数量以及提高每个作业的效率,这提高了多线程性能。整个函数管道可以在每个函数上异步运行。

动态pass管道 

在某些情况下,在另一个遍历中运行一个遍历管道可能是有用的,以允许基于正在运行的当前算子的某些不变性进行配置或过滤。例如, Inliner Pass 可能希望在进行内联时,运行过程内pass简化,以产生更好的成本模型,并提供更好的内联。为了实现这一点,pass过程可以OpPassManagerpassLogicalResult Pass::runPipeline(OpPassManager &, Operation *)方法对正在执行的当前算子或嵌套在当前算子内的任何算子进行任意运算。与顶级PassManager::run方法的结果类似,此方法返回动态管道是成功还是失败。下面是一个简单的示例:

void MyModulePass::runOnOperation() {

ModuleOp module = getOperation();

if (hasSomeSpecificProperty(module)) {

OpPassManager dynamicPM("module");

...; // Build the dynamic pipeline.

if (failed(runPipeline(dynamicPM, module)))

return signalPassFailure();

}

}

注意:尽管上面在runOnOperation方法中构造了动态管道 ,但这不是必需的,并且应尽可能缓存管道,因为OpPassManager可以安全地复制该类。

每当pass管道以嵌套方式运行时,即当无法与主传递管道的其余部分一起静态调度嵌套管道时,都应使用本节中描述的机制。更具体地说,PassManager通常不应在内构造a Pass。使用runPipeline还可以确保将所有分析, 设备 和其它与过程管理器相关的组件与正在执行的动态管道集成在一起。

实例特定的pass选项 

MLIR提供了一种内置的机制,用于指定配置其行为的选项。这些选项在遍历构造时针对遍历的每个实例独立地进行解析。使用Option<>和 ListOption<>类定义选项,并遵循 LLVM命令行 标志定义规则。参见以下示例:

struct MyPass ... {

/// Make sure that we have a valid default constructor and copy constructor to

/// ensure that the options are initialized properly.

MyPass() = default;

MyPass(const MyPass& pass) {}

/// Any parameters after the description are forwarded to llvm::cl::list and

/// llvm::cl::opt respectively.

Option<int> exampleOption{*this, "flag-name", llvm::cl::desc("...")};

ListOption<int> exampleListOption{*this, "list-flag-name",

llvm::cl::desc("...")};

};

对于pass管道,PassPipelineRegistration模板采用其它模板参数作为可选的Option结构定义。该结构应继承mlir::PassPipelineOptions并包含所需的管道选项。使用PassPipelineRegistration时,构造函数现在使用带有签名的函数,该函数void (OpPassManager &pm, const MyPipelineOptions&) 应构造来自选项的传递,并将其传递给pm:

struct MyPipelineOptions : public PassPipelineOptions {

// The structure of these options is the same as those for pass options.

Option<int> exampleOption{*this, "flag-name", llvm::cl::desc("...")};

ListOption<int> exampleListOption{*this, "list-flag-name",

llvm::cl::desc("...")};

};

void registerMyPasses() {

PassPipelineRegistration<MyPipelineOptions>(

"example-pipeline", "Run an example pipeline.",

[](OpPassManager &pm, const MyPipelineOptions &pipelineOptions) {

// Initialize the pass manager.

});

}

传递统计信息 

统计信息是跟踪编译器正在执行的算子以及各种转换的有效性的一种方式。通常,查看特定转换对特定输入有什么影响以及触发频率有多有用。传递统计信息特定于每个传递实例,从而可以查看在传递管道内特定位置放置特定转换的效果。例如,帮助回答诸如“如果我再次在这里运行CSE会怎样?”之类的问题。

可以使用'Pass :: Statistic'类将统计信息添加到过程中。此类采用构造函数自变量:父传递,名称和描述。此类的作用类似于算子无符号整数,并且可以相应地增加和更新。这些统计信息依赖于相同的基础结构 llvm::Statistic ,因此具有相似的使用约束。收集的统计信息可以pass管理器以 编程方式pass 转储 PassManager::enableStatistics;或pass-pass-statistics和 -pass-statistics-display在命令行上。

一个例子如下所示:

struct MyPass ... {

/// Make sure that we have a valid default constructor and copy constructor to

/// ensure that the options are initialized properly.

MyPass() = default;

MyPass(const MyPass& pass) {}

/// Define the statistic to track during the execution of MyPass.

Statistic exampleStat{this, "exampleStat", "An example statistic"};

void runOnOperation() {

...

// Update the statistic after some invariant was hit.

++exampleStat;

...

}

};

收集的统计信息可以汇总为两种类型的视图:

管道视图,模拟了pass管理器的结构,这是默认视图:

$ mlir-opt -pass-pipeline='func(my-pass,my-pass)' foo.mlir -pass-statistics

===-------------------------------------------------------------------------===

... Pass statistics report ...

===-------------------------------------------------------------------------===

'func' Pipeline

MyPass

(S) 15 exampleStat - An example statistic

VerifierPass

MyPass

(S)  6 exampleStat - An example statistic

VerifierPass

VerifierPass

列表视图汇总了特定遍历所有实例的统计信息:

$ mlir-opt -pass-pipeline='func(my-pass, my-pass)' foo.mlir -pass-statistics -pass-statistics-display=list

===-------------------------------------------------------------------------===

... Pass statistics report ...

===-------------------------------------------------------------------------===

MyPass

(S) 21 exampleStat - An example statistic

 

Pass Infrastructure基础架构(上)的更多相关文章

  1. Pass Infrastructure基础架构(下)

    Pass Infrastructure基础架构(下) pass注册  PassRegistration该类在示例中简要显示了各种pass类型的定义 .该机制允许注册pass类,以便可以在文本pass管 ...

  2. nodejs学习笔记_nodejs和PHP在基础架构上的差别--共享状态的并发

    绝大多数对于Node.js的讨论都把关注点放在了处理高并发能力上,做开发的时候一定要明确node内部做出的权衡,以及node应用性能好的原因. node 为javascript引入了一个复杂的概念,: ...

  3. 云计算服务模型,第 1 部分: 基础架构即服务(IaaS)

    英文原文:Cloud computing service models, Part 1: Infrastructure as a Service 本文介绍三个云类别中的第一个:基础架构即服务(infr ...

  4. AI基础架构Pass Infrastructure

    AI基础架构Pass Infrastructure Operation Pass OperationPass : Op-Specific OperationPass : Op-Agnostic Dep ...

  5. 虚拟桌面基础架构(VDI)与终端服务和传统PC对比

    VDI(Virtual Desktop Infrastructure),即虚拟桌面基础架构,正迅速成为一个热门词汇,它将颠覆企业向终端用户交付应用的游戏规则.这篇专题就是想通过VDI与两种传统技术的对 ...

  6. 如何使用 Docker、ECS、Terraform 重建基础架构?

    早期 Segment 基础架构普遍组合在一起.我们通过 AWS 界面设定实例,使用许多闲散的 AMI,并且采用三种不同的部署方式. 然而随着商业的飞速发展,工程师团队的规模不断扩大,基础架构的复杂度也 ...

  7. 转://Oracle 高可用技术与云基础架构

    众所周知Oracle云基础架构已经在越来越多的行业里应用.大家了解云基础架构是如何演进的嘛?可能有人会说Oracle高可用技术是组成云架构的基础,那它们的关系是怎么样的?大家又了解Oracle高可用技 ...

  8. 关于云计算基础架构IaaS层的几点看法

    真实的云计算什么样? 云计算对普通用户来说,总是一个云里雾里的话题. 本文从最基础的概念開始科普,说明了四个常见的错误理解,和作者的四个猜想. IaaS(Infrastructure as a Ser ...

  9. ITIL《信息技术基础架构库》

    一 概述 1. ITIL 自上世纪70年代开始,个人计算机以及计算机网络开始在欧美发达国家普及.随着时间的推移,信息系统的规模越来越大,人们对信息系统的依赖也越来越强.特别是到了80年代,互联网开始普 ...

随机推荐

  1. Laravel 队列功能 简单应用

    生成任务类 默认情况下,应用程序的所有可排队任务都存储在 app/Jobs 目录下.如果 app/Jobs 目录不存在,则会在运行 make:job Artisan 命令时将创建它.你可以使用 Art ...

  2. hdu3622 二分+2sat

    题意:      给你N组炸弹,每组2个,让你在这N组里面选取N个放置,要求(1)每组只能也必须选取一个(2)炸弹与炸弹之间的半径相等(3)不能相互炸到对方.求最大的可放置半径. 思路:      二 ...

  3. Action: Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may ne

    更多精彩关注微信公众号 错误原因 在pom中引入了mybatis-spring-boot-starter ,Spring boot默认会加载org.springframework.boot.autoc ...

  4. Python JWT 介绍

    Python JWT 介绍 目录 Python JWT 介绍 1. JWT 介绍 2. JWT 创建 token 2.1 JWT 生成原理 2.2 JWT 校验 token 原理 3. 代码实现 4. ...

  5. shell 脚本中常用的内置变量

    在 Bash 解释器中,内置了许多变量,这些变量的功能是解释器自带的,我们在编写shell脚本时如果能灵活的使用它们,对脚本的编写效率以及差错大有帮助, 下面一一介绍这些变量 $FUNCNAME.$L ...

  6. C++ primer plus读书笔记——第3章 处理数据

    第3章 处理数据 1. C++对于变量名称的长度没有限制,ANSI C只保证名称中的前63个字符有意义(前63个字符相同的名称被认为是相同的,即使第64个字符不同). 2. 对类型名(int)使用si ...

  7. volatile 的使用

    ① 编译器很聪明,会帮我们做些优化,比如: int a; a = 0; // 这句话可以优化掉,不影响 a 的结果 a = 1; ② 有时候编译器会自作聪明,比如: int *p = ioremap( ...

  8. MSSQL·查看数据库编码格式

    阅文时长 | 0.67分钟 字数统计 | 837.6字符 主要内容 | 1.引言&背景 2.声明与参考资料 『MSSQL·查看数据库编码格式』 编写人 | SCscHero 编写时间 | 20 ...

  9. Elastic Stack(ElasticSearch 、 Kibana 和 Logstash) 实现日志的自动采集、搜索和分析

    Elastic Stack 包括 Elasticsearch.Kibana.Beats 和 Logstash(也称为 ELK Stack).能够安全可靠地获取任何来源.任何格式的数据,然后实时地对数据 ...

  10. [Java] Tomcat 部署

    背景 免费web服务器,Apache组织发布,Sun公司开发 基于Java,平台无关 可部署Web应用,为客户端提供服务 使用 启动 运行 \tomcat\bin\startup.bat 出现&quo ...