MLIR

方言

  • 简介
    • 操作
    • 区域
      • 值范围
      • Control Flow and SSACFG Regions
    • 操作与多区域(Operations with Multiple Regions)
    • 闭包(Closure)
    • 图形区域(Graph Regions)
    • 参数和结果(Arguments and Results)
    • 属性
    • 类型系统
    • 类型别名
    • 方言类型
      • 内建类型
      • 属性
      • 属性
      • 方言属性
      • 内置方言

简介

方言(Dialects)是参与并扩展MLIR(多级中间表示,多级中间语言)生态系统的机制。它们允许定义新的操作、属性和类型。每个方言都有一个唯一的命名空间,这个命名空间会作为前缀添加到每个定义的属性、操作和类型前。例如,Affine方言定义的命名空间是:affine。

MLIR允许多个方言共存,即使这些方言不在主树结构内,也可以在一个模块内共同存在。方言由特定的传递过程产生和消费。MLIR提供了一个框架,可以在不同的方言之间进行转换,也可以在同一个方言内进行转换。

操作

MLIR引入了操作的概念,用于描述不同级别的抽象和计算。操作可以具有特定于应用程序的语义,并且是完全可扩展的,也就是说没有固定的操作列表。

每个操作由一个唯一的字符串标识来识别,例如"dim"、“tf.Conv2d”、“x86.repmovsb”、"ppc.eieio"等。操作可以返回零个或多个结果,接受零个或多个操作数。它还可以存储属性、具有属性的字典、具有零个或多个后继操作以及零个或多个封闭区域。

操作的内部表示相对简单,通常以字面形式包含所有这些元素。为了指示结果和操作数的类型,它还包括一个函数类型。

一个块(Block)是一个操作列表。在SSA CFG(静态单赋值形式控制流图)区域中,每个块代表一个编译器基本块,其中块内的指令按顺序执行,终结操作(terminator operations)实现基本块之间的控制流分支。

块中的最后一个操作必须是终结操作。一个只有单个块的区域可以通过在封闭操作上附加NoTerminator来免除这一要求。顶级的ModuleOp就是一个定义这种特性且其块体没有终结操作的操作的例子。

MLIR中的块接受一个块参数列表,表示方式类似于函数。块参数与由个别操作语义指定的值绑定。区域入口块的块参数也是区域的参数,这些参数的值由包含操作的语义决定。其他块的块参数由终结操作的语义决定,例如将该块作为后继者的分支。在具有控制流的区域中,MLIR利用这种结构隐式地表示控制流依赖值的传递,而无需传统SSA表示中的PHI节点的复杂细节。注意,不依赖控制流的值可以直接引用,不需要通过块参数传递。

以下是一个简单的函数示例,展示了分支、返回和块参数:

func.func @simple(i64, i1) -> i64 {
^bb0(%a: i64, %cond: i1): // 由^bb0支配的代码可以引用%a
  cf.cond_br %cond, ^bb1, ^bb2

^bb1:
  cf.br ^bb3(%a: i64)    // 分支传递%a作为参数

^bb2:
  %b = arith.addi %a, %a : i64
  cf.br ^bb3(%b: i64)    // 分支传递%b作为参数

// ^bb3从前驱接收一个参数,命名为%c,并将其与%a一起传递给bb4。
// %a直接从其定义操作引用,不通过^bb3的参数传递。
^bb3(%c: i64):
  cf.br ^bb4(%c, %a : i64, i64)

^bb4(%d : i64, %e : i64):
  %0 = arith.addi %d, %e : i64
  return %0 : i64   // 返回也是一个终结操作。
}

区域

区域是MLIR(多级中间表示)块的有序列表。区域内部的语义并不是由IR(中间表示)强加的,而是由包含该区域的操作定义的。MLIR当前定义了两种区域:SSACFG区域,用于描述块之间的控制流,以及Graph区域,不需要块之间的控制流。操作中的区域类型通过RegionKindInterface描述。

