Pass Infrastructure基础架构(下)

pass注册 

PassRegistration该类在示例中简要显示了各种pass类型的定义 。该机制允许注册pass类,以便可以在文本pass管道描述中创建它们 。注册示例如下所示:

void registerMyPass() {

PassRegistration<MyPass>("argument", "description");

}

  • MyPass 是派生密码类的名称。
  • “参数”是用于以文本格式引用过程的参数。
  • “说明”是pass的简短说明。

对于无法默认构造的pass,PassRegistration接受可选的第三个参数,该参数接受回调以创建pass:

void registerMyPass() {

PassRegistration<MyParametricPass>(

"argument", "description",

[]() -> std::unique_ptr<Pass> {

std::unique_ptr<Pass> p = std::make_unique<MyParametricPass>(/*options*/);

/*... non-trivial-logic to configure the pass ...*/;

return p;

});

}

例如,可以使用这种注册变体来接受命令行参数的传递配置,并将其传递给pass构造函数。

注意:请确保该pass以不共享数据的方式是可复制构造的,因为 pass管理器 可能会创建该pass的副本以并行运行。

pass管道注册 

上面描述的是用于注册特定派生pass类的机制。最重要的是,MLIR允许以类似的方式注册自定义传递管道。这允许自定义管道以与传递相同的方式提供给mlir-opt之类的工具,这对于封装常见的管道(例如“ -O1”传递系列)很有用。管道pass类似的机制注册,形式为PassPipelineRegistration。与之相比PassRegistration,此类采用管道构造器形式的附加参数,该参数修改提供的参数OpPassManager。

void pipelineBuilder(OpPassManager &pm) {

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

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

}

void registerMyPasses() {

// Register an existing pipeline builder function.

PassPipelineRegistration<>(

"argument", "description", pipelineBuilder);

// Register an inline pipeline builder.

PassPipelineRegistration<>(

"argument", "description", [](OpPassManager &pm) {

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

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

});

}

文本传递管道规范 

前面的部分详细介绍了如何使用特定的参数和说明注册pass和pass管道。一旦注册,就可以使用它们从字符串描述中配置通道管理器。这对于mlir-opt从命令行配置过程管理器的工具,或作为利用动态过程管道的过程的选项的 工具尤其有用 。

为了支持描述传递管道的完整结构的能力,MLIR支持对传递管道的自定义文本描述。文字描述包括嵌套结构,运行的传递和传递管道的参数以及这些传递和管道的任何选项。文本管道定义为一系列名称,每个名称本身都可以递归包含嵌套的管道描述。该规范的语法如下:

pipeline          ::= op-name `(` pipeline-element (`,` pipeline-element)* `)`

pipeline-element  ::= pipeline | (pass-name | pass-pipeline-name) options?

options           ::= '{' (key ('=' value)?)+ '}'

  • op-name
    • 这对应于要继续运行的算子的助记符名称,例如func或module。
  • pass-name | pass-pipeline-name
    • 这对应于已注册的pass或pass管道的参数,例如cse或canonicalize。
  • options
    • 选项是特定的键值对,代表pass或传递管道定义的选项,如 “实例特定的传递选项” 部分所述。有关文本管道中的用法示例,请参见本节。

例如,以下管道:

$ mlir-opt foo.mlir -cse -canonicalize -convert-std-to-llvm='use-bare-ptr-memref-call-conv=1'

也可以指定为(pass-pass-pipeline标志):

$ mlir-opt foo.mlir -pass-pipeline='func(cse,canonicalize),convert-std-to-llvm{use-bare-ptr-memref-call-conv=1}'

为了支持使用来回传递一个通向文本表示的传递 OpPassManager::printAsTextualPipeline(raw_ostream&),请重写StringRef Pass::getArgument()以指定注册传递时使用的参数。

声明式pass规范 

