news 2026/9/18 6:10:25

CANN ops-transformer 旋转位置编码算子 RopeWithSinCosCache 深度解析:cache 化 RoPE 的计算原理与 aclnn 两段式调用实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-transformer 旋转位置编码算子 RopeWithSinCosCache 深度解析:cache 化 RoPE 的计算原理与 aclnn 两段式调用实战

CANN ops-transformer 旋转位置编码算子 RopeWithSinCosCache 深度解析:cache 化 RoPE 的计算原理与 aclnn 两段式调用实战

【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer

导读:本文聚焦 CANN ops-transformer 仓库中的旋转位置编码算子RopeWithSinCosCache(位于 posembedding/rope_with_sin_cos_cache),该算子是推理网络中用于将预计算的 sin/cos 位置编码表(cache)直接应用到 query/key 上的高性能实现。读完本文,你将完整掌握该算子的 rope/mrope 两种计算模式、GPT-NeoX 与 GPT-J 两种旋转风格的计算公式、全部入参约束,以及基于aclnnRopeWithSinCosCache两段式接口的落地调用方法,并能结合仓库源码理解其参数校验、多核 tiling 与 kernel 分派原理。


产品支持情况

依据 posembedding/rope_with_sin_cos_cache/README.md,该算子对产品的支持情况如下:

产品是否支持
Ascend 950PR/Ascend 950DT
Atlas A3 训练系列产品/Atlas A3 推理系列产品
Atlas A2 训练系列产品/Atlas A2 推理系列产品
Atlas 200I/500 A2 推理产品×
Atlas 推理系列产品×
Atlas 训练系列产品×
Kirin X90 处理器系列产品
Kirin 9030 处理器系列产品

需要补充说明的是:

  • 在 op_host/rope_with_sin_cos_cache_def.cpp 的算子定义中,RopeWithSinCosCache实际为ascend910bascend910_93ascend950kirinx90kirin9030五个平台配置了 AICore 算子实现(其中ascend950使用独立的rope_with_sin_cos_cache_apt实现,Kirin 平台使用独立配置),与产品支持矩阵一致。
  • Kirin X90/Kirin 9030 处理器系列产品不支持 BFLOAT16,其算子配置中 queryIn/keyIn/cosSinCache 仅支持DT_FLOATDT_FLOAT16(见 rope_with_sin_cos_cache_def.cpp)。
  • cacheMode扩展参数的 V2 接口aclnnRopeWithSinCosCacheV2目前不支持 Ascend 950PR/Ascend 950DT(见 docs/aclnnRopeWithSinCosCacheV2.md)。

功能说明:为推理性能设计的 cache 化旋转位置编码

设计动机

旋转位置编码(RoPE)在 Transformer 推理网络中广泛应用。常规实现中,每个 token 的 sin/cos 值需要在计算时临时生成;而在推理场景下,位置编码值往往是可预先计算并缓存的。RopeWithSinCosCache 算子正是将 sin 和 cos 输入通过 cache(cosSinCache)传入,直接按位置索引查表并执行旋转位置编码计算,从而省去重复计算,提升推理性能。

整体计算分三步:

  1. 按位置索引positionscosSinCache中取出对应的cosSin张量;
  2. cosSin沿最后一维切分为cossin两半;
  3. query/key拆分为需要旋转的部分queryRot(前rotaryDim维)和直接透传的部分queryPass(其余维度),对queryRot应用旋转,最后拼回。

mrope 模式:多模态位置编码

mrope 模式下,positions的 shape 为[m, numTokens],其中 m 为mropeSection的元素个数(支持 3 或 4),用于多模态(如图像、视频、文本混合)场景下对不同位置使用不同的位置编码频率:

cosSin[i] = cosSinCache[positions[i]] cos, sin = cosSin.chunk(2, dim=-1)

mropeSection元素个数为3时,按三段拼接cossin