区域没有名称或地址,只有区域中包含的块有。区域必须包含在操作内,并且没有类型或属性。区域中的第一个块是一个特殊的块,称为“入口块”。入口块的参数也是区域本身的参数。入口块不能被列为任何其他块的后继块。

值范围

  • 区域的层次结构:
    区域提供了一种层次化的封装方式,这意味着你不能从一个区域跳转(branch)到另一个区域。例如,如果你有一个代码块在区域A,那么你不能直接跳转到区域B的代码块。
    值的作用范围
  • 区域自然地限制了值的可见性:
    在一个区域中定义的值不会逃逸到外部的区域。比如,在内部区域定义的变量,在外部区域是不可见的。
  • 区域内的操作:
    在一个区域内的操作可以引用外部区域定义的值,但前提是这些值在包含该区域的操作中是合法的。比如,如果外部区域允许使用某个变量,那么这个变量在内部区域也是可以使用的。
  • 限制引用的特性:
    可以使用一些特性(traits)来限制引用,例如 OpTrait::IsolatedFromAbove,或者使用自定义验证器来控制这些规则。
    示例解释:
"any_op"(%a) ({ // 如果 %a 在包含的区域中是可见的…
   // 那么 %a 在这里也是可见的。
  %new_value = "another_op"(%a) : (i64) -> (i64)
}) : (i64) -> (i64)

在这个例子中,如果 %a 在外部区域中是可见的,那么它在内部区域中也是可见的。

  • MLIR中的层次支配概念:
    MLIR(多级中间表示)定义了一种广义的“层次支配”概念,用来确定一个值是否“在作用范围内”以及是否可以被某个操作使用。
    在同一个区域内,值是否可以被另一个操作使用,取决于该区域的类型。
    如果一个值在一个区域中定义,那么只有当一个操作的父操作在同一区域且可以使用该值时,这个值才能被使用。
    一个区域的参数定义的值,可以被该区域内的任何操作使用。
    一个区域中定义的值,永远不能在该区域外部使用。

Control Flow and SSACFG Regions

在MLIR中,有一种叫做SSACFG的区域,这个区域的操作就像我们写代码那样,是按顺序执行的。简单来说:

  • 操作顺序执行:在一个操作执行前,它需要的所有数据(操作数)已经准备好并且有明确的值。操作执行后,这些数据的值保持不变,同时生成的结果也有明确的值。
  • 操作之间的控制流:操作一个接一个地执行,直到执行到块(代码段)末尾的“终止操作”。然后,控制流会根据终止操作的指示,跳到其他地方继续执行。

控制流的进入和退出

  • 进入区域:控制流总是从区域的第一个块(入口块)开始。
  • 退出区域:控制流可以通过任何带有合适终止操作的块退出区域。比如,某个块的终止操作指示跳回外部操作(像函数的返回)。

实例

func.func @accelerator_compute(i64, i1) -> i64 { // 一个SSACFG区域
^bb0(%a: i64, %cond: i1): // 被 ^bb0 支配的代码可以引用 %a
  cf.cond_br %cond, ^bb1, ^bb2

^bb1:
  // 这里定义的 %value 不支配 ^bb2
  %value = "op.convert"(%a) : (i64) -> i64
  cf.br ^bb3(%a: i64)    // 分支传递 %a 作为参数

^bb2:
  accelerator.launch() { // 一个SSACFG区域
    ^bb0:
      // 嵌套在 "accelerator.launch" 下的代码区域,它可以引用 %a 但不能引用 %value。
      %new_value = "accelerator.do_something"(%a) : (i64) -> ()
  }
  // %new_value 不能在区域外引用

^bb3:
  ...
}

说明

  • 支配关系和变量引用
    支配关系(Domination)在编译原理中指一个基本块B1支配另一个基本块B2,如果每次控制流进入B2之前必定会经过B1。这个概念帮助我们理解变量的可见性和生命周期。

块^bb0

  • 支配关系:bb0是函数的入口块,所以它支配所有其他块(bb1、bb2和bb3)。
  • 变量引用:bb0中的变量%a和%cond在bb0、bb1、bb2和bb3中都可以被引用,因为这些块都被bb0支配。