可以pass类似于算子的形式声明性地指定pass的某些方面 。该规范简化了定义通道时使用的几种机制。它可用于生成过程注册调用,定义样板过程实用程序以及生成过程文档。

考虑以下在C ++中指定的过程:

struct MyPass : PassWrapper<MyPass, OperationPass<ModuleOp>> {

MyPass() = default;

MyPass(const MyPass &) {}

...

// Specify any options.

Option<bool> option{

*this, "example-option",

llvm::cl::desc("An example option"), llvm::cl::init(true)};

ListOption<int64_t> listOption{

*this, "example-list",

llvm::cl::desc("An example list option"), llvm::cl::ZeroOrMore,

llvm::cl::MiscFlags::CommaSeparated};

// Specify any statistics.

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

};

/// Expose this pass to the outside world.

std::unique_ptr<Pass> foo::createMyPass() {

return std::make_unique<MyPass>();

}

/// Register this pass.

void foo::registerMyPass() {

PassRegistration<MyPass>("my-pass", "My pass summary");

}

此pass可以这样声明地指定:

def MyPass : Pass<"my-pass", "ModuleOp"> {

let summary = "My Pass Summary";

let description = [{

Here we can now give a much larger description of `MyPass`, including all of

its various constraints and behavior.

}];

// A constructor must be provided to specify how to create a default instance

// of MyPass.

let constructor = "foo::createMyPass()";

// Specify any options.

let options = [

Option<"option", "example-option", "bool", /*default=*/"true",

"An example option">,

ListOption<"listOption", "example-list", "int64_t",

"An example list option",

"llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated">

];

// Specify any statistics.

let statistics = [

Statistic<"statistic", "example-statistic", "An example statistic">

];

}

使用gen-pass-decls生成器,可以自动生成上面的大多数样板。该生成器将一个-name参数作为输入,该参数为正在生成的一组pass提供标签。该生成器产生两个输出块:

第一个是用于在全局注册表中注册声明性传递的代码块。对于每次pass,生成器都会生成一个registerFooPasswhereFoo 是tablegen中指定的定义的名称。它还会生成一个 registerGroupPasses,其中Group是pass-name输入参数提供的标记,该标记会注册所有存在的pass。

// gen-pass-decls -name="Example"

#define GEN_PASS_REGISTRATION

#include "Passes.h.inc"

void registerMyPasses() {

// Register all of the passes.

registerExamplePasses();

// Register `MyPass` specifically.

registerMyPassPass();

}

第二个是每个通道的基类,其中包含与通道定义相关的大多数样板。这些类以的形式命名 MyPassBase,其中MyPass是tablegen中传递定义的名称。可以这样更新原始的C ++传递定义:

/// Include the generated base pass class definitions.

#define GEN_PASS_CLASSES

#include "Passes.h.inc"

/// Define the main class as deriving from the generated base class.

struct MyPass : MyPassBase<MyPass> {

/// The explicit constructor is no longer explicitly necessary when defining

/// pass options and statistics, the base class takes care of that

/// automatically.

...

/// The definitions of the options and statistics are now generated within

/// the base class, but are accessible in the same way.

};

/// Expose this pass to the outside world.

std::unique_ptr<Pass> foo::createMyPass() {

return std::make_unique<MyPass>();

}

使用gen-pass-doc生成器,可以生成每个pass的降价文档。参阅 Passes.md 以获取实际MLIRpass的示例输出。

Tablegen规范 

该Pass级用来启动一个新的通行定义。此类将传递给属性的注册表参数以及与传递所算子的算子类型相对应的可选字符串作为参数。该类包含以下字段:

  • summary
    • 该pass的简短摘要,用作注册pass时的描述。
  • description
    • pass的更长,更详细的描述。在生成pass文件时使用。
  • dependentDialects
    • 代表Dialect此过程的类的字符串列表可能会引入实体,属性/算子/类型/等。
  • constructor
    • 用于创建pass的默认实例的代码块。
  • options
    • pass使用的pass选项列表。
  • statistics
    • pass使用的pass统计信息列表。