cos0 = cos[0, :, :mropeSection[0]] cos1 = cos[1, :, mropeSection[0]:(mropeSection[0] + mropeSection[1])] cos2 = cos[2, :, (mropeSection[0] + mropeSection[1]):(mropeSection[0] + mropeSection[1] + mropeSection[2])] cos = torch.cat((cos0, cos1, cos2), dim=-1) sin0 = sin[0, :, :mropeSection[0]] sin1 = sin[1, :, mropeSection[0]:(mropeSection[0] + mropeSection[1])] sin2 = sin[2, :, (mropeSection[0] + mropeSection[1]):(mropeSection[0] + mropeSection[1] + mropeSection[2])] sin = torch.cat((sin0, sin1, sin2), dim=-1)

mropeSection元素个数为4时,使用更简洁的 split 形式表达同一语义:

cos = torch.cat([m[i] for i, m in enumerate(cos.split(mropeSection, dim=-1))], dim=-1) sin = torch.cat([m[i] for i, m in enumerate(sin.split(mropeSection, dim=-1))], dim=-1)

之后统一进入旋转计算(与 rope 模式共用):

queryRot = query[..., :rotaryDim] queryPass = query[..., rotaryDim:]

rope 模式:标准位置编码

rope 模式下,positions的 shape 为[numTokens](一维索引),直接查表并切分:

cosSin[i] = cosSinCache[positions[i]] cos, sin = cosSin.chunk(2, dim=-1) queryRot = query[..., :rotaryDim] queryPass = query[..., rotaryDim:]

说明:在 aclnn 接口中,mropeSection入参传nullptr即表示 rope 模式;传入非空mropeSection即开启 mrope 模式(详见 docs/aclnnRopeWithSinCosCache.md 参数说明)。从源码看,kernel 中以mropeSection[0] > 0作为进入 mrope 分支的判断条件(见 op_host/op_api/aclnn_rope_with_sin_cos_cache.cpp)。

rotate_half(GPT-NeoX style)旋转模式

isNeoxStyle=true时采用该模式,先将queryRot沿最后一维对半切分,再交叉相乘后拼接:

x1, x2 = torch.chunk(queryRot, 2, dim=-1) o1[i] = x1[i] * cos[i] - x2[i] * sin[i] o2[i] = x2[i] * cos[i] + x1[i] * sin[i] queryRot = torch.cat((o1, o2), dim=-1) query = torch.cat((queryRot, queryPass), dim=-1)

rotate_interleaved(GPT-J style)旋转模式

isNeoxStyle=false时采用该模式,按奇偶位交错拆分并旋转:

x1 = queryRot[..., ::2] x2 = queryRot[..., 1::2] o1[i] = x1[i] * cos[i] - x2[i] * sin[i] o2[i] = x2[i] * cos[i] + x1[i] * sin[i] queryRot = torch.stack((o1, o2), dim=-1) query = torch.cat((queryRot, queryPass), dim=-1)

keyIn执行完全相同的旋转流程,得到keyOut


参数说明

下表完整摘录自 posembedding/rope_with_sin_cos_cache/README.md:

参数名输入/输出/属性描述数据类型数据格式
positions输入Device 侧的 aclTensor,输入索引。INT32、INT64ND
queryIn输入Device 侧的 aclTensor,表示要执行旋转位置编码的第一个张量,公式中的queryBFLOAT16、FLOAT16、FLOAT32ND
keyIn输入Device 侧的 aclTensor,表示要执行旋转位置编码的第二个张量。BFLOAT16、FLOAT16、FLOAT32ND
cosSinCache输入Device 侧的 aclTensor,表示参与计算的位置编码张量。BFLOAT16、FLOAT16、FLOAT32ND
mropeSection输入mrope 模式下用于整合输入的位置编码张量信息,公式中的mropeSectionINT64-
headSize输入表示每个注意力头维度大小。INT64-
isNeoxStyle输入true 表示 rotate_half(GPT-NeoX style)计算模式,false 表示 rotate_interleaved(GPT-J style)计算模式。BOOL-
queryOut输出输出 query 执行旋转位置编码后的结果。FLOAT、FLOAT16、BFLOAT16ND
keyOut输出输出 key 执行旋转位置编码后的结果。FLOAT、FLOAT16、BFLOAT16ND