块^bb1

  • 支配关系:bb1不是其他任何块的支配块,因为从bb0到bb3可以通过bb2而不经过^bb1。
  • 变量引用:bb1定义了变量%value,但由于bb1不支配bb2和bb3,%value不能在bb2和bb3中引用。然而,%value可以在^bb1内部引用。

块^bb2

  • 支配关系:^bb2同样不支配其他任何块。
  • 变量引用:bb2中的加速器启动区域(accelerator.launch)是一个新的嵌套区域,虽然它可以引用来自外部块(bb0)的变量%a,但不能引用来自bb1的变量%value,因为bb1不支配^bb2。

加速器启动区域(accelerator.launch 内部的 ^bb0)

  • 支配关系:这个区域是独立的SSACFG区域,有它自己的控制流和支配关系。区域内的块^bb0支配区域内的所有操作。
  • 变量引用:区域内的操作可以引用外部区域的变量%a,但不能引用%value,因为%value的定义在当前区域的控制流之外(即不在这个区域内的支配链上)。

块^bb3

  • 支配关系:bb3既不支配也不被bb1或^bb2支配。
  • 变量引用:bb3只能引用在bb0中定义并且被传递下来的变量%a,但不能引用bb1中定义的%value,因为%value的作用范围仅限于bb1

综上所述

  • 入口块^bb0支配所有其他块,因此它定义的变量%a和%cond在整个函数中都是可见的。
    **bb1定义的变量%value**只能在bb1内部引用,不能在bb2和bb3中引用,因为bb1不支配bb2和^bb3。
    **加速器启动区域(accelerator.launch)**可以引用外部块(bb0)的变量%a,但不能引用来自bb1的变量%value。
    bb3只能引用bb0中定义并传递下来的变量%a,但不能引用^bb1中的%value。

操作与多区域(Operations with Multiple Regions)

  • 概念解释:
    在编程中,操作(operation)可以包含多个区域(region)。区域就像是操作内部的小块代码或逻辑。当控制流到达一个操作时,这个操作可以选择将控制权传递给它内部的任何一个区域。当控制流从一个区域返回时,操作可以继续将控制权传递给其他区域。一个操作可以同时管理多个区域,甚至可以调用其他操作中的区域。

  • 实际例子:
    假设我们有一个主操作 mainOp,它包含两个子区域 regionA 和 regionB。当 mainOp 被执行时,它首先将控制权传递给 regionA。当 regionA 完成后,mainOp 将控制权传递给 regionB。

"mainOp"() ({
  // regionA
  "opA"() : () -> ()
  // regionB
  "opB"() : () -> ()
}) : () -> ()
在这个例子中,mainOp 包含了 regionA 和 regionB 两个区域,并按顺序执行它们。

闭包(Closure)

  • 概念解释:
    闭包是一种将代码块和其环境打包成一个整体的技术。区域允许我们定义创建闭包的操作,将区域的主体“打包”成一个值。闭包可以在以后执行,具体执行的方式由操作定义。如果一个操作是异步执行的,调用方需要确保等待操作完成,以保证所用的值依然有效。

  • 实际例子:
    假设我们有一个操作 createClosure,它将一个区域打包成一个闭包,并返回一个函数值。

"createClosure"() ({
  // The region to be packed as a closure
  %result = "opInClosure"() : () -> (i32)
}) : () -> (function<i32()>)

在这个例子中,createClosure 将 opInClosure 操作打包成一个闭包,并返回一个函数值,可以在以后调用。

图形区域(Graph Regions)

在MLIR(多级中间表示)中,图形区域(graph region)的概念用于表示图状语义,即没有控制流但可能存在并发语义或通用有向图数据结构的情况。图形区域非常适合表示没有基本顺序的循环关系或耦合值之间的关系。例如,图形区域中的操作可以代表独立的控制线程,而值可以代表数据流。