选项 

选项可以passOption和ListOption类指定。该Option 班采用下列模板参数:

  • C ++变量名称
    • 用于生成的选项变量的名称。
  • 论据
    • 选项的参数名称。
  • 类型
    • 选项的C ++类型。
  • 默认值
    • 默认选项值。
  • 描述
    • 选项的一行描述。
  • 附加选项标志
    • 一个字符串,其中包含构造选项所必需的任何其它选项。

def MyPass : Pass<"my-pass"> {

let options = [

Option<"option", "example-option", "bool", /*default=*/"true",

"An example option">,

];

}

该ListOption班采取以下字段:

  • C ++变量名称
    • 用于生成的选项变量的名称。
  • 论据
    • 选项的参数名称。
  • 元素类型
    • 列表元素的C ++类型。
  • 描述
    • 选项的一行描述。
  • 附加选项标志
    • 一个字符串,其中包含构造选项所必需的任何其它选项。

def MyPass : Pass<"my-pass"> {

let options = [

ListOption<"listOption", "example-list", "int64_t",

"An example list option",

"llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated">

];

}

统计 

可以pass来指定统计信息Statistic,该参数采用以下模板参数:

  • C ++变量名称
    • 用于生成的统计变量的名称。
  • 显示名称
    • 显示统计信息时使用的名称。
  • 描述
    • 统计信息的一行描述。

def MyPass : Pass<"my-pass"> {

let statistics = [

Statistic<"statistic", "example-statistic", "An example statistic">

];

}

pass检测 

MLIRpass该类提供了一个可定制的框架,以pass过程执行和分析计算PassInstrumentation。此类提供了通向PassManager的钩子,用于观察各种事件:

  • runBeforePipeline
    • 该回调仅在执行传递管道(即传递管理器)之前运行。
  • runAfterPipeline
    • 成功执行传递管道之后,无论是否成功执行此回调。
  • runBeforePass
    • 该回调将在执行传递之前运行。
  • runAfterPass
    • 成功执行传递后立即运行此回调。如果执行此钩子,runAfterPassFailed则不会
  • runAfterPassFailed
    • 传递执行失败后立即运行此回调。如果执行此钩子,runAfterPass则不会
  • runBeforeAnalysis
    • 该回调在计算分析之前运行。
  • runAfterAnalysis
    • 在计算分析后立即运行此回调。

PassInstrumentation实例可以 pass方法直接向PassManager实例注册 addInstrumentation。添加到PassManager的工具以类似堆栈的方式运行,即最后一个执行runBefore*钩子的工具将是第一个执行相应runAfter*钩子的工具。PassInstrumentation 保证类的钩子以线程安全的方式执行,因此不需要额外的同步。在下面的示例设备中,该设备对计算DominanceInfo分析的次数进行计数:

struct DominanceCounterInstrumentation : public PassInstrumentation {

/// The cumulative count of how many times dominance has been calculated.

unsigned &count;

DominanceCounterInstrumentation(unsigned &count) : count(count) {}

void runAfterAnalysis(llvm::StringRef, TypeID id, Operation *) override {

if (id == TypeID::get<DominanceInfo>())

++count;

}

};

MLIRContext *ctx = ...;

PassManager pm(ctx);

// Add the instrumentation to the pass manager.

unsigned domInfoCount;

pm.addInstrumentation(

std::make_unique<DominanceCounterInstrumentation>(domInfoCount));

// Run the pass manager on a module operation.

ModuleOp m = ...;

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

...

llvm::errs() << "DominanceInfo was computed " << domInfoCount << " times!\n";

标准设备 

MLIR利用pass工具框架提供一些有用的开发人员工具和实用程序。这些工具中的每一个均可直接用于MLIR Pass框架的所有用户。

