ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CCV NNC Micro Ops(微算子)深度解析:用循环 IR 描述、融合与自动微分所有算子的设计实践

CCV NNC Micro Ops(微算子)深度解析:用循环 IR 描述、融合与自动微分所有算子的设计实践 计算机视觉深度学习【免费下载链接】ccvC-based/Cached/Core Computer Vision Library, A Modern Computer Vision Library项目地址https://gitcode.com/gh_mirrors/cc/ccv点击查看免费下载导读NNCNeural Network Collection是 CCV 项目中的机器学习框架层。随着模型形态的快速分化NNC 多年累积了约 50 个算子而 PyTorch、TensorFlow 的算子数量都超过 100 且仍在增长算子的跨平台实现CUDA / x86_64 / aarch64、直接卷积与 Winograd 等更是让实现与验证成本成倍上升。本文基于 doc/nnc-uops.rst 展开系统讲解 NNC 借鉴 Jittor 思路引入的micro ops微算子简称 uops方案以 reindex、逐元素一元/二元运算、reduce、select 四类元算子为骨架用一套极简的循环中间表示IR描述任意复杂算子并在此基础上完成循环融合、变量替换与自动微分。读完本文你将理解这套 IR 的块/循环/语句结构、$param参数机制、merge to left/right融合算法、保守变量替换策略以及从emit到emit_grad的六步自动微分管线并看到如何用 uops 完整表达一个卷积算子并通过随机 Oracle 测试验证正确性。一、动机算子数量爆炸与正确性优先的设计取向1.1 为什么需要一套能描述所有算子的机制一个由可微算子构造的机器学习框架天然会面临算子数量失控的问题。模型各有差异即使刻意限制算子种类NNC 也累积了约 50 个算子PyTorch 和 TensorFlow 则各超过 100 个且仍在增长原文档 doc/nnc-uops.rst 第 4 行。实现这些算子本身并不是最麻烦的部分麻烦在于为不同平台、不同优化技巧分别实现。以卷积为例至少存在 6 种实现CUDA 直接卷积、CUDA 3x3 优化 Winograd 实现、x86_64 直接实现、x86_64 3x3 Winograd、aarch64 直接实现、aarch64 3x3 Winograd——这还只是冰山一角仅 cuDNN 一个库里卷积实现就可能超过 7 个独立版本。Apache TVM 试图通过自动生成与调度算子内核来解决这种异构计算问题但那是另一条技术路线。1.2 正确性优先随机 Oracle 测试NNC 作者明确表示不追求极致性能NNC 是小项目异构环境下的性能调优容易消耗全部时间且成功率不定应交给更专业的团队而是关注算子的正确性以及一种能快速验证各种内核实现NNC 术语中称backend实现正确性的方法。验证正确性最有效的手段是随机化 Oracle 测试randomized Oracle tests用参考实现canonical implementation与待测内核在随机输入上对比输出。但 Oracle 测试的前提是有一份可信的参考实现。如何对参考实现本身建立信心作者的经验是代码行数越少、同一份代码被越多场景运行越容易建立信心。因此 NNC 的追求非常具体——用尽可能少的代码描述所有算子并在描述之下放一个极小的内核来执行它。Halide 及其启发的一众工作如 JAX接近这个目标但把 Halide 或 JAX 作为 NNC 的依赖并不现实XLA 这类完整内核也谈不上小而可验证。Jittor 给出了一个更轻量的答案定义少量、各自实现简单且易于求导的 meta-ops。二、核心思路Jittor 的 meta-ops 与 NNC 的 micro opsJittor 定义了一小组 meta-ops它们单独实现非常直接且很容易求导包括reindex重索引/张量重排与广播逐元素一元 / 二元运算element-wise unary / binary opsreduce归约select选择/掩码这些 meta-ops 单独运行时开销可能较大主要是内存把一个复杂算子分解为 meta-ops 组合常常需要先把值展开到更高维度再在高维空间做逐元素运算展开本身会带来可观的内存成本。因此循环融合loop-fusion这类常规优化几乎是让该方案变得可用的前提。NNC 完整采用了与 Jittor 相同的方案用寥寥几个 reindex 与逐元素算子就能简洁地描述各种算子由于这些 meta-opsNNC 称之为 micro-ops / uops各自实现容易整个实现应只有几千行代码且会被持续、高频地运行验证。实测仓库中的实现规模与文档判断一致核心实现 lib/nnc/ccv_nnc_micro_core.c1228 行、简化优化 lib/nnc/ccv_nnc_micro_simplify.c1481 行、组合与生命周期管理 lib/nnc/ccv_nnc_micro.c346 行、解释执行器 lib/nnc/ccv_nnc_micro_interpret.c380 行合计约 3400 行。三、描述 Micro Ops一套极简的循环中间表示要进行循环融合或从 micro ops 生成代码系统不仅要能执行micro ops还要能理解并优化它们因此必须用**中间表示IR**来描述 micro ops。3.1 IR 的块block、嵌套循环loop与语句statementNNC 使用一个非常专门的 IR。概括地说IR 由若干块blocks组成块之间串行执行每个块是一个嵌套循环nested loop每个循环包含起始索引start index、结束索引end index、循环携带变量数组loop-carried variable ids、以及要在循环内执行的语句数组statements嵌套循环总是先执行完内层循环再执行自己的语句起始/结束索引用索引表达式index expressions描述因此非常灵活。循环内的语句只有两种类型赋值语句assignment把求值后的表达式写入张量复合赋值语句compound assignment用循环携带变量与求值后的表达式做归约。整个 IR没有任何分支branching。这一点在 lib/nnc/_ccv_nnc_micro.h 中得到完整印证ccv_nnc_micro_loop_statement_t只区分CCV_NNC_MICRO_LOOP_STATEMENT_TYPE_ASSIGNMENT与CCV_NNC_MICRO_LOOP_STATEMENT_TYPE_COMPOUND_ASSIGNMENT两种类型第 126-141 行ccv_nnc_micro_loop_t携带carried_count/statement_count/start_index/end_index/carrieds/statements字段第 151-159 行ccv_nnc_micro_loop_block_t是许多互相嵌套的循环的容器第 161-166 行ccv_nnc_micro_function_t则是一串按序执行的循环块第 178-184 行ccv_nnc_micro_program_t把输入、输出、张量变量与函数串起来第 186-197 行。3.2 直接构造 IR而不是 DSL实现方式的差异Jittor 用一套DSL描述 meta-opsDSL 需要先解析成自己的 IRNNC 则用辅助函数helper functions直接构造 IR。NNC 只对索引表达式做解析一个相当直白的递归下降解析器recursive-descent parser目的是让 reindex 算子更容易描述。整体原则是尽量减少解析缩小实现面。同样该 IR不对张量应用 SSA静态单赋值——否则像把一个张量清零然后累加这类简单操作会变得非常复杂。SSA 通常要求每个变量恰好赋值一次而清零累加天然需要重复写同一位置。3.3 公开的 micro op 构建 API公共 API 声明在 lib/nnc/ccv_nnc.h第 558-698 行核心函数包括ccv_nnc_micro_input(dimensions)创建表示张量的自由输入第 590 行ccv_nnc_micro_reindex(shape, shape_count, ss, s_count, reindex, reindex_count, x)用 shape 表达式与 reindex 表达式把张量重索引为不同形状第 614 行ccv_nnc_micro_unary(op, x)/ccv_nnc_micro_binary(op, left, right)逐元素一元/二元运算第 621、629 行ccv_nnc_micro_reduce(op, axis, axis_count, x)对若干轴做归约第 638 行ccv_nnc_micro_select(axis, x, index)用索引张量按轴挑选值用于实现掩码类算子第 646 行ccv_nnc_micro_grad(x)表示某输出的梯度第 654 行ccv_nnc_micro_combine_new(...)把 micro ops 组合成一个算子并跑优化遍第 674 行以及配套的ccv_nnc_micro_combine_free、ccv_nnc_micro_combine_interpret解释模式仅用于调试内部、ccv_nnc_micro_combine_c生成 C 代码作为参考实现。算子枚举同样有明确证据一元算子只有NEG、LOG、EXP三种第 530-535 行二元算子有PLUS、MINUS、MUL、DIV、MAX、MIN、EQUAL_TO、LESS_THAN八种第 537-546 行归约算子有MAX、MIN、ARGMAX、ARGMIN、MEAN、SUM、PROD七种第 548-556 行。注意头文件注释特别提醒MEAN 归约比较特殊需要在循环之后静态地计算总数属于有意思的实现点。四、索引表达式与 reindex 的写法约定ccv_nnc_micro_reindex的表达式遵循一套简洁约定见 lib/nnc/ccv_nnc.h 第 592-604 行的注释整数参数以$开头如$kh、$kw、$kc维度用dXn表示如dA0、dA1、dA2……0 起始编号对输入张量的索引用i0、i1、i2……表示同样是 0 起始支持常量如235、431当前支持的运算符为-、、/、*。头文件给出的两个直观例子把张量x[w, h]广播到y[w, h, h]shape: { dA0, dA1, dA1 }reindex: { i0, i1, 0 }——输出三个轴中前两个来自 x第三个轴恒取常量 0即沿 h 维广播转置shape: { dA1, dA0 }reindex: { i1, i0 }——输出第 0 轴取输入的 i1第 1 轴取输入的 i0。解析这些表达式的是递归下降解析器索引表达式本身在 IR 中以ccv_nnc_micro_loop_index_term_t表示lib/nnc/_ccv_nnc_micro.h 第 34-65 行支持四种类型无NONE、id引用循环计数器/轴大小/标量、立即值VAL、二元表达式BINARY可递归组合、-、*、/、max、min。解释执行器 lib/nnc/ccv_nnc_micro_interpret.c 第 9-57 行_ccv_nnc_micro_index_interpret逐类型求值这些索引表达式可以看到 SCALAR_ID 支持CCV_8U、CCV_32S、CCV_64S三种整型标量BINARY 则按PLUS/MINUS/MUL/DIV/MAX/MIN分派运算。五、参数Parameters为 ahead-of-time 编译保留的$param与 Jittor 不同NNC 的实现不做激进的 JITjust-in-time。文档明确指出支持 JIT 意味着要假设 NNC 运行环境的许多细节这与 NNC 的设计哲学相悖。除非引入 QBE、MIR 这类轻量级方案否则 NNC 会用 micro ops 生成代码并预先编译ahead-of-timeAOT。既然不能对参数做激进的 JIT 处理这些参数就必须传入 AOT 生成的代码中。因此 NNC 为 reindex 支持$param语法且这些参数在生成代码时会被保留retained。参数机制的源码证据在 lib/nnc/ccv_nnc_micro.c 的ccv_nnc_micro_combine_new中第 62-76 行把参数名绑定为标量 idbind_scalars再通过_scalars_lookup在emit阶段把$name解析为对应的标量槽位。ccv_nnc_micro_combine_interpret在运行时接收与参数一一对应的ccv_nnc_micro_scalar_t值数组见 lib/nnc/ccv_nnc.h 第 680-692 行测试代码中即以{ .type CCV_32S, .i32 3 }这样的形式为$kh、$kw、$kc提供值见 test/unit/nnc/micro.tests.c 第 81-94 行。六、简化Simplification让生成的代码不那么慢、不那么吃内存为保证生成代码不会可怕地慢或可怕地吃内存NNC 在 IR 之上实现了至少两类优化循环融合loop-fusion变量替换variable substitution。此外由于自动微分选择了逐 uop 求导路线见后文还需要死代码消除dead-code elimination。6.1 循环融合按块匹配嵌套循环NNC 对 IR 做非常激进的循环融合。由于 IR 高度以循环为中心NNC 能够匹配相同的嵌套循环为术语清晰把用来匹配与合并的嵌套循环称为blocks——即使它们循环顺序不同。匹配规则一个循环与另一个循环可匹配当且仅当它们的起始索引与结束索引一致。但两个嵌套循环之间存在 1:1 的循环映射并不代表它们一定能合并。文档给出了一个经典的例子原文档第 57-98 行for (i 0; i 10; i) { // L1 for (j 2; j 5; j) { // L2 float a 0; for (k 0; k 2; k) { // L3 a x[i, j, k]; } y[i, j] a; } }for (i 0; i 10; i) { // M1 for (k 0; k 2; k) { // M2 float b 0; for (j 2; j 5; j) { // M3 b x[i, j, k]; } z[i, k] b; } }这两个嵌套循环之间存在 1:1 映射L1:M1、L2:M3、L3:M2。但如果强行合并会得到错误代码for (i 0; i 10; i) { // L1 float b 0; for (j 2; j 5; j) { // L2 float a 0; for (k 0; k 2; k) { // L3 a x[i, j, k]; b x[i, j, k]; // WRONG! z[i, k] b; // WRONG! } y[i, j] a; } }问题完全出在循环携带变量a与b上合并后b的累加被错误地搬进了内层 k 循环且写z[i, k]的位置也不对。如果把代码改写为直接对张量做复合累加、不使用局部携带变量则合并是正确的for (i 0; i 10; i) { // L1 for (j 2; j 5; j) { // L2 for (k 0; k 2; k) { // L3 y[i, j] x[i, j, k]; z[i, k] x[i, j, k]; } } }这是 NNC 与 Jittor 的又一个设计分歧点NNC 希望生成更像人手写出来的、更地道的代码idiomatic code。6.2 枢轴点pivot point与 O(n²) 重排算法因此匹配两个嵌套循环时应当把带有语句或循环携带变量的循环视为枢轴点pivot points。枢轴点之前、之后的循环可以重排但枢轴点本身不可移动。NNC 将这一不变量invariant强制应用于嵌套循环匹配并且只在不违反该不变量即可重排的前提下合并循环。为此设计了一个简单的O(n²) 重排算法。从源码看这个匹配逻辑实现在 lib/nnc/ccv_nnc_micro_simplify.c 的_ccv_nnc_same_loop第 87 行起它用left_right_link/right_left_link数组记录左右两侧循环的一一对应关系把从 0 到 1的单位循环标记为 ONE 直接视为可重排单元其余循环则通过_ccv_nnc_same_index_term第 11-76 行比较起止索引支持轴大小经 union-find 分组归并后的等价判断对应ccv_nnc_micro_equal_assertions收集的维度相等断言。6.3 数据依赖与merge to left/merge to right除了找到可合并的循环还要小心数据依赖。例如即使 block 1 与 block 3 匹配如果 block 3 读取了 block 2 写过的任何变量就不能合并这两个块。为此 NNC 为循环融合以及后续的死代码消除生成块级依赖信息block-level dependency information对给定变量可以得知它在哪个块被读、在哪个块被写。有了这份信息就能判断 block 3 是否与 block 2 存在数据依赖进而要么拒绝合并 block 1 与 block 3要么执行所谓的merge to right。正常情况下 NNC 的循环融合执行merge to leftblock 3 的语句全部移入 block 1block 3 变空。这种左合并以 O(n²) 方式检查两个块能否合并。如果 block 3 与 block 2 存在数据依赖而不能向左合并则转而检查block 2 是否依赖 block 1若 block 2 与 block 1 无数据依赖就把 block 1 向右合并进 block 3即merge to right最终块序变为[block 2, block 1 3]。这个技巧之所以有实际收益是因为在梯度回传中经常存在一个reset block——把所有值清零的块。reset block 通常与其他块没有数据依赖却恰好夹在两个本可合并的块之间。merge to right把这些 reset block 移到最前从而把前后两个块合并到一起。源码证据同样充分ccv_nnc_micro_program_simplifylib/nnc/ccv_nnc_micro_simplify.c 第 1291 行内部维护block_dependencies[i].merge_to i的并查结构第 487 行在合并扫描中以merge_to_right标志记录右合并方向第 678、691 行并在第 773-774 行把右块 merge_to 指向左块后清空左侧。头文件也明确声明了简化接口ccv_nnc_micro_program_simplifylib/nnc/_ccv_nnc_micro.h 第 480 行。6.4 变量替换Variable Substitution循环融合之后许多中间张量只被写一次、并在紧接着的下一条语句中被消费。这些张量可以被整体移除用其赋值语句的右值表达式替换这相当于编译器里的常量传播/临时变量消除。NNC 的变量替换策略相当保守只有当某个张量满足以下两个条件时才做替换——它只在一个循环中使用在那个循环里它的索引访问方式完全相同。即便如此保守由于循环融合后绝大多数中间张量都缩进了同一个循环内这一优化仍然能替换掉大量变量。七、自动微分Automatic Differentiation7.1 两种实现路线及其取舍对 uops 做自动微分有两种途径直接对 opcode 施加自动微分实现一次后可以对任何 opcode 序列求导。但由于 opcode 是带循环的自动微分后的代码会继续在这些循环内部求导为了适配各种带原则性的循环使用模式opcode 本身会变得更复杂为每个 uop 实现一个自动微分 passemit_grad直接使用这些生成的 opcode实现更简单但初期只支持一阶梯度。这并不是最终形态——完全有可能在 uops 内部形成闭环即用其他 uops 表示某个 uop 的梯度那大概需要再增加少量 uopsreindex-reduce、broadcast 与三元算子。NNC 选择了第二种方法逐 uop 实现emit_grad。也因此优化 passes 需要多扩充一点点——加入死代码消除。仓库中五种 micro op 的 vtab 均实现了emit_grad钩子lib/nnc/ccv_nnc_micro_core.c 中_ccv_nnc_micro_reindex_emit_grad第 436 行、_ccv_nnc_micro_unary_emit_grad第 599 行、_ccv_nnc_micro_binary_emit_grad第 721 行、_ccv_nnc_micro_reduce_emit_grad第 990 行、_ccv_nnc_micro_select_emit_grad第 1139 行。7.2 宏算子的梯度输入输出格式与 bitmaskuops 被设计为实现其他宏算子macro ops的轻松起点。宏算子的梯度 pass 遵循固定的输入输出格式若前向为|x| - |y|则梯度 pass 为|g(y)|x|y| - |g(x)|。宏算子有一个特定的bitmask方法用来标注哪些输入是必需的、哪些可以省略。例如exp(x) - y的梯度 pass 不需要知道y的值|g(y)|x|-| - |g(x)|就足够了-表示省略的位置。这正是从能否省掉某个输入出发的位掩码设计。因此在调用ccv_nnc_micro_combine_new时明确指定哪些是输入包括前向的输入/输出与输入梯度、哪些是期望的输出梯度会很有帮助NNC 为此引入了简单的ccv_nnc_micro_grad函数表示某个 uop 对应的梯度。ccv_nnc_micro_combine_new的完整签名lib/nnc/ccv_nnc.h 第 674 行同时接收inputs、parameters、outputs、ingrads、outgrads五组描述正是对这一设计的落实。7.3 自动微分的六步流程综合起来NNC 用 uops 完成自动微分的完整步骤原文档第 152-159 行为每个 uop 实现emit_grad生成梯度 pass 对应的 opcodes梯度程序按拓扑序emit前向 opcodes再按逆拓扑序emit_grad梯度 opcodes把梯度程序中指定的输入标注为终止点termination points从梯度程序指定的输出出发反向标记必需的块遇到标注的输入即停止——这就是活体分析liveness analysis基于第 4 步的活体分析执行死代码消除执行前面提到的其他优化 passes循环融合、变量替换等。从ccv_nnc_micro_combine_new的实现lib/nnc/ccv_nnc_micro.c 第 22-193 行可以看到这条流水线的落地顺序先做逆拓扑排序第 27-59 行绑定参数标量第 62-76 行给输入/输出/梯度编号第 78-92 行填张量形状第 93-109 行对每个 output 依次emit出函数第 110-118 行对 forward 跑一次ccv_nnc_micro_program_simplify第 136 行再构造 backward 函数——前向 emit 一遍、梯度 emit_grad 一遍第 137-148 行最后对 backward 程序再跑一次简化第 161 行。forward 与 backward 的 vars 形状共享combine-backward.vars vars第 158 行注释说明形状与相关分配不会在简化中被改变因此可以安全共享。八、实战验证用 uops 表达卷积并通过 Oracle 测试文档的抽象描述在仓库测试中有最直接的落地证据测试文件 test/unit/nnc/micro.tests.c 的第一个用例 represent convolution with micro ops, with external variables第 14-134 行完整演示了如何用四个 uops 拼出一个卷积。核心构造逻辑第 16-54 行ccv_nnc_micro_io_t x ccv_nnc_micro_input(4); ccv_nnc_micro_io_t xx ccv_nnc_micro_reindex((const char*[]){ dA0, dA1 - $kh 1, dA2 - $kw 1, $kh, $kw, dA3, $kc }, 7, x, 1, (const char*[]){ i0, i1 i3, i2 i4, i5 }, 4, x); ccv_nnc_micro_io_t w ccv_nnc_micro_input(4); ccv_nnc_micro_io_t ww ccv_nnc_micro_reindex((const char*[]){ dA0, dA1 - $kh 1, dA2 - $kw 1, $kh, $kw, dA3, $kc }, 7, x, 1, (const char*[]){ i6, i3, i4, i5 }, 4, w); ccv_nnc_micro_io_t yy ccv_nnc_micro_binary(CCV_NNC_MICRO_BINARY_OP_MUL, xx, ww); ccv_nnc_micro_io_t y ccv_nnc_micro_reduce(CCV_NNC_MICRO_REDUCE_OP_SUM, (const int[]){ 3, 4, 5 }, 3, yy);这段代码把输入x4 维重索引成 7 维滑动窗口形式输出形状{dA0, dA1-$kh1, dA2-$kw1, $kh, $kw, dA3, $kc}索引映射{i0, i1i3, i2i4, i5}实现了卷积的窗口滑动权重w重索引成相同 7 维形状{i6, i3, i4, i5}二者逐元素相乘后对轴 3、4、5即 kh、kw、kc 三个窗口/通道维做 SUM 归约——这正是卷积求和。$kh、$kw、$kc通过参数列表第 58-62 行传入。然后测试用ccv_nnc_micro_combine_new组合出带梯度的完整算子第 55-69 行用解释器执行前向ccv_nnc_micro_combine_interpret第 80-94 行并与现有的CMD_CONVOLUTION_FORWARD第 96 行在相同随机输入dsfmt 随机数下对比REQUIRE_TENSOR_EQ断言二者完全一致第 97 行。反向同理解释器执行 backward第 104-118 行与CMD_CONVOLUTION_BACKWARD第 121 行对比以1e-5容差断言第 122-123 行。这就是文档所说的随机化 Oracle 测试uops 组合作为参考实现与既有 backend 内核互证。第二个用例 represent convolution with micro ops, no external variables第 136 行起则演示了不用$参数、改用dB0/dB1/dB2/dA3[dB3]这种张量派生维度 相等断言的写法进一步说明 shape 表达式的两种模式外部参数 vs 兄弟张量的轴尺寸对应 lib/nnc/_ccv_nnc_micro.h 第 168-174 行ccv_nnc_micro_tensor_t中input/sibling字段input表示形状从哪个张量派生sibling表示形状相同的兄弟。九、结语uops 在 NNC 中的定位总结这套设计的闭环描述层四类 uopsreindex、逐元素一元/二元、reduce、select 极简循环 IR $param参数让复杂算子如卷积能以几十行、无分支的描述呈现优化层以ccv_nnc_micro_program_simplify为核心通过基于枢轴不变量的 O(n²) 循环匹配、块级依赖分析下的merge to left/right融合以及保守的变量替换把描述翻译成接近手写的、高效的循环代码求导层逐 uop 的emit_grad 逆拓扑序发射 终止点标注 活体分析死代码消除得到一阶梯度程序验证层解释执行器lib/nnc/ccv_nnc_micro_interpret.c与ccv_nnc_micro_combine_c的代码生成能力为随机 Oracle 测试提供参考实现从而在不追求极端性能的前提下用最少的代码为各种 backend 内核的正确性建立信心。这一整套机制的关键实现文件都位于lib/nnc目录下IR 结构与辅助构造函数见 lib/nnc/_ccv_nnc_micro.h组合算子生命周期与编号见 lib/nnc/ccv_nnc_micro.c五种 uops 的 emit/emit_grad 见 lib/nnc/ccv_nnc_micro_core.c融合与替换见 lib/nnc/ccv_nnc_micro_simplify.c可运行验证样例见 test/unit/nnc/micro.tests.c。对于希望在自有框架里用最小算子集 循环 IR 融合优化来 bootstrap 机器学习算子的开发者NNC 的 uops 是一份结构清晰、规模可控、且自带随机验证路径的参考实现。赞分享计算机视觉深度学习【免费下载链接】ccvC-based/Cached/Core Computer Vision Library, A Modern Computer Vision Library项目地址https://gitcode.com/gh_mirrors/cc/ccv点击查看免费下载相关推荐CANN ops-transformer 通算融合算子 aclnnAlltoAllMatmulAlltoAll 通信与 Matmul 计算的深度融合实践CANN ops transformer 通算融合算子 aclnnAlltoAllMatmulAlltoAll 通信与 Matmul 计算的深度融合实践 本篇算子库人工智能大模型深度学习CANNAscendMatMulAlltoAll 通算融合算子深度解析CANN ops-transformer 中 MatMulPermuteAlltoAll 的融合计算与量化实践MatMulAlltoAll 通算融合算子深度解析CANN ops transformer 中 MatMulPermuteAlltoAll 的融合计算与量算子库人工智能大模型深度学习CANNAscendG-Helper 快速教程用单个 exe 管住华硕笔记本的 5 项核心控制G Helper 快速教程用单个 exe 管住华硕笔记本的 5 项核心控制 G Helper 是一款面向华硕笔记本的轻量控制工具单个 exe 完成性能模式、桌面应用系统编程上一篇如何使用Windows11项目轻松定制系统10个必学注册表调整技巧下一篇终极镜像加速指南DaoCloud自动同步方案彻底解决Docker镜像下载难题创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表