图形区域有以下几个关键点:

  • 单一基本块:目前,图形区域被限制为只能包含一个基本块(entry block)。虽然这种限制没有特定的语义原因,但它被添加的目的是为了简化通过的基础设施,确保处理图形区域的各种传递(passes)能够正确处理反馈循环。未来,如果有需求,可能会允许多基本块的图形区域。

  • 操作和值的表示:在图形区域中,MLIR操作代表图中的节点,而每个MLIR值代表一个多边连接,即一个源节点和多个目标节点的连接。区域内定义的所有值都在区域内的作用域内,并且可以被区域内的其他操作访问。

  • 操作的顺序无关性:在图形区域中,基本块内操作的顺序和区域内基本块的顺序在语义上没有意义,非终止操作可以自由重排,例如通过规范化(canonicalization)进行重排。

  • 循环的可能性:在图形区域中,循环(cycles)可以发生在单个基本块内,也可以发生在基本块之间。

参数和结果(Arguments and Results)

  • 概念解释:
    一个区域的第一个块的参数被视为区域的参数。参数的来源由父操作的语义决定,可能对应操作本身使用的一些值。区域会生成一个(可能为空的)值列表,操作的语义定义了区域结果与操作结果之间的关系。

  • 实际例子:
    假设我们有一个操作 funcOp,它包含一个区域 funcRegion,区域的参数为 %arg1 和 %arg2。

module {
  func @main(%arg0: i32, %arg1: i32) -> i32 {
    %0 = "myFuncOp"(%arg0, %arg1) : (i32, i32) -> (i32)
    return %0 : i32
  }

  "myFuncOp"(%input1: i32, %input2: i32) -> (i32) {
    ^entry(%arg1: i32, %arg2: i32):
      %result = addi %arg1, %arg2 : i32
      return %result : i32
  }
}

关系解释

  • 父操作 myFuncOp:
    myFuncOp 是父操作,它包含一个区域。
    myFuncOp 接收两个输入参数 %input1 和 %input2,类型为 i32。
    区域 funcRegion:

  • funcRegion 是 myFuncOp 的区域。
    funcRegion 的第一个基本块 ^entry 接收两个参数 %arg1 和 %arg2,这些参数直接对应父操作 myFuncOp 的输入 %input1 和 %input2。
    区域参数:

  • ^entry 基本块的参数 %arg1 和 %arg2 被视为整个区域 funcRegion 的参数。
    这些参数的来源是父操作 myFuncOp 的输入 %input1 和 %input2。
    区域结果:

  • 在 ^entry 基本块内,我们执行一个加法操作 addi,计算 %arg1 和 %arg2 的和,并将结果存储在 %result。
    最后,区域返回计算结果 %result,这个结果成为操作 myFuncOp 的输出。

"test.graph_region"() ({ // 一个图形区域
  %1 = "op1"(%1, %3) : (i32, i32) -> (i32)  // 这是允许的,%1 和 %3 都在作用域内
  %2 = "test.ssacfg_region"() ({
     %5 = "op2"(%1, %2, %3, %4) : (i32, i32, i32, i32) -> (i32) // 这是允许的,%1, %2, %3, %4 都定义在包含的区域内
  }) : () -> (i32)
  %3 = "op2"(%1, %4) : (i32, i32) -> (i32)  // 这是允许的,%4 在作用域内
  %4 = "op3"(%1) : (i32) -> (i32)
}) : () -> ()

属性

类型系统

在编程中,每个数据都有一个类型,比如整数、浮点数、字符串等等。MLIR也是这样,但它有一个更灵活的类型系统,允许我们定义自己的类型。
在MLIR中,类型系统是开放的,这意味着你可以定义任何你需要的类型,没有一个预先固定的类型列表。这对于不同的应用程序来说非常有用,因为你可以创建特定的类型来满足你的需求。
类型的基本构成

在MLIR中,类型可以分为几种:

  • 类型别名(type-alias):一个类型的替代名字。
  • 方言类型(dialect-type):为特定应用定义的类型。
  • 内建类型(builtin-type):系统预定义的一些基本类型。

类型列表有两种表示方式:

  • 不带括号的类型列表:多个类型用逗号分隔,比如 int, float。
  • 带括号的类型列表:可以是空的括号(),也可以是多个类型用逗号分隔并包含在括号内,比如 (int, float)。