pass时间 

PassTiming设备提供有关pass执行和分析计算的时序信息。这可以快速了解哪些通道花费了最多的时间来执行,以及通道对管道总执行时间的影响。用户可以pass直接在PassManager上启用此检测enableTiming。也可以pass-pass-timing标志在mlir-opt中使用此工具。PassTiming设备为计时结果提供了几种不同的显示模式,下面分别介绍每种模式:

列表显示模式 

在这种模式下,结果显示在一个列表中,该列表按总时间排序,每个pass/分析实例汇总为一个唯一的结果。此视图有助于大致了解分析/pass最多花费的时间。此显示模式可在mlir-opt via中使用 -pass-timing-display=list。

$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline='func(cse,canonicalize)' -convert-std-to-llvm -pass-timing -pass-timing-display=list

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

... Pass execution timing report ...

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

Total Execution Time: 0.0203 seconds

---Wall Time---  --- Name ---

0.0047 ( 55.9%)  Canonicalizer

0.0019 ( 22.2%)  VerifierPass

0.0016 ( 18.5%)  LLVMLoweringPass

0.0003 (  3.4%)  CSE

0.0002 (  1.9%)  (A) DominanceInfo

0.0084 (100.0%)  Total

管道显示模式 

在这种模式下,结果将显示在嵌套管道视图中,该视图镜像在通道管理器中执行的内部通道管道。该视图对于特定地了解流水线中哪个部分花费最多的时间很有用,并且还可用于识别何时使分析无效和重新计算。这是默认显示模式。

$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline='func(cse,canonicalize)' -convert-std-to-llvm -pass-timing

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

... Pass execution timing report ...

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

Total Execution Time: 0.0249 seconds

---Wall Time---  --- Name ---

0.0058 ( 70.8%)  'func' Pipeline

0.0004 (  4.3%)    CSE

0.0002 (  2.6%)      (A) DominanceInfo

0.0004 (  4.8%)    VerifierPass

0.0046 ( 55.4%)    Canonicalizer

0.0005 (  6.2%)    VerifierPass

0.0005 (  5.8%)  VerifierPass

0.0014 ( 17.2%)  LLVMLoweringPass

0.0005 (  6.2%)  VerifierPass

0.0082 (100.0%)  Total

多线程传递时序 

在pass管理器中启用多线程后,显示的含义会稍有变化。首先,添加一个新的计时列User Time,显示所有线程花费的总时间。其次,该Wall Time 列显示在所有线程中花费的最长的个人时间。这意味着该Wall Time列将继续提供感知时间或时钟时间的指示符,而User Time则将显示总的cpu时间。

$ mlir-opt foo.mlir -pass-pipeline='func(cse,canonicalize)' -convert-std-to-llvm -pass-timing

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

... Pass execution timing report ...

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

Total Execution Time: 0.0078 seconds

---User Time---   ---Wall Time---  --- Name ---

0.0177 ( 88.5%)     0.0057 ( 71.3%)  'func' Pipeline

0.0044 ( 22.0%)     0.0015 ( 18.9%)    CSE

0.0029 ( 14.5%)     0.0012 ( 15.2%)      (A) DominanceInfo

0.0038 ( 18.9%)     0.0015 ( 18.7%)    VerifierPass

0.0089 ( 44.6%)     0.0025 ( 31.1%)    Canonicalizer

0.0006 (  3.0%)     0.0002 (  2.6%)    VerifierPass

0.0004 (  2.2%)     0.0004 (  5.4%)  VerifierPass

0.0013 (  6.5%)     0.0013 ( 16.3%)  LLVMLoweringPass

0.0006 (  2.8%)     0.0006 (  7.0%)  VerifierPass

0.0200 (100.0%)     0.0081 (100.0%)  Total

IR打印 