补充要点:

  • Kirin X90/Kirin 9030 处理器系列产品不支持 BFLOAT16(该说明位于 README 参数表下方)。
  • 在算子定义 rope_with_sin_cos_cache_def.cpp 中,除上述接口入参外,底层算子还包含numQHeadsnumKHeadsheadSizemropeSectionqStridekStrideisNeoxStylecacheMode等属性,其中qStride/kStride表示 query/key 行间步长(用于支持非连续输入),cacheMode默认为 0。

约束说明

调用该算子前需满足以下约束(源自 README 与 docs/aclnnRopeWithSinCosCache.md 的约束章节):

  1. queryInkeyIncosSinCache只支持2 维shape 输入;其中queryIn/keyInshape 为(numTokens, numQHeads*headSize)/(numTokens, numKHeads*headSize)cosSinCacheshape 为(maxSeqLen, rotaryDim)maxSeqLen表示模型处理序列的最大长度,rotaryDim表示旋转位置嵌入的维度大小。
  2. queryInkeyIncosSinCache数据类型需要保持一致,且queryOut/keyOut与对应输入数据类型一致。
  3. headSize:数据类型为 BFLOAT16 或 FLOAT16 时为32 的倍数,数据类型为 FLOAT32 时为16 的倍数(等价于 64 Byte 对齐,tiling 侧按headSize % (64 / 元素字节数) == 0校验)。
  4. rotaryDim:始终小于等于headSize;BFLOAT16/FLOAT16 时为 32 的倍数,FLOAT32 时为 16 的倍数;mrope 模式下应满足rotaryDim = mropeSection所有元素之和的 2 倍,即mropeSection[0] + mropeSection[1] + mropeSection2 = rotaryDim / 2
  5. 输入 tensorpositions的取值应小于cosSinCache的 0 维maxSeqLen
  6. aclnnRopeWithSinCosCache默认确定性实现
  7. mropeSection 取值限制:README 标注为[16, 24, 24];而 docs/aclnnRopeWithSinCosCache.md 与 V2 接口文档给出更完整的白名单:[16, 24, 24][24, 20, 20][8, 12, 12][16, 16, 16, 16],V2 接口另支持[11, 11, 10]。源码中的mropeSupportList校验了全部 5 种取值(见 aclnn_rope_with_sin_cos_cache.cpp)。

调用方式:aclnn 两段式接口

该算子通过 CANN 的 aclnn(AscendCL NN)两段式接口调用:必须先调用aclnnRopeWithSinCosCacheGetWorkspaceSize获取 workspace 大小与执行器,再调用aclnnRopeWithSinCosCache执行计算

接口原型

aclnnStatus aclnnRopeWithSinCosCacheGetWorkspaceSize( const aclTensor *positions, const aclTensor *queryIn, const aclTensor *keyIn, const aclTensor *cosSinCache, const aclIntArray *mropeSection, int64_t headSize, bool isNeoxStyle, aclTensor *queryOut, aclTensor *keyOut, uint64_t *workspaceSize, aclOpExecutor **executor);
aclnnStatus aclnnRopeWithSinCosCache( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);

第一段接口参数详解

参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensor
positions输入公式中的 positions,用于选取位置编码张量。不支持空 tensor;rope 模式 shape 为(numTokens);mrope 模式 shape 为(3, numTokens)(4, numTokens)INT64ND1-2
queryIn输入公式中的 query,要执行旋转位置编码的第一个张量。不支持空 tensor;要求 2D,shape 为(numTokens, numQHeads*headSize)BFLOAT16、FLOAT16、FLOAT32ND2
keyIn输入要执行旋转位置编码的第二个张量。不支持空 tensor;要求 2D,shape 为(numTokens, numKHeads*headSize)BFLOAT16、FLOAT16、FLOAT32ND2
cosSinCache输入表示参与计算的位置编码张量。不支持空 tensor;要求 2D,shape 为(maxSeqLen, rotaryDim)BFLOAT16、FLOAT16、FLOAT32ND2
mropeSection输入公式中的 mropeSection,mrope 模式下用于整合输入的位置编码张量信息。输入 mropeSection 表示开启 mrope 模式,不开启(rope 模式)时输入nullptraclIntArray---
headSize输入每个注意力头维度大小。需满足对齐约束(见上文)。INT64---
isNeoxStyle输入表示是否使用 GPT-NeoX 计算模式。true 表示 GPT-NeoX style;false 表示 GPT-J style。BOOL---
queryOut输出query 执行旋转位置编码后的结果。数据类型同 query;要求 2D,shape 为(numTokens, numQHeads*headSize)FLOAT32、FLOAT16、BFLOAT16ND2×
keyOut输出key 执行旋转位置编码后的结果。数据类型同 key;要求 2D,shape 为(numTokens, numKHeads*headSize)FLOAT32、FLOAT16、BFLOAT16ND2×
workspaceSize输出返回用户需要在 Device 侧申请的 workspace 大小。-----
executor输出返回 op 执行器,包含算子计算流程。-----