当我们使用一个带有特定类型的值时,通常用这样的形式表示:值: 类型。

函数类型

函数类型用一个箭头->连接输入类型和输出类型。比如,一个函数接受一个整数并返回一个浮点数,可以表示为:int -> float。如果有多个输入或输出类型,可以用括号括起来,比如:(int, float) -> (string, bool)。

类型别名

类型别名就像给一个复杂类型起了一个简短的名字。比如,!avx_m128 = vector<4 x f32> 这句话定义了一个别名!avx_m128,它相当于vector<4 x f32>。以后在代码中你可以用!avx_m128来代替vector<4 x f32>,这样代码会更简洁和易读。

!avx_m128 = vector<4 x f32>

// Using the original type.
"foo"(%x) : vector<4 x f32> -> ()

// Using the type alias.
"foo"(%x) : !avx_m128 -> ()

方言类型

方言类型是一种自定义的数据类型,它可以扩展现有的类型系统。就像编程语言允许你创建自定义的类和结构体一样,方言类型允许你在特定的命名空间内创建自定义的类型。
方言类型的两种表示方式

  • 不透明类型(Opaque Type):
    用尖括号 <> 包裹的详细内容。
    例如:!tf 表示一个 TensorFlow 的字符串类型。
    “不透明”指的是类型的具体内部结构或实现细节对外部系统或用户不可见。这意味着外部系统不需要知道或理解类型的具体实现,只需要知道这个类型存在并能够使用它。

  • 简洁类型(Pretty Type):
    省略了一些冗长的符号,使其更易读。
    例如:!tf.string 也是表示一个 TensorFlow 的字符串类型,但更简洁。

内建类型

内建类型就是MLIR(多级中间表示)提供的一些基础数据类型。就像编程语言里我们常见的整型、浮点型和函数类型一样,这些类型在MLIR中也是直接可以使用的,并且其他任何自定义扩展(叫做方言)都可以利用这些基础类型。

属性

属性是附加在某个操作上的额外信息。就像你给一个文件夹贴上标签一样,这些属性为操作提供了更多的背景信息或特性。这些信息可以是关于操作自身的特定数据,并且可以通过特定的方法进行访问和使用。

假设你有一个“加法操作”,你可以为这个操作添加一些属性,比如“这两个数字相加的结果是否需要四舍五入”。这个属性就存储在“加法操作”上,具体值可能是 true 或 false。你可以通过特定的方法读取这个属性并决定是否执行四舍五入。

%result = addi %a, %b : i32 { rounding = true }

属性

在编程中,属性(Attributes)是一种为操作(operation)添加额外信息的方式。想象一下,你在写一个食谱,每个步骤(操作)可能有一些特定的要求或注释(属性),这些要求或注释不能被改变,只能作为参考。

如何确定属性类型

  • 文档和规范:通常,MLIR操作的文档和规范会明确指出哪些属性是必需的,哪些是可选的。
  • 操作定义:在MLIR操作定义文件(.td文件)中,属性的定义通常会表明其重要性和必要性。
  • 上下文理解:通过理解操作的上下文和行为,判断属性是否是执行该操作所必需的。

方言属性

方言属性可以看作是给你的MLIR代码添加一些自定义的标签或者注释,这些标签可以携带特定的信息。就像你给你的代码打上“重要”、“需要优化”这样的标签一样,方言属性可以携带特定的信息供后续使用。

  • 假设你有一个自定义方言,命名空间为foo,你想要给某个操作添加一个字符串属性和一个复杂属性。
// 定义一个字符串属性
#foo<string<"example_string">>

// 定义一个复杂属性
#foo<"a123^^^" + bar>