调试时,通常在传递管道的各个阶段转储IR很有用。这是IR打印设备发挥作用的地方。pass选择性地对正在执行的遍历进行过滤,该设备可以在遍历执行之前和之后有条件地打印IR。该工具可以passenableIRPrinting方法直接添加到PassManager 。mlir-opt提供了一些使用此工具的有用标志:

  • print-ir-before=(comma-separated-pass-list)
    • 在pass列表中提供的每个pass之前打印IR。
  • print-ir-before-all
    • 在管道中的每一次pass之前都要打印IR。

$ mlir-opt foo.mlir -pass-pipeline='func(cse)' -print-ir-before=cse

*** IR Dump Before CSE ***

func @simple_constant() -> (i32, i32) {

%c1_i32 = constant 1 : i32

%c1_i32_0 = constant 1 : i32

return %c1_i32, %c1_i32_0 : i32, i32

}

  • print-ir-after=(comma-separated-pass-list)
    • 在pass列表中提供的每个pass之后,打印IR。
  • print-ir-after-all
    • 每次pass管道后,请打印IR。

$ mlir-opt foo.mlir -pass-pipeline='func(cse)' -print-ir-after=cse

*** IR Dump After CSE ***

func @simple_constant() -> (i32, i32) {

%c1_i32 = constant 1 : i32

return %c1_i32, %c1_i32 : i32, i32

}

  • print-ir-after-change
    • 如果pass更改了IR,则仅在pass之后打印IR。这有助于减少“无趣”pass的IR转储数量。
    • 注意:pass比较传递前后算子的哈希值来检测更改。这增加了额外的运行时间来计算IR的哈希值,在极少数情况下,取决于所使用的哈希算法的冲突率,可能会导致假阳性。
    • 注意:此选项应与上面的其它“以后打印”选项一起使用,因为仅此选项无法启用打印。

$ mlir-opt foo.mlir -pass-pipeline='func(cse,cse)' -print-ir-after=cse -print-ir-after-change

*** IR Dump After CSE ***

func @simple_constant() -> (i32, i32) {

%c1_i32 = constant 1 : i32

return %c1_i32, %c1_i32 : i32, i32

}

  • print-ir-module-scope
    • 无论pass类型或算子嵌套级别如何,始终打印顶层模块算子。
    • 注意:仅在禁用多线程(-mlir-disable-threading)时才应使用在模块范围内打印

$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline='func(cse)' -print-ir-after=cse -print-ir-module-scope

*** IR Dump After CSE ***  ('func' operation: @bar)

func @bar(%arg0: f32, %arg1: f32) -> f32 {

...

}

func @simple_constant() -> (i32, i32) {

%c1_i32 = constant 1 : i32

%c1_i32_0 = constant 1 : i32

return %c1_i32, %c1_i32_0 : i32, i32

}

*** IR Dump After CSE ***  ('func' operation: @simple_constant)

func @bar(%arg0: f32, %arg1: f32) -> f32 {

...

}

func @simple_constant() -> (i32, i32) {

%c1_i32 = constant 1 : i32

return %c1_i32, %c1_i32 : i32, i32

}

崩溃和失败复制 

MLIR中的 pass管理器包含一个内置的机制,即使发生崩溃或pass失败,也可以生成可复制的内容 。可以passPassManager::enableCrashReproducerGeneration或pass命令行标志 启用此功能 pass-pipeline-crash-reproducer。在任何一种情况下,都提供一个参数,该参数对应于.mlir应将可复制文件写入的输出文件名。可复制的内容包含正在执行的过程管理器的配置,以及运行任何过程之前的初始IR。潜在的可复制性可能具有以下形式:

// configuration: -pass-pipeline='func(cse,canonicalize),inline'

// note: verifyPasses=false

module {

func @foo() {

...

}

}

本地复制器生成 