返回值与错误码

第一段接口完成入参校验,出现以下场景时报错:

返回值错误码描述
ACLNN_ERR_PARAM_NULLPTR161001传入的必选输入、必选输出或必选属性是空指针。
ACLNN_ERR_PARAM_INVALID161002输入和输出的数据类型、数据格式不在支持范围内。
ACLNN_ERR_INNER_TILING_ERROR561002多个输入 tensor 之间的 shape 信息不匹配,或输入属性与输入 tensor 的 shape 信息不匹配。
ACLNN_ERR_INNER_TILING_ERROR361001query 或 key 非 64 Byte 对齐;或 rotaryDim > headSize。

完整调用示例

以下为完整可运行流程(关键步骤摘录,完整样例见 examples/test_aclnn_rope_with_sin_cos_cache.cpp)。示例为 rope 模式(mropeSectionnullptr),采用 BFLOAT16 类型:

#include <iostream> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_rope_with_sin_cos_cache.h" #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) // 计算连续 tensor 的 strides 并用 aclCreateTensor 构造 aclTensor template <typename T> int CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size = 1; for (auto i : shape) size *= i; size *= sizeof(T); auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, return ret); ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, return ret); std::vector<int64_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 初始化(固定写法) int32_t deviceId = 0; aclrtStream stream; aclInit(nullptr); aclrtSetDevice(deviceId); aclrtCreateStream(&stream); // 2. 构造输入与输出(rope 模式:positions 为 1 维索引) std::vector<int64_t> positionsShape = {2}; // numTokens = 2 std::vector<int64_t> queryInShape = {2, 64}; // numQHeads*headSize = 2*32 std::vector<int64_t> keyInShape = {2, 64}; std::vector<int64_t> cosSinCacheShape = {2, 32}; // (maxSeqLen, rotaryDim) std::vector<int64_t> queryOutShape = {2, 64}; std::vector<int64_t> keyOutShape = {2, 64}; void* positionsDeviceAddr = nullptr; void* queryInDeviceAddr = nullptr; void* keyInDeviceAddr = nullptr; void* cosSinCacheDeviceAddr = nullptr; void* queryOutDeviceAddr = nullptr; void* keyOutDeviceAddr = nullptr; aclTensor* positions = nullptr; aclTensor* queryIn = nullptr; aclTensor* keyIn = nullptr; aclTensor* cosSinCache = nullptr; aclTensor* queryOut = nullptr; aclTensor* keyOut = nullptr; int64_t headSize = 32; // BF16 下需为 32 的倍数 bool isNeoxStyle = true; // true: GPT-NeoX style std::vector<int64_t> positionsHostData = {0, 1}; std::vector<float> queryInHostData(128, 1.0f); // 实际请填充真实数据 std::vector<float> keyInHostData(128, 1.0f); std::vector<float> cosSinCacheHostData(64, 1.0f); // 长度 = 2*32 std::vector<float> queryOutHostData(128, 0.0f); std::vector<float> keyOutHostData(128, 0.0f); CreateAclTensor(positionsHostData, positionsShape, &positionsDeviceAddr, aclDataType::ACL_INT64, &positions); CreateAclTensor(queryInHostData, queryInShape, &queryInDeviceAddr, aclDataType::ACL_BF16, &queryIn); CreateAclTensor(keyInHostData, keyInShape, &keyInDeviceAddr, aclDataType::ACL_BF16, &keyIn); CreateAclTensor(cosSinCacheHostData, cosSinCacheShape, &cosSinCacheDeviceAddr, aclDataType::ACL_BF16, &cosSinCache); CreateAclTensor(queryOutHostData, queryOutShape, &queryOutDeviceAddr, aclDataType::ACL_BF16, &queryOut); CreateAclTensor(keyOutHostData, keyOutShape, &keyOutDeviceAddr, aclDataType::ACL_BF16, &keyOut); // 3. 两段式调用:第一段获取 workspace 与执行器 uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; auto ret = aclnnRopeWithSinCosCacheGetWorkspaceSize( positions, queryIn, keyIn, cosSinCache, nullptr /* mropeSection, rope模式 */, headSize, isNeoxStyle, queryOut, keyOut, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, return ret); // 4. 按需申请 workspace void* workspaceAddr = nullptr; if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, return ret); } // 5. 第二段接口执行计算 ret = aclnnRopeWithSinCosCache(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, return ret); // 6. 同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, return ret); // 7. 释放资源 aclDestroyTensor(positions); aclDestroyTensor(queryIn); aclDestroyTensor(keyIn); aclDestroyTensor(cosSinCache); aclDestroyTensor(queryOut); aclDestroyTensor(keyOut); aclrtFree(positionsDeviceAddr); aclrtFree(queryInDeviceAddr); aclrtFree(keyInDeviceAddr); aclrtFree(cosSinCacheDeviceAddr); aclrtFree(queryOutDeviceAddr); aclrtFree(keyOutDeviceAddr); if (workspaceSize > 0) aclrtFree(workspaceAddr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }

要点提醒:

  • 若开启 mrope 模式,只需将示例中的nullptr替换为按aclIntArray构造的mropeSection(如{16, 24, 24}{16, 16, 16, 16}),并将positions构造为(3, numTokens)(4, numTokens)的二维张量。
  • 示例中queryIn等输入张量支持非连续(stride 不连续)Tensor,接口内部会先做Contiguous处理;queryOut/keyOut不支持非连续(见 aclnn_rope_with_sin_cos_cache.cpp 中l0op::Contiguousl0op::ViewCopy的组合使用)。

V2 接口:cacheMode 扩展

仓库同时提供了扩展接口aclnnRopeWithSinCosCacheV2(文档见 docs/aclnnRopeWithSinCosCacheV2.md,样例见 examples/test_aclnn_rope_with_sin_cos_cache_v2.cpp),与 V1 相比新增cacheMode参数,用于指示拼接 cos/sin 的方式:

  • cacheMode = 0:与aclnnRopeWithSinCosCache实现相同,为分段式拼接 cos 和 sin(即上文 mrope 三段/四段 cat 的逻辑)。
  • cacheMode = 1:为交错式拼接 cos 和 sin。以三段为例:
cosTmp = cos cos[..., 1:mropeSection[1] * 3:3] = cosTmp[1, ..., 1:mropeSection[1] * 3:3] cos[..., 2:mropeSection[1] * 3:3] = cosTmp[2, ..., 2:mropeSection[1] * 3:3] sinTmp = sin sin[..., 1:mropeSection[1] * 3:3] = sinTmp[1, ..., 1:mropeSection[1] * 3:3] sin[..., 2:mropeSection[1] * 3:3] = sinTmp[2, ..., 2:mropeSection[1] * 3:3]

V2 的约束差异:mrope 模式下cacheMode仅支持 0 和 1,且当mropeSection[16, 16, 16, 16]仅支持 0(tiling 侧同样做了该校验,见 rope_with_sin_cos_cache_tiling.cpp)。源码中 V1 接口内部固定传入cacheMode = 0,V2 才真正暴露该参数(见 aclnn_rope_with_sin_cos_cache.cpp)。


源码实现原理

算子定义与属性