// 在MLIR代码中使用这些属性
func @example() {
  %0 = "foo.operation"() { attr = #foo<string<"example_string">> } : () -> ()
  %1 = "foo.operation"() { complex_attr = #foo<"a123^^^" + bar> } : () -> ()
  return
}

在这个例子中,foo.operation操作使用了两个自定义的方言属性,一个是字符串属性,另一个是复杂属性。这些属性可以在后续的编译、优化或者代码生成过程中被利用。

内置方言

内置方言就像是MLIR系统提供的一些基础设施,这些基础设施包含了一些基本的工具和数据类型,所有人都可以直接使用,而不需要自己重新发明轮子。内置方言提供了一些通用的属性值和类型,这些属性和类型可以被任何方言直接使用,方便了不同方言之间的互操作。

  • 假设你需要使用一些基本的整数和浮点数属性,这些属性是MLIR系统内置的。
func @example() {
  // 使用内置的整数属性
  %0 = "builtin.operation"() { int_attr = 42 : i32 } : () -> ()
  
  // 使用内置的浮点数属性
  %1 = "builtin.operation"() { float_attr = 3.14 : f32 } : () -> ()
  
  return
}

在这个例子中,builtin.operation操作使用了内置的整数属性和浮点数属性。因为这些属性是内置的,所以任何方言都可以直接使用它们,而不需要自己定义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/772831.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop-11-MapReduce JOIN 操作的Java实现 Driver Mapper Reducer具体实现逻辑 模拟SQL进行联表操作

章节内容 上一节我们完成了&#xff1a; MapReduce的介绍Hadoop序列化介绍Mapper编写规范Reducer编写规范Driver编写规范WordCount功能开发WordCount本地测试 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学…

利用YOLOv8识别自定义模型

一、背景介绍 最近项目需要识别自定义物品&#xff0c;于是学习利用YOLOv8算法&#xff0c;实现物品识别。由于物体类别不再常规模型中&#xff0c;因此需要自己训练相应的模型&#xff0c;特此记录模型训练的过程。 二、训练模型的步骤 1.拍照获取训练图片&#xff08;训练图…

【前端CSS3】CSS显示模式(黑马程序员)

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、CSS元素显示模式&#xff1a;☀️☀️☀️2.1 什么是元素显示模式2.2 块元素2.3 行内元素2.4 行块元素2.5 元素显示模式的转换 三、总结&#x1f680;&#x1f680;&#x1f680; 一、前言&#x1f680;&#x1f…

操作符“->“

title: 操作符"->" date: 2024-01-16 00:00:00 categories: C箭头 tags: 箭头操作以及偏移量计算 #嘎嘎 操作符"->" ->是一个成员访问的操作&#xff0c;它的作用是通过一个指针来访问它所指向的对象的成员变量或成员函数。它的左边必须是一个指…

(vue)eslint-plugin-vue版本问题 安装axios时npm ERR! code ERESOLVE

(vue)eslint-plugin-vue版本问题 安装axios时npm ERR! code ERESOLVE 解决方法&#xff1a;在命令后面加上 -legacy-peer-deps结果&#xff1a; 解决参考&#xff1a;https://blog.csdn.net/qq_43799531/article/details/131403987

全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系,你知道多少?

全网最适合入门的面向对象编程教程&#xff1a;09 类和对象的 Python 实现-类之间的关系&#xff0c;你知道多少&#xff1f; 摘要&#xff1a; 本文主要对类之间的关系进行了基本介绍&#xff0c;包括继承、组合、依赖关系&#xff0c;并辅以现实中的例子加以讲解&#xff0…

Java的进程和线程

一Java的进程 二Java的线程 多线程 ◆如果在一个进程中同时运行了多个线程&#xff0c;用来完成不同的工作&#xff0c;则称之为“多线程”。 ◆多个线程交替占用CPU资源&#xff0c;而非真正的并行执行。 ◆多线程好处。 ◆充分利用CPU的资源。 ◆简化编程模型。 ◆良好的用…

js 递归调用 相同对象--数组递归调用

<div class="save-cl"> <a-button @click="saveCl" >保存为常用策略</a-button> </div> saveCl(){ console.log(this.form.filterList[0],--------常用策略)// 此对象为上图对象 console.log(this.allElementsHaveValue(thi…

AGI系列(7)Reflection 在 AI agent 中的应用实例

斯坦福大学教授吴恩达一直非常推崇AI Agent,之前他提出过AI Agent的四种工作模式,分别是Reflection(反思)、Tool use(工具使用)、Planning(规划)和Multi-agent collaboration(多智能体协同)。 近日,他又开源了一个翻译 AI Agent, 他认为 AI 智能体机器翻译对改进传…

阿里巴巴Arthas分析调优JVM实战及常量池详解

目录 一、阿里巴巴Arthas详解 Arthas使用场景 Arthas命令 Arthas使用 二、GC日志详解 如何分析GC日志 CMS G1 GC日志分析工具 三、JVM参数汇总查看命令 四、Class常量池与运行时常量池 字面量 符号引用 五、字符串常量池 字符串常量池的设计思想 三种字符串操作…

掌握电路交换与分组交换:计算机网络的核心技术

计算机网络是现代信息社会的基石&#xff0c;而交换技术是实现网络通信的核心。本文将详细介绍两种典型的交换方式&#xff1a;电路交换和分组交换&#xff0c;帮助基础小白快速掌握这两种技术的基本概念和区别。 什么是电路交换&#xff1f; 电路交换&#xff08;Circuit Swi…

技术革新引领钢材质量智能化检测新纪元,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建钢材工业生产场景下钢材缺陷智能检测识别系统

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;其应用领域不断拓宽&#xff0c;正深刻改变着传统产业的运作模式。在钢材生产这一基础工业领域&#xff0c;AI的引入正为钢材的质量检测带来革命性的变革。 在传统的钢材生产流程中&#xff0c;质量检测是确…

【C++】二叉搜索树的模拟实现

前言&#xff1a;今天我们学习相对来说比前面轻松一点的内容&#xff0c;二叉搜索树&#xff0c;在之前我们学习过二叉树今天的内容对于我们就会比较简单一点了&#xff0c;一起加油。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质…

从零开始学量化~Ptrade使用教程——安装与登录

PTrade交易系统是一款高净值和机构投资者专业投资软件&#xff0c;为用户提供普通交易、篮子交易、日内回转交易、算法交易、量化投研/回测/实盘等各种交易工具&#xff0c;满足用户的各种交易需求和交易场景&#xff0c;帮助用户提高交易效率。 运行环境及安装 操作系统&…

计算机的错误计算(二十一)

摘要 两个不相等数相减&#xff0c;差为0&#xff1a; ? 在计算机的错误计算&#xff08;十九&#xff09;中&#xff0c;高中生小明发现本应为0的算式结果不为0. 今天他又发现对本不为0的算式&#xff0c;计算机的输出为0. 在 Python 中计算 &#xff1a; 则输出为0. 若用 C…

必备的 Adobe XD 辅助工具

想要高效便捷的使用 Adobe XD&#xff0c; Adobe XD 插件是必不可少的&#xff0c; Adobe XD 的插件非常多&#xff0c;但 90%都是英文&#xff0c;并且良莠不齐。在这儿挑选 9 个好用的 Adobe XD 插件给大家&#xff0c;这里是我整理的一些实用 Adobe XD 插件&#xff0c;让你…

3-2 梯度与反向传播

3-2 梯度与反向传播 主目录点这里 梯度的含义 可以看到红色区域的变化率较大&#xff0c;梯度较大&#xff1b;绿色区域的变化率较小&#xff0c;梯度较小。 在二维情况下&#xff0c;梯度向量的方向指向函数增长最快的方向&#xff0c;而其大小表示增长的速率。 梯度的计算 …

UE5 07-给物体添加一个拖尾粒子

添加一个(旧版粒子系统)cascade粒子系统组件 ,在模板中选择一个开发学习初始包里的粒子

Elasticsearch:Ingest architectures - 摄取架构

我们提供各种采集架构&#xff0c;以满足各种用例和网络配置的需求。 要将数据采集到 Elasticsearch&#xff0c;请使用最符合你的需求和用例的选项。对于许多用户和用例来说&#xff0c;最简单的方法是使用 Elastic Agent 采集数据并将其发送到 Elasticsearch。Elastic Agent…