可以向命令行传递一个附加标志PassManager::enableCrashReproducerGeneration,并passpass-pipeline-local-reproducer命令行指定 该附加标志 ,以表明pass管理器应尝试生成“本地”再现器。这将尝试在pass失败之前立即生成包含IR的复制器。这对于已知崩溃是在特定遍内或原始输入依赖于可能并不总是可用的成分(如语言或遍)的情况很有用。

例如,如果先前示例中的失败来自canonicalize,则将生成以下复制器:

// configuration: -pass-pipeline='func(canonicalize)'

// note: verifyPasses=false

module {

func @foo() {

...

}

}

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

  1. Pass Infrastructure基础架构(上)

    Pass Infrastructure基础架构(上) Operation Pass OperationPass : Op-Specific OperationPass : Op-Agnostic De ...

  2. 探索ABP基础架构-下

    配置应用程序 ASP.NET Core 的配置系统提供了一个基于键值对的配置方法.它是一个可扩展的系统,可以从各种资源中读取键值对,例如 JSON 设置文件.环境变量.命令行参数等等. 设置配置值 默 ...

  3. AI基础架构Pass Infrastructure

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

  4. OpenGL Insights 阅读有感 - Tile Based架构下的性能调校 翻译

    Performance Tunning for Tile-Based Architecture Tile-Based架构下的性能调校 by Bruce Merry GameKnife译 译序 在大概1 ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. hdu3594 强连通 tarjan

    题意: 判断是不是强连通图 ,同时每一条边必须只能在一个环里 思路:之前我的强连通用的全是双深搜,结果题目的第二个要求很难判断,一开始写了三个深搜加上并查集,结果越写越乱,其实就是在判断一个边是否只在 ...

  2. POJ2570 二进制,位运算,Floyd

    题意:       给你一个有向图,两点之间有多种连接方式,然后每次询问都问你点A,B之间有哪些方式可以到达,每个小字母是一个方式. 思路:       很巧妙的位运算和Floyd应用,借助Floyd ...

  3. office 2007

    Microsoft office2007免费版几乎包括了Word2007.Excel2007.PowerPoint.Outlook.Publisher.OneNote.Groove.Access.In ...

  4. FreeSql之Expression表达式拼接参数扩展

    在FreeSql源码中Expression表达式拼接默认最多支持到5个泛型参数,当我们使用表关联比较多的时候,就需要进行扩展. 新建一个类,将命名空间改为System.Linq.Expressions ...

  5. JavaScript 原始值与包装对象

    前言 随着 JavaScript 越来越流行,越来越多地开发者开始接触并使用 JavaScript. 同时我也发现,有不少开发者对于 JavaScript 最基本的原始值和包装对象都没有很清晰的理解. ...

  6. 大数据开发-Flink-数据流DataStream和DataSet

    Flink主要用来处理数据流,所以从抽象上来看就是对数据流的处理,正如前面大数据开发-Flink-体系结构 && 运行架构提到写Flink程序实际上就是在写DataSource.Tra ...

  7. 在微信框架模块中,基于Vue&Element前端,通过动态构建投票选项,实现单选、复选的投票操作

    最近把微信框架的前端改造一下,在原来基于Bootstrap框架基础上的微信后台管理,增加一套Vue&Element的前端,毕竟Vue的双向绑定开发起来也还是很方便的,而且Element本身也提 ...

  8. mysql基本命令(增,查,改,删)

    from oldboy egon

  9. 风变编程(Python自学笔记)第10关-工作量计算器

    1.%f的意思是格式化字符串为浮点型,%.1f的意思是格式化字符串为浮点型,并保留1位小数. 2.向上取整:ceil() 使用ceil()方法时需要导入math模块,例如 1 >>> ...

  10. Spring Boot 2.5.0 重新设计的spring.sql.init 配置有啥用?

    前几天Spring Boot 2.5.0发布了,其中提到了关于Datasource初始化机制的调整,有读者私信想了解这方面做了什么调整.那么今天就要详细说说这个重新设计的配置内容,并结合实际情况说说我 ...