CANN ops-transformer 的 aclnnInplacePartialRotaryMul 算子:Inplace 部分旋转位置编码接口详解与实现剖析
CANN ops-transformer 的 aclnnInplacePartialRotaryMul 算子Inplace 部分旋转位置编码接口详解与实现剖析【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer本篇文章围绕 CANN ops-transformer 仓库中 posembedding/inplace_partial_rotary_mul 模块的 aclnn 接口文档展开系统讲解aclnnInplacePartialRotaryMul算子的功能定位、interleave 旋转位置编码的数学原理、partial_slice局部旋转机制、两段式接口的函数原型与完整参数语义、错误码表与约束说明并结合该模块的算子定义、InferShape、Tiling 与 Kernel 源码剖析其底层实现最后补充 Python 接口的调用方式与自动微分用法。读完本文你将掌握如何在 Ascend NPU 上以 Inplace 方式高效完成单路旋转位置编码RoPE的前向计算并理解其内部的数据切分与无操作no-op分支逻辑。算子功能概述aclnnInplacePartialRotaryMul执行单路旋转位置编码Rotary Position EmbeddingRoPE的 Inplace 计算直接修改输入张量xRef不产生新的输出张量。与常规 RoPE 算子最大的差异在于两点Inplace 语义输入张量xRef与输出共享同一块内存计算结果直接写回xRef省去输出张量的显存分配与搬运开销适合作为 LLM 推理/训练中 attention 前处理链路的一环。局部旋转通过partial_slice参数指定[start, end)范围仅对输入张量最后一维Head-Dim 维范围内的数据执行旋转位置编码范围之外的数据保持原值。这对应业界常见的部分维度旋转RoPE 变体如仅对 KV 压缩后的部分通道做旋转。该算子位于 CANN ops-transformer 的 posembedding/inplace_partial_rotary_mul 目录下同一目录中还提供了 PyTorch 接口docs/torchapi_inplace_partial_rotary_mul.md与图模式GEIR调用方式三者共享同一套算子实现。产品支持情况依据 aclnnInplacePartialRotaryMul.md 与 README.md产品支持情况如下产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品Atlas 310P 等不支持Atlas 训练系列产品Atlas 910 等不支持从算子定义源码 inplace_partial_rotary_mul_def.cpp 可以看到AICore 侧仅注册了ascend910b、ascend910_93即 Atlas A2/A3 训练系列对应 SoC与ascend950三个平台的配置与文档中的产品支持矩阵完全对应。其中ascend950平台通过ExtendCfgInfo(opFile.value, inplace_partial_rotary_mul_apt)指定了独立的 apt 内核文件对应 op_kernel/inplace_partial_rotary_mul_apt.cpp。计算原理与公式算子执行的旋转位置编码为 interleave 模式rotary_mode等于 1其计算过程与 PyTorch 语义等价x1 x[..., ::2] # 偶数下标分量 x2 x[..., 1::2] # 奇数下标分量 x_rotate torch.cat((-x2, x1), dim-1) # 旋转后的交错拼接 x x * cos x_rotate * sin即先按最后一维的奇偶下标将张量拆成两半对调并取负号后拼接为旋转向量再与cos、sin位置编码张量做逐元素乘加。数学表达为$$x_1 x[..., ::2]$$$$x_2 x[..., 1::2]$$$$x_{rotate} \mathrm{cat}(-x_2, x_1)$$$$x x \cdot \cos x_{rotate} \cdot \sin$$partial_slice 局部旋转机制partialSlice作用于输入张量的最后一维D 维以左闭右开区间[start, end)的形式指定需要旋转的范围不传值或 Python 接口传入None时默认按[0, 0]处理即整条 D 维参与旋转编码start与end相等切片长度为 0时不执行旋转位置编码直接返回no-op其余位置[0, start)与[end, D)的数据保持原值不变。被旋转的局部张量为x[..., start:end]cos与sin只作用于这一段其最后一维大小必须等于切片长度end - start。输入张量xRef采用 BSND 维度排布BBatch为批量大小SSeq-Length为序列长度NHead-Num为多头数量DHead-Dim为每个头的隐藏维度大小。partial_slice的区间即落在 D 维上。两段式接口与函数原型与 CANN 大多数 aclnn 算子一致aclnnInplacePartialRotaryMul采用两段式接口详见 docs/zh/context/two_phase_api.md先调用aclnnInplacePartialRotaryMulGetWorkspaceSize完成入参校验与 workspace 大小计算获取workspaceSize与executor再调用aclnnInplacePartialRotaryMul传入 workspace 与 executor 执行实际计算。aclnnStatus aclnnInplacePartialRotaryMulGetWorkspaceSize( const aclTensor *xRef, const aclTensor *cos, const aclTensor *sin, int64_t rotary_mode, const aclIntArray *partialSlice, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnInplacePartialRotaryMul( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)从 aclnn_inplace_partial_rotary_mul.cpp 的实现可以看到这两个 aclnn 接口是 aclnnInner 内部接口的轻量转发封装真实的入参校验、workspace 计算与执行逻辑在算子库内部完成。aclnnInplacePartialRotaryMulGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorxRef输入待执行旋转位置编码的张量公式中的 x。Inplace 模式xRef 同时作为输出写入结果-BFLOAT16、FLOAT16、FLOAT32ND4×cos输入位置编码张量公式中的 cos与 xRef 数据类型一致或者为 FLOAT32BFLOAT16、FLOAT16、FLOAT32ND4×sin输入位置编码张量公式中的 sin与 xRef 数据类型一致或者为 FLOAT32BFLOAT16、FLOAT16、FLOAT32ND4×rotary_mode输入旋转模式0 为 half 模式1 为 interleave 模式当前仅支持 interleave 模式INT64---partialSlice输入部分旋转的切片范围 [start, end)作用于最后一维不传值则默认整 D 轴做旋转编码start 和 end 相等时则不做旋转编码INT64 数组---workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含算子计算流程-----返回值与错误码第一段接口返回aclnnStatus状态码详见 docs/zh/context/aclnn_return_code.md。第一段接口完成入参校验出现以下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 xRef、cos 或 sin 是空指针ACLNN_ERR_PARAM_INVALID161002传入的 xRef、cos、sin 的数据类型不在支持范围内或 cos 与 sin 的数据类型不一致或精度组合不满足要求ACLNN_ERR_PARAM_INVALID161002传入的 rotary_mode 不为 1仅支持 interleave 模式ACLNN_ERR_PARAM_INVALID161002传入的 partialSlice 长度不为 2或取值范围不合法ACLNN_ERR_PARAM_INVALID161002传入的 xRef、cos、sin 的形状不满足约束维度不为 4或 cos 与 sin 形状不一致或 xRef 最后一维大小超过 1024或 xRef 最后一维不是 2 的倍数或 partialSlice 切片长度不是 2 的倍数其中精度组合不满足要求与算子定义源码 inplace_partial_rotary_mul_def.cpp 中声明的数据类型矩阵一致x支持 FP16/FLOAT32/BF16cos、sin额外兼容 FLOAT32即当 x 为半精度时cos/sin 可抬升为 FLOAT32 参与计算也就是 Kernel 侧mixed模板的来源。aclnnInplacePartialRotaryMul 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnInplacePartialRotaryMulGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream 流约束说明使用该接口时必须遵守以下约束依据 aclnnInplacePartialRotaryMul.md确定性计算aclnnInplacePartialRotaryMul默认为确定性实现确定性计算的一般说明可参考 docs/zh/context/determinism_compute.md。不支持非连续non-contiguousTensor。仅支持 interleave 模式rotary_mode 1half 模式0当前未开放。Inplace 执行输入 xRef 和输出共享同一个 Tensor计算结果直接写回输入 xRef。输入张量 xRef 的 shape 为 BSND 排布各 shape 约束如下xRef 最后一维D大小不超过 1024interleave 模式下 xRef 最后一维D必须为 2 的倍数partialSlice切片长度partialSlice[1] - partialSlice[0]也必须是 2 的倍数cos、sin 最后一维大小必须相同且必须等于partialSlice的切片长度partialSlice[1] - partialSlice[0]cos/sin 的 shape 必须与 xRef 满足 广播关系且存在平台差异Ascend 950PR / Ascend 950DTcos/sin 的 shape 当前只支持 BSND、B1ND、B11D、111D 四种排布Atlas A3 / Atlas A2 训练与推理系列产品cos/sin 的 shape 当前只支持 BS1D、B11D 两种排布即要求 B 轴保持相等。partialSlice 取值范围sliceStart ≥ 0sliceEnd ≥ 0sliceEnd ≤ xRef 最后一维D大小sliceLength sliceEnd - sliceStart 0当sliceEnd与sliceStart相同时不做旋转位置编码直接返回。完整调用示例C以下示例来自官方文档可直接参考 examples/test_aclnn_inplace_partial_rotary_mul.cpp 与编译与运行样例进行编译执行。示例中x的 shape 为{96, 1, 1, 512}B96, S1, N1, D512cos/sin的 shape 为{96, 1, 1, 64}partialSlice {448, 512}即只对最后一维的[448, 512)共 64 个元素做旋转编码。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_inplace_partial_rotary_mul.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. 固定写法device/stream初始化参考acl API手册 int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入需要根据API的接口定义构造 std::vectorint64_t xShape {96, 1, 1, 512}; std::vectorint64_t cosShape {96, 1, 1, 64}; std::vectorint64_t sinShape {96, 1, 1, 64}; int64_t rotary_mode 1; std::vectorint64_t partialSlice {448, 512}; void* xDeviceAddr nullptr; void* cosDeviceAddr nullptr; void* sinDeviceAddr nullptr; aclTensor* x nullptr; aclTensor* cos nullptr; aclTensor* sin nullptr; // Create host data (example data) std::vectorfloat xHostData { /* fill with your data */ }; std::vectorfloat cosHostData { /* fill with your data */ }; std::vectorfloat sinHostData { /* fill with your data */ }; ret CreateAclTensor(xHostData, xShape, xDeviceAddr, aclDataType::ACL_FLOAT, x); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(cosHostData, cosShape, cosDeviceAddr, aclDataType::ACL_FLOAT, cos); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(sinHostData, sinShape, sinDeviceAddr, aclDataType::ACL_FLOAT, sin); CHECK_RET(ret ACL_SUCCESS, return ret); aclIntArray* partialSliceArray aclCreateIntArray(partialSlice.data(), partialSlice.size()); // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnInplacePartialRotaryMul第一段接口 ret aclnnInplacePartialRotaryMulGetWorkspaceSize(x, cos, sin, rotary_mode, partialSliceArray, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(GetWorkspaceSize failed. ERROR: %d\n, ret); return ret); void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret;); } // 调用aclnnInplacePartialRotaryMul第二段接口 ret aclnnInplacePartialRotaryMul(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(InplacePartialRotaryMul failed. ERROR: %d\n, ret); return ret); // 4. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // Cleanup aclDestroyIntArray(partialSliceArray); aclDestroyTensor(x); aclDestroyTensor(cos); aclDestroyTensor(sin); aclrtFree(xDeviceAddr); aclrtFree(cosDeviceAddr); aclrtFree(sinDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }调用流程可归纳为四步初始化 ACL 环境 → 构造 aclTensor 输入与 aclIntArray 属性 → 两段式调用算子 → 同步并释放资源。注意xHostData需按真实推理数据填充且示例中 cos/sin 均以ACL_FLOAT构造——这正是文档允许的cos/sin 可为 FLOAT32 精度组合。从源码看实现原理算子定义数据类型与属性默认值inplace_partial_rotary_mul_def.cpp 通过OpDef注册算子其中输入x、cos、sin均声明REQUIRED格式为FORMAT_ND并通过AutoContiguous()声明自动连续化要求与文档不支持非连续 Tensor的约束对应输入cos、sin的数据类型列表为{FP16, FP32, BF16, FP32, FP32}即在 x 为 FP16/BF16 时允许 cos/sin 以 FP32 参与mixed 精度模板在 x 为 FP32 时 cos/sin 必须同为 FP32属性rotary_mode类型OPTIONAL、默认值 0属性partial_slice类型OPTIONAL、默认值{0, 0}与文档不传值则默认整 D 轴做旋转编码的语义一致输入与输出的张量名同为x直观体现了 Inplace 输入输出同地址的语义。InferShape输出即输入inplace_partial_rotary_mul_infershape.cpp 的实现非常简洁*yShape *xShape输出 shape 直接拷贝输入 shape输出数据类型同样直接继承输入数据类型。这也从推理层面印证了 Inplace 语义——输出张量与输入张量 shape、dtype 完全一致计算结果原地写回。Tiling数据切分与 no-op 分支tiling 头文件 中定义了InplacePartialRopeRegbaseTilingData包含 B、S、N、D、blockNumB/S/N、blockFactorB/S/N、ubLoopNum/ubFactor/ubTailFactorB/S/N 三个维度的核间与核内切分因子、sliceStart/sliceEnd/sliceLength以及 A3 平台专用的coreTUbLoopTime、ubFactor等字段同时定义了InplacePartialRotaryPosEmbeddingMode枚举HALF0、INTERLEAVE1、QUARTER2、DEEPSEEK_INTERLEAVE3与InplacePartialRopeLayout布局枚举NO_BROADCAST、BROADCAST_BSN、BSND、SBND、BNSD。Tiling 主流程DoTiling()中有两个值得注意的点no-op 提前返回当切片为空sliceStart sliceEnd时直接构造一份 sliceLength0 的 tiling 数据、SetBlockDim(1)、设置专门的 tiling key20040并申请固定大小的 workspace 后返回kernel 侧会检测sliceLength 0直接跳过计算按广播布局分发模板根据 x 与 cos 的 shape 关系判定layout_如 BSND 的aba_and_ba、B11D 的ab等再调用对应子类的SplitCore()、SplitUb()、ComputeUbFactor()完成核间/核内切分。Kernel按 TilingKey 分发inplace_partial_rotary_mul.cpp 是 AscendC 编写的 AICore 内核入口参数为x, cos, sin, y, workspace, tiling。内核首先读取 tiling 数据若headDim 0即 no-op 场景直接返回随后按 tiling key 分发到不同的处理模板TILING_KEY1/21/2走InplacePartialRotaryMulABADTYPE_X, true/false模板2000/2010/2020 等按 S、BS、BSN 三种切分维度 × half/bf16/float 三种数据类型分发到InterleavedSplitS、InterleavedSplitBS、InterleavedSplitBSN2001/2011 等对应带 pad 的变体InterleavedSplitSPad、InterleavedSplitBSPad、InterleavedSplitBSNPad2030/2040、2130/2140 等对应 mixed 精度模板cos/sin 为 FP32、x 为 half/bf16文件分布在 op_kernel 下的inplace_rotate_interleaved_split_*_mixed.h系列。由此可以看出内核针对不同的广播布局S/BS/BSN 切分、数据类型组合同精度 / mixed 精度以及是否 pad 设计了多套模板由 tiling 阶段根据实际 shape 与平台信息选定这也是该算子在不同 shape 组合下保持高效的原因。Python 接口与自动微分除了 aclnn C 接口该算子还封装了 PyTorch 接口cann_ops_transformer.inplace_partial_rotary_mul详见 docs/torchapi_inplace_partial_rotary_mul.md封装实现位于 torch_extension/inplace_partial_rotary_mul.py。cann_ops_transformer.inplace_partial_rotary_mul(x, r1, r2, *, rotary_modeinterleave, partial_sliceNone) - None参数语义与 aclnn 接口一一对应x对应 xRefBSND 排布bfloat16/float16/float32r1对应 cosr2对应 sinrotary_mode仅支持interleave默认值即 interleavepartial_slice默认None内部按[0, 0]处理。约束与 aclnn 完全一致D ≤ 1024、D 为 2 的倍数、切片长度为 2 的倍数、r1/r2 最后一维等于切片长度等。单算子模式调用import torch import torch_npu from cann_ops_transformer.ops import inplace_partial_rotary_mul torch_npu.npu.set_device(0) B 2 S 32 N 8 D 128 slice_start 0 slice_end 64 x torch.randn(B, S, N, D, devicenpu, dtypetorch.float16) r1 torch.randn(B, S, 1, slice_end - slice_start, devicenpu, dtypetorch.float16) r2 torch.randn(B, S, 1, slice_end - slice_start, devicenpu, dtypetorch.float16) inplace_partial_rotary_mul( x, r1, r2, rotary_modeinterleave, partial_slice[slice_start, slice_end], )该接口无返回值Nonex在计算后 shape 与 dtype 保持不变partial_slice指定范围以外的数据保持原值。训练模式调用自动微分当x.requires_gradTrue时接口内部会走InplacePartialRotaryMulFn一个torch.autograd.Function正向调用算子本体并将r1/r2保存反向时自动调用inplace_partial_rotary_mul_backward计算x的梯度。r1cos、r2sin的梯度不计算、始终为 None——这与 RoPE 的标准用法一致位置编码视为常量。import torch import torch_npu from cann_ops_transformer.ops import inplace_partial_rotary_mul torch_npu.npu.set_device(0) B, S, N, D 2, 32, 8, 128 slice_start, slice_end 0, 64 x torch.randn(B, S, N, D, devicenpu, dtypetorch.float16, requires_gradTrue) r1 torch.randn(B, S, 1, slice_end - slice_start, devicenpu, dtypetorch.float16) r2 torch.randn(B, S, 1, slice_end - slice_start, devicenpu, dtypetorch.float16) y x * 1.0 y.retain_grad() # 正向自动追踪计算图y被inplace修改无需接收返回值 inplace_partial_rotary_mul( y, r1, r2, rotary_modeinterleave, partial_slice[slice_start, slice_end], ) # 继续前向计算 loss y.sum() loss.backward() # 自动调用inplace_partial_rotary_mul_backward print(y.grad.shape) print(x.grad.shape) # r1.grad, r2.grad始终为Nonecos/sin不计算梯度从 inplace_partial_rotary_mul.py 源码可以看到InplacePartialRotaryMulFn.forward通过ctx.mark_dirty(x)声明 inplace 修改backward将 grad_output 连续化后调用反向算子反向算子同样支持空 Tensor 与切片长度为零的场景执行 no-op因此自动微分可正常使用。需要注意因算子为输入输出同地址操作x不能是requires_gradTrue的叶子张量。图模式调用Python 接口同样支持通过torch.compiletorchair后端以图模式运行另有图模式 C 调用样例 examples/test_geir_inplace_partial_rotary_mul.cpp算子 IR 定义见 op_graph/inplace_partial_rotary_mul_proto.himport torch import torch_npu import torchair from cann_ops_transformer.ops import inplace_partial_rotary_mul torch_npu.npu.set_device(0) B 2 S 32 N 8 D 128 slice_start 0 slice_end 64 class InplacePartialRotaryMulModel(torch.nn.Module): def forward(self, x, r1, r2): inplace_partial_rotary_mul( x, r1, r2, rotary_modeinterleave, partial_slice[slice_start, slice_end], ) return x model InplacePartialRotaryMulModel().npu() npu_backend torchair.get_npu_backend() model torch.compile(model, backendnpu_backend, dynamicFalse) x torch.randn(B, S, N, D, devicenpu, dtypetorch.float16) r1 torch.randn(B, S, 1, slice_end - slice_start, devicenpu, dtypetorch.float16) r2 torch.randn(B, S, 1, slice_end - slice_start, devicenpu, dtypetorch.float16) output model(x, r1, r2)图模式封装torch_extension/graph_convert_inplace_partial_rotary_mul.py负责在 torchair 图模式下将高层调用转换为底层 GEIR 算子节点。总结aclnnInplacePartialRotaryMul是 CANN ops-transformer 在 posembedding 场景下提供的一个小而精的算子它以 Inplace 语义省去输出显存开销以partial_slice支持仅对 Head-Dim 的局部区间做旋转位置编码以 interleave 模式覆盖主流 RoPE 变体。接口层面提供 aclnn 两段式 C 接口、PyTorch 单算子/训练/图模式三种调用方式实现层面则由 OpDef 定义、InferShape输出即输入、按广播布局分发的多模板 Tiling 以及按 TilingKey 分发的 AscendC Kernel 共同构成并在空切片场景下通过 no-op 分支零开销返回。在模型部署或训练脚本中当你的 LLM 推理链路需要对 KV 缓存或中间激活张量的部分通道做原地旋转编码时可以直接复用本算子的 aclnn 或 Python 接口并严格遵循 D ≤ 1024、切片长度为 2 的倍数、cos/sin 最后一维等于切片长度等约束即可获得与框架内建 RoPE 语义一致的确定性计算结果。【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考