在 op_host/rope_with_sin_cos_cache_def.cpp 中,RopeWithSinCosCache通过OpDef注册:

  • 4 个必选输入(positionsqueryInkeyIncosSinCache)与 2 个必选输出(queryOutkeyOut),数据类型均为DT_FLOAT / DT_FLOAT16 / DT_BF16(positions 为 INT64),格式均为 ND;
  • 属性包括必选的numQHeadsnumKHeadsheadSize,可选的mropeSection(默认{0, 0, 0})、qStridekStrideisNeoxStyle(默认 true)、cacheMode(默认 0);
  • 各平台配置均开启动态编译(DynamicCompileStaticFlag)、动态 rank 与动态 shape 支持。

入参校验逻辑

op_host/op_api/aclnn_rope_with_sin_cos_cache.cpp 中CheckParams依次完成四类校验:

  1. 空指针检查CheckNotNull):6 个 tensor 均非空;
  2. 数据类型检查CheckDtypeValid):positions 必须为 INT64,keyIn/cosSinCache/queryOut/keyOut的数据类型须与queryIn一致,且整体在支持列表内;
  3. shape 检查CheckShape):三个输入 tensor 均为 2 维、均非空、keyIn.shape[0] == queryIn.shape[0]、输出与输入 shape 相等;
  4. mropeSection 校验:元素个数必须为 3 或 4、元素非负、当mropeSection[0] > 0(mrope 模式)时要求所有元素之和的 2 倍等于rotaryDim(即cosSinCache第 1 维),且取值必须命中mropeSupportList白名单{16,24,24}、{8,12,12}、{24,20,20}、{11,11,10}、{16,16,16,16}(见 aclnn_rope_with_sin_cos_cache.cpp)。

Tiling 与多核切分

op_host/rope_with_sin_cos_cache_tiling.cpp 展示了 host 侧 tiling 策略,从中可以看到该算子典型的性能设计:

  • 按 token 数切分 corefront_coretail_core两级分配,将numTokens行数据近似均匀地分配到所有 AI Core,每个 core 内部再按 UB 容量分多轮循环搬运(loop_time_each_front_core/loop_time_each_tail_core);
  • 按 UB 空间约束分片:以maxUbSize(UB 内存)为上限,根据rotaryDimheadSize、数据类型字节数估算单轮可加载的最大 token 行数与 head 数(maxNPerLoopForUbnumHeadsForUb),超过时再沿 head 维度循环(loop_along_qheads/loop_along_kheads);
  • tilingKey 按数据类型分派:BF16 对应TILING_BF16 = 20、FP16 对应 21、FP32 对应 22(见 rope_with_sin_cos_cache_tiling.cpp),同时校验headSize/rotaryDim的 64 Byte 对齐与headSize >= rotaryDim
  • 全部 tiling 参数(约 30 个字段)定义在 op_host/rope_with_sin_cos_cache_tiling.h 中,kernel 侧通过GET_TILING_DATA读取。

Kernel 入口与数据类型分派

op_kernel/rope_with_sin_cos_cache.cpp 是 AI Core 侧入口函数rope_with_sin_cos_cache,接收position_id、query_in、key_in、cos_sin_cache、query_out、key_out、workspace、tiling八个 GM 地址参数,并按 tilingKey 实例化不同的模板实现:

  • TILING_KEY_IS(20)RopeWithSinCosCacheFP16<bfloat16_t>(BF16);
  • TILING_KEY_IS(21)RopeWithSinCosCacheFP16<half>(FP16);
  • TILING_KEY_IS(22)RopeWithSinCosCacheF32<float>(FP32)。

针对不同架构还提供了独立实现:op_kernel/arch35/目录下的rope_with_sin_cos_cache_f_bf16_arch35.hrope_with_sin_cos_cache_fp32_arch35.h等文件面向 arch35 平台,另有 op_kernel/rope_with_sin_cos_cache_apt.cpp 对应 Ascend 950 的 regbase 实现。

测试与验证

仓库为算子提供了完善的测试用例:

  • ST 用例:tests/st/aclnnRopeWithSinCosCache/atk_aclnnRopeWithSinCosCache.json 定义了 210 组边界与随机用例,覆盖 fp16/bf16/fp32 三种类型、headSize 32/64/96/128、rotaryDim 32/64/96/128 以及 numTokens 从 1 到 131073(2^17+1)的极大规模组合,测试数据还包含naninf-inf等异常值输入;
  • UT 用例:见 tests/ut/op_host 下的 infershape 与 tiling 单测、tests/ut/op_kernel 下的 kernel 单测及其 gen_data.py 数据生成脚本,可在无 NPU 环境下先行验证 tiling 与 kernel 逻辑。

小结

RopeWithSinCosCache 是 CANN ops-transformer 中面向推理场景的位置编码算子,核心价值在于:

  • cache 化设计:将 sin/cos 位置编码预计算并缓存,按positions索引查表,避免推理路径上的重复计算;
  • 双模式覆盖:支持标准 rope 与多模态 mrope(3/4 段mropeSection),并兼容 GPT-NeoX(rotate_half)与 GPT-J(rotate_interleaved)两种主流旋转风格;
  • 产品矩阵完善:覆盖 Ascend 950、Atlas A2/A3 系列与 Kirin X90/Kirin 9030,并提供 V1/V2(cacheMode)两套 aclnn 接口;
  • 实现细节完整:从算子定义、参数校验白名单、UB 感知的多核 tiling 到按数据类型分派的 kernel 模板,仓库源码均可逐一对照验证。

如需进一步深入,可继续阅读 docs/aclnnRopeWithSinCosCache.md(V1 接口完整说明)、docs/aclnnRopeWithSinCosCacheV2.md(V2 接口完整说明)以及上文列出的源码与测试文件。

【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 6:10:24

Chrome DevTools MCP与Playwright MCP深度对比:AI浏览器自动化选型指南

最近几个月&#xff0c;只要你在折腾 AI Agent&#xff0c;就一定绕不开 MCP 这个话题。协议本身不算复杂&#xff0c;真正让人纠结的是生态里那些"官方出品、看着都挺好"的服务端到底怎么选。浏览器自动化这块尤其典型&#xff1a;一边是 Google 的 Chrome DevTools…

作者头像 李华
网站建设 2026/9/18 6:08:37

Python迭代器与生成器核心解析及高效应用

1. Python迭代器与生成器核心概念解析在Python编程中&#xff0c;迭代器和生成器是处理大数据集和实现惰性求值的利器。很多初学者容易混淆这两个概念&#xff0c;其实它们既有联系又有本质区别。迭代器&#xff08;Iterator&#xff09;是一个可以记住遍历位置的对象&#xff…

作者头像 李华
网站建设 2026/9/18 6:06:28

Linux系统时间管理全攻略:硬件时钟、时区与NTP同步实践

上周有个同事跑过来问我&#xff0c;说新装的服务器时间总是不对&#xff0c;用date命令改好了&#xff0c;重启之后又跳回原来的错误时间&#xff0c;折腾了一下午没搞定。这个问题我在各种技术社群里见过太多次了——很多人对 Linux 系统时间的管理体系理解得不够深&#xff…

作者头像 李华
网站建设 2026/9/18 6:06:27

真空灭弧室小型化绝缘设计:Maxwell静电场仿真精准定位电场畸变

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 6:05:23

Apache Tika Colibri 文档解析与内容抽取实战

1. Colibri 到底是个什么东西第一次听到 colibri 这个词&#xff0c;很多人会先想到蜂鸟——南美那种体型极小、振翅频率极高、能在空中悬停的小鸟。Tika 项目组给这个图形化工具起这个名字&#xff0c;多少带点这个意思&#xff1a;轻、快、随时随地能停在你想停的位置上。但如…

作者头像 李华
网站建设 2026/9/18 6:05:15

conda与PyCharm环境协同原理与实操指南

1. 这不是“安装教程”&#xff0c;而是你真正需要的Python环境掌控逻辑你搜过“anaconda创建虚拟环境”“pycharm配置python环境”这类关键词&#xff0c;点开十篇教程&#xff0c;八篇在教你怎么点菜单、输命令、选路径——结果配好了跑不起来&#xff0c;报错看不懂&#xf…

作者头像 李华