Open
gather_for_rank1迁移 #220
suhaolongAdd on 8 days ago
gather_for_rank1迁移 #220
Open
Merged fromsuhaolong/ops-recmaster_gather1intoAscend/ops-recmaster
Total: 6 files changed+402-0
| @@ -0,0 +1,66 @@ | |||
| 1 | +# gather_for_rank1 | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +| 产品 | 是否支持 | | ||
| 6 | +| :--- | :------: | | ||
| 7 | +| Ascend 950PR&950DT系列产品 | √ | | ||
| 8 | +| Atlas A3系列产品 | √ | | ||
| 9 | +| Atlas A2系列产品 | √ | | ||
| 10 | + | ||
| 11 | +## 功能说明 | ||
| 12 | + | ||
| 13 | +对输入 x(一维张量)执行按索引取数(index_select / gather),即根据 index 中的索引值,从 x 中选择对应元素。 | ||
| 14 | + | ||
| 15 | +计算公式: | ||
| 16 | + | ||
| 17 | +```text | ||
| 18 | +y[i] = x[index[i]] | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +对应接口: | ||
| 22 | + | ||
| 23 | +- aclnn 接口:`aclnnGatherForRank1` | ||
| 24 | +- torch 接口:`ops_rec.ascendc.embedding.gather_for_rank1` | ||
| 25 | +- 反向(自动求导):`ops_rec.ascendc.embedding.index_select_for_rank1_backward`(独立反向算子,见聚合根 `index_select_for_rank1_backward/`) | ||
| 26 | + | ||
| 27 | +## 算子实现原理 | ||
| 28 | + | ||
| 29 | +输入张量 x 为一维张量,包含 `embed_dim` 个元素;索引张量 index 为一维张量,包含 `index_num` 个索引值。算子根据 index 中的索引值,从 x 中选择对应的元素并写入输出 y,输出张量 y 的每个元素 `y[i] = x[index[i]]`。 | ||
| 30 | + | ||
| 31 | +例如: | ||
| 32 | + | ||
| 33 | +```text | ||
| 34 | +x = [x0, x1, x2, ..., x128] # shape: [129] | ||
| 35 | +index = [3, 1, 4, 1, 5, ...] # shape: [index_num] | ||
| 36 | +y = [x[3], x[1], x[4], x[1], x[5]] # shape: [index_num] | ||
| 37 | +``` | ||
| 38 | + | ||
| 39 | +算子实现分两步: | ||
| 40 | + | ||
| 41 | +1. 将整个 x 张量拷贝进每个核的 Unified Buffer | ||
| 42 | +2. 按 index 分片均匀切分到各个核,逐块读取索引并执行 `Gather`,结果写回输出 y | ||
| 43 | + | ||
| 44 | +## 参数说明 | ||
| 45 | + | ||
| 46 | +| 参数名 | 输入/输出 | 参数类型 | 数据类型 | 数据格式 | shape | 说明 | | ||
| 47 | +|--------|---------|---------|---------|---------|-------|------| | ||
| 48 | +| x | 输入 | Tensor | FLOAT16 / FLOAT32 | ND | [embed_dim] | 仅支持一维,`0 < embed_dim <= 20480` | | ||
| 49 | +| index | 输入 | Tensor | INT64 | ND | [index_num] | 仅支持一维;index 内元素值不得超过 x 的长度(即 `< embed_dim`),否则可能导致算子执行失败 | | ||
| 50 | +| y | 输出 | Tensor | FLOAT16 / FLOAT32 | ND | [index_num] | 从 x 中按 index 索引选取的元素 | | ||
| 51 | + | ||
| 52 | +## 约束说明 | ||
| 53 | + | ||
| 54 | +- 仅支持一维输入 x 与一维索引 index | ||
| 55 | +- 支持 FLOAT16 / FLOAT32 的 x,index 仅支持 INT64 | ||
| 56 | +- `embed_dim <= 20480`(每个核的 Unified Buffer 需要完整容纳 x) | ||
| 57 | +- 该算子需要在 NPU 设备上运行 | ||
| 58 | + | ||
| 59 | +> 通过 torch 接口调用时,扩展层还会额外校验:输入 tensor 必须已定义且非空、所有输入 tensor 必须位于同一 NPU 设备、x 与 index 必须为 1D、index 为 INT64。详见 [gather_for_rank1 接口文档](./docs/torch_gather_for_rank1.md)。 | ||
| 60 | + | ||
| 61 | +## 调用示例 | ||
| 62 | + | ||
| 63 | +- [aclnn 调用示例](./examples/aclnn/test_aclnn_gather_for_rank1.cpp) | ||
| 64 | +- [torch 调用示例](./examples/torch/test_torch_gather_for_rank1.py) | ||
| 65 | +- [aclnn 接口文档](./docs/aclnn_gather_for_rank1.md) | ||
| 66 | +- [torch 接口文档](./docs/torch_gather_for_rank1.md) | ||
| @@ -0,0 +1,83 @@ | |||
| 1 | +# aclnnGatherForRank1 | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +| 产品 | 是否支持 | | ||
| 6 | +| :--- | :------: | | ||
| 7 | +| Ascend 950PR&950DT系列产品 | √ | | ||
| 8 | +| Atlas A3系列产品 | √ | | ||
| 9 | +| Atlas A2系列产品 | √ | | ||
| 10 | + | ||
| 11 | +## 功能说明 | ||
| 12 | + | ||
| 13 | +对输入 x(一维张量)执行按索引取数,即 `y[i] = x[index[i]]`。 | ||
| 14 | + | ||
| 15 | +计算公式: | ||
| 16 | + | ||
| 17 | +```text | ||
| 18 | +y[i] = x[index[i]] | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +## 函数原型 | ||
| 22 | + | ||
| 23 | +两段式接口,必须先调用 `aclnnGatherForRank1GetWorkspaceSize` 接口获取 workspace 大小和执行器,再调用 `aclnnGatherForRank1` 接口执行计算。 | ||
| 24 | + | ||
| 25 | +```c | ||
| 26 | +aclnnStatus aclnnGatherForRank1GetWorkspaceSize( | ||
| 27 | + const aclTensor *x, | ||
| 28 | + const aclTensor *index, | ||
| 29 | + const aclTensor *y, | ||
| 30 | + uint64_t *workspaceSize, | ||
| 31 | + aclOpExecutor **executor); | ||
| 32 | + | ||
| 33 | +aclnnStatus aclnnGatherForRank1( | ||
| 34 | + void *workspace, | ||
| 35 | + uint64_t workspaceSize, | ||
| 36 | + aclOpExecutor *executor, | ||
| 37 | + aclrtStream stream); | ||
| 38 | +``` | ||
| 39 | + | ||
| 40 | +## aclnnGatherForRank1GetWorkspaceSize | ||
| 41 | + | ||
| 42 | +### 参数说明 | ||
| 43 | + | ||
| 44 | +| 参数名 | 输入/输出 | 描述 | 数据类型 | 数据格式 | | ||
| 45 | +|--------|---------|------|---------|---------| | ||
| 46 | +| x(aclTensor) | 输入 | 输入数据,shape [embed_dim] | FLOAT16 / FLOAT32 | ND | | ||
| 47 | +| index(aclTensor) | 输入 | 索引数据,shape [index_num] | INT64 | ND | | ||
| 48 | +| y(aclTensor) | 输出 | 输出数据,shape [index_num] | FLOAT16 / FLOAT32 | ND | | ||
| 49 | +| workspaceSize(uint64_t*) | 输出 | 返回需要在 Device 侧申请的 workspace 大小 | - | - | | ||
| 50 | +| executor(aclOpExecutor**) | 输出 | 返回算子执行器 | - | - | | ||
| 51 | + | ||
| 52 | +### 返回值 | ||
| 53 | + | ||
| 54 | +| 返回值 | 错误码 | 描述 | | ||
| 55 | +|--------|-------|------| | ||
| 56 | +| ACLNN_ERR_PARAM_NULLPTR | 161001 | 传入参数是必选输入/输出且为空指针 | | ||
| 57 | +| ACLNN_ERR_PARAM_INVALID | 161002 | 参数数据类型或数据格式不在支持范围内 | | ||
| 58 | + | ||
| 59 | +## aclnnGatherForRank1 | ||
| 60 | + | ||
| 61 | +### 参数说明 | ||
| 62 | + | ||
| 63 | +| 参数名 | 输入/输出 | 描述 | | ||
| 64 | +|--------|---------|------| | ||
| 65 | +| workspace | 输入 | 在 Device 侧申请的 workspace 内存地址 | | ||
| 66 | +| workspaceSize | 输入 | workspace 大小,由 `aclnnGatherForRank1GetWorkspaceSize` 获取 | | ||
| 67 | +| executor | 输入 | 算子执行器 | | ||
| 68 | +| stream | 输入 | 指定执行任务的 Stream | | ||
| 69 | + | ||
| 70 | +### 返回值 | ||
| 71 | + | ||
| 72 | +aclnnStatus:返回状态码,成功返回 `ACLNN_SUCCESS`。 | ||
| 73 | + | ||
| 74 | +## 约束说明 | ||
| 75 | + | ||
| 76 | +- x 与 index 必须为一维 | ||
| 77 | +- x 支持 FLOAT16 / FLOAT32;index 仅支持 INT64,且 index 内元素值需小于 `embed_dim` | ||
| 78 | +- `embed_dim <= 20480` | ||
| 79 | +- 该算子需要在 NPU 设备上运行 | ||
| 80 | + | ||
| 81 | +## 调用示例 | ||
| 82 | + | ||
| 83 | +具体参考 [test_aclnn_gather_for_rank1.cpp](../examples/aclnn/test_aclnn_gather_for_rank1.cpp) 的完整实现。 | ||
| @@ -0,0 +1,83 @@ | |||
| 1 | +# gather_for_rank1 | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +| 产品 | 是否支持 | | ||
| 6 | +| :--- | :------: | | ||
| 7 | +| Ascend 950PR&950DT系列产品 | √ | | ||
| 8 | +| Atlas A3系列产品 | √ | | ||
| 9 | +| Atlas A2系列产品 | √ | | ||
| 10 | + | ||
| 11 | +## 功能说明 | ||
| 12 | + | ||
| 13 | +对输入 x(一维张量)执行按索引取数,即 `y[i] = x[index[i]]`。前向算子注册为 `ops_rec::gather_for_rank1`,其自动求导通过内置 autograd 反向绑定调用 `ops_rec::index_select_for_rank1_backward`。 | ||
| 14 | + | ||
| 15 | +## 接口原型 | ||
| 16 | + | ||
| 17 | +```python | ||
| 18 | +ops_rec.ascendc.embedding.gather_for_rank1(x: Tensor, index: Tensor) -> Tensor | ||
| 19 | +``` | ||
| 20 | + | ||
| 21 | +## 参数说明 | ||
| 22 | + | ||
| 23 | +| 参数名 | 输入/输出 | 参数类型 | 数据类型 | 数据格式 | shape | 说明 | | ||
| 24 | +|--------|---------|---------|---------|---------|-------|------| | ||
| 25 | +| x | 输入 | Tensor | FLOAT16 / FLOAT32 | ND | [embed_dim] | 仅支持一维,`0 < embed_dim <= 20480` | | ||
| 26 | +| index | 输入 | Tensor | INT64 | ND | [index_num] | 仅支持一维;index 内元素值不得超过 x 的长度 | | ||
| 27 | +| y | 输出 | Tensor | FLOAT16 / FLOAT32 | ND | [index_num] | 从 x 中按 index 索引选取的元素 | | ||
| 28 | + | ||
| 29 | +## 约束说明 | ||
| 30 | + | ||
| 31 | +约束分为两层:算子自身语义适用于 aclnn 与 torch 两条调用路径;torch 扩展层额外加的入参校验仅在调用 `ops_rec.ascendc.embedding.gather_for_rank1` 时生效。 | ||
| 32 | + | ||
| 33 | +### 算子约束 | ||
| 34 | + | ||
| 35 | +- x 与 index 必须为一维 | ||
| 36 | +- x 支持 FLOAT16 / FLOAT32;index 仅支持 INT64 | ||
| 37 | +- `embed_dim <= 20480`,index 内元素值需小于 `embed_dim` | ||
| 38 | +- 该算子需要在 NPU 设备上运行 | ||
| 39 | + | ||
| 40 | +### torch 扩展层额外校验 | ||
| 41 | + | ||
| 42 | +由 [gather_for_rank1_plugin.cpp](../../torch_extension/gather_for_rank1_plugin.cpp) 在调用算子前完成: | ||
| 43 | + | ||
| 44 | +- 输入 tensor 必须已定义且非空 | ||
| 45 | +- 所有输入 tensor 必须位于同一 NPU 设备 | ||
| 46 | +- x 与 index 必须为 1D | ||
| 47 | +- index 必须为 INT64(当前迁移仅编译 int64 索引路径) | ||
| 48 | +- 需要安装 `torch_npu` | ||
| 49 | + | ||
| 50 | +## 反向算子 index_select_for_rank1_backward | ||
| 51 | + | ||
| 52 | +前向算子的梯度通过 autograd 自动路由到反向算子,也可直接调用: | ||
| 53 | + | ||
| 54 | +```python | ||
| 55 | +ops_rec.ascendc.embedding.index_select_for_rank1_backward(grad_y: Tensor, x: Tensor, index: Tensor) -> (Tensor, Tensor) | ||
| 56 | +``` | ||
| 57 | + | ||
| 58 | +- 返回 `(grad_x, grad_index)`,其中 `grad_index` 为未定义张量(index 不产生梯度) | ||
| 59 | +- `grad_x` 的 shape 与 x 相同,数据类型为 FLOAT32 | ||
| 60 | + | ||
| 61 | +## 安装方式 | ||
| 62 | + | ||
| 63 | +```bash | ||
| 64 | +bash build.sh --install -j16 | ||
| 65 | +``` | ||
| 66 | + | ||
| 67 | +## 调用示例 | ||
| 68 | + | ||
| 69 | +完整调用示例参考 [test_torch_gather_for_rank1.py](../examples/torch/test_torch_gather_for_rank1.py)。 | ||
| 70 | + | ||
| 71 | +```python | ||
| 72 | +import torch | ||
| 73 | +import ops_rec | ||
| 74 | + | ||
| 75 | +x = torch.randn(129, device="npu") | ||
| 76 | +index = torch.randint(0, 129, (10000,), dtype=torch.int64, device="npu") | ||
| 77 | +x.requires_grad_() | ||
| 78 | +y = ops_rec.ascendc.embedding.gather_for_rank1(x, index) | ||
| 79 | + | ||
| 80 | +loss = torch.mean(y) | ||
| 81 | +loss.backward() # 自动调用 index_select_for_rank1_backward | ||
| 82 | +print(x.grad) | ||
| 83 | +``` | ||
| @@ -0,0 +1,83 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved. | ||
| 3 | +# | ||
| 4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | +# you may not use this file except in compliance with the License. | ||
| 6 | +# You may obtain a copy of the License at | ||
| 7 | +# | ||
| 8 | +# http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | +# | ||
| 10 | +# Unless required by applicable law or agreed to in writing, software | ||
| 11 | +# distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | +# See the License for the specific language governing permissions and | ||
| 14 | +# limitations under the License. | ||
| 15 | +# ============================================================================== | ||
| 16 | +"""gather_for_rank1 前向算子的 autograd 注册(ops_rec/autograd 兼容)。 | ||
| 17 | + | ||
| 18 | +前向算子 ops_rec::gather_for_rank1 计算 y = x[index](1D 嵌入权重表按 index | ||
| 19 | +查表)。反向梯度由主仓独立算子 ops_rec::index_select_for_rank1_backward | ||
| 20 | +(scatter-add 语义累加 grad_x)计算,本模块用 torch.library.register_autograd | ||
| 21 | +建立前反向绑定,使 loss.backward() 自动路由。 | ||
| 22 | + | ||
| 23 | +本文件位于聚合根 torch_extension/ 下,由 build.sh sync_autograd() 同步进 | ||
| 24 | +ops_rec/autograd/,import ops_rec 时经 _load_plugins("autograd") 自动调用 | ||
| 25 | +register_autograd()。 | ||
| 26 | +""" | ||
| 27 | + | ||
| 28 | +import torch | ||
| 29 | + | ||
| 30 | +# 前向算子全名:ops_rec::gather_for_rank1 | ||
| 31 | +# 反向算子全名:ops_rec::index_select_for_rank1_backward(主仓独立算子) | ||
| 32 | +_OP_NAMESPACE = "ops_rec" | ||
| 33 | +_FORWARD_OP = f"{_OP_NAMESPACE}::gather_for_rank1" | ||
| 34 | +_BACKWARD_OP = f"{_OP_NAMESPACE}::index_select_for_rank1_backward" | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +def _setup_context(ctx, inputs, output): | ||
| 38 | + """前向后保存反向所需张量。 | ||
| 39 | + | ||
| 40 | + Args: | ||
| 41 | + ctx: autograd context | ||
| 42 | + inputs: 前向输入 (x, index) | ||
| 43 | + output: 前向输出 y(反向算子内部自行推导,无需保存) | ||
| 44 | + """ | ||
| 45 | + x, index = inputs | ||
| 46 | + # index 本身不可导,但反向算子需要 index 定位 grad_y 散射到 grad_x 的位置 | ||
| 47 | + ctx.save_for_backward(x, index) | ||
| 48 | + | ||
| 49 | + | ||
| 50 | +def _backward(ctx, grad_output): | ||
| 51 | + """反向传播。 | ||
| 52 | + | ||
| 53 | + Args: | ||
| 54 | + ctx: autograd context | ||
| 55 | + grad_output: loss 对 y 的梯度 | ||
| 56 | + | ||
| 57 | + Returns: | ||
| 58 | + 与前向输入 (x, index) 一一对应的梯度:(grad_x, None) | ||
| 59 | + """ | ||
| 60 | + x, index = ctx.saved_tensors | ||
| 61 | + # 主仓反向算子返回 (grad_x, grad_index),grad_index 恒为 undefined | ||
| 62 | + grad_x, _ = torch.ops.ops_rec.index_select_for_rank1_backward(grad_output, x, index) | ||
| 63 | + # index 不可导,其梯度为 None | ||
| 64 | + return grad_x, None | ||
| 65 | + | ||
| 66 | + | ||
| 67 | +def register_autograd(): | ||
| 68 | + """注册 gather_for_rank1 的 autograd 公式。 | ||
| 69 | + | ||
| 70 | + 在 ops_rec.autograd 插件包自动加载时被调用,也可手动调用。 | ||
| 71 | + 重复注册会抛异常,因此做一次幂等保护。 | ||
| 72 | + """ | ||
| 73 | + try: | ||
| 74 | + torch.library.register_autograd(_FORWARD_OP, _backward, setup_context=_setup_context) | ||
| 75 | + except RuntimeError as exc: | ||
| 76 | + if "already" in str(exc).lower() or "registered" in str(exc).lower(): | ||
| 77 | + return | ||
| 78 | + raise | ||
| 79 | + | ||
| 80 | + | ||
| 81 | +if __name__ == "__main__": | ||
| 82 | + register_autograd() | ||
| 83 | + | ||
| @@ -0,0 +1,85 @@ | |||
| 1 | +/* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved. | ||
| 2 | + | ||
| 3 | +Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +you may not use this file except in compliance with the License. | ||
| 5 | +You may obtain a copy of the License at | ||
| 6 | + | ||
| 7 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + | ||
| 9 | +Unless required by applicable law or agreed to in writing, software | ||
| 10 | +distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +See the License for the specific language governing permissions and | ||
| 13 | +limitations under the License. | ||
| 14 | +==============================================================================*/ | ||
| 15 | + | ||
| 16 | +// 声明本插件依赖的 vendor(aclnn 接口所在 op_api 库),必须在 include "pytorch_npu_helper.hpp" 之前定义 | ||
| 17 | +#define OPS_REC_CUST_VENDORS "gather_for_rank1_forward" | ||
| 18 | + | ||
| 19 | +#include <torch/library.h> | ||
| 20 | + | ||
| 21 | +#include "common_utils.h" | ||
| 22 | +#include "pytorch_npu_helper.hpp" | ||
| 23 | + | ||
| 24 | +using namespace at; | ||
| 25 | + | ||
| 26 | +namespace npu_gather_for_rank1 { | ||
| 27 | + | ||
| 28 | +constexpr int64_t RANK1_DIM = 1; | ||
| 29 | + | ||
| 30 | +at::Tensor gather_for_rank1_impl(const at::Tensor& x, const at::Tensor& index) | ||
| 31 | +{ | ||
| 32 | + check_tensor_non_empty(x, "x"); | ||
| 33 | + check_tensor_non_empty(index, "index"); | ||
| 34 | + | ||
| 35 | + // 检查NPU设备且设备ID一致 | ||
| 36 | + std::vector<at::Tensor> tensors = {x, index}; | ||
| 37 | + std::vector<std::string> names = {"x", "index"}; | ||
| 38 | + check_tensor_npu_device(tensors, names); | ||
| 39 | + | ||
| 40 | + TORCH_CHECK(x.dim() == RANK1_DIM, "The x should be 1D"); | ||
| 41 | + TORCH_CHECK(index.dim() == RANK1_DIM, "The index should be 1D"); | ||
| 42 | + // 当前迁移仅编译 int64 索引路径 | ||
| 43 | + TORCH_CHECK(index.scalar_type() == at::kLong, "The index should be int64"); | ||
| 44 | + | ||
| 45 | + auto x_conti = x.contiguous(); | ||
| 46 | + auto index_conti = index.contiguous(); | ||
| 47 | + | ||
| 48 | + at::Tensor y = at::zeros_like(index, x_conti.options()); | ||
| 49 | + EXEC_NPU_CMD(aclnnGatherForRank1, x_conti, index_conti, y); | ||
| 50 | + return y; | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +} // namespace npu_gather_for_rank1 | ||
| 54 | + | ||
| 55 | +// 注册 gather_for_rank1 的 schema。 | ||
| 56 | +// autograd 反向不在此注册:由 ops_rec.autograd 模块经 | ||
| 57 | +// torch.library.register_autograd 绑定到 ops_rec::index_select_for_rank1_backward。 | ||
| 58 | +TORCH_LIBRARY_FRAGMENT(ops_rec, m) | ||
| 59 | +{ | ||
| 60 | + m.def("gather_for_rank1(Tensor x, Tensor index) -> Tensor"); | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +// NPU 设备实现(PrivateUse1 = Ascend NPU 后端标识) | ||
| 64 | +TORCH_LIBRARY_IMPL(ops_rec, PrivateUse1, m) | ||
| 65 | +{ | ||
| 66 | + m.impl("gather_for_rank1", TORCH_FN(npu_gather_for_rank1::gather_for_rank1_impl)); | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +namespace { | ||
| 70 | + | ||
| 71 | +// Meta 实现:仅推导 shape/dtype,用于 torch.compile (inductor) 图模式的 shape 推断 | ||
| 72 | +at::Tensor gather_for_rank1_meta(const at::Tensor& x, const at::Tensor& index) | ||
| 73 | +{ | ||
| 74 | + TORCH_CHECK(x.dim() == npu_gather_for_rank1::RANK1_DIM, "The x should be 1D"); | ||
| 75 | + TORCH_CHECK(index.dim() == npu_gather_for_rank1::RANK1_DIM, "The index should be 1D"); | ||
| 76 | + return at::empty_like(index, x.options()); | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +} // anonymous namespace | ||
| 80 | + | ||
| 81 | +// Meta 实现,用于 torch.compile 的 shape 推断 | ||
| 82 | +TORCH_LIBRARY_IMPL(ops_rec, Meta, m) | ||
| 83 | +{ | ||
| 84 | + m.impl("gather_for_rank1", TORCH_FN(gather_for_rank1_meta)); | ||
| 85 | +} | ||
| @@ -65,6 +65,7 @@ ops-rec 是面向大规模推荐场景的高性能算子库,基于昇腾 CANN | |||
| 65 | 65 | ||
| 66 | | 算子 | 接口签名 | 功能简介 | 硬件支持 | | 66 | | 算子 | 接口签名 | 功能简介 | 硬件支持 | |
| 67 | | ---- | -------- | -------- | -------- | | 67 | | ---- | -------- | -------- | -------- | |
| 68 | +| [gather_for_rank1](../../ascendc/embedding/gather_for_rank1/gather_for_rank1_forward/docs/torch_gather_for_rank1.md) | `ops_rec.ascendc.embedding.gather_for_rank1(x, index)` | 对一维张量 x 按索引 index 取数 `y[i]=x[index[i]]`,反向自动调用 `index_select_for_rank1_backward` | Atlas A2系列产品, Atlas A3系列产品, Ascend 950PR&950DT系列产品 | | ||
| 68 | | [index_select_for_rank1_backward](../../ascendc/embedding/gather_for_rank1/index_select_for_rank1_backward/docs/torch_index_select_for_rank1_backward.md) | `ops_rec.ascendc.embedding.index_select_for_rank1_backward(grad_y, x, index)` | rank-1 嵌入查表 `y = x[index]` 的反向传播,按 scatter_add 语义累加权重表梯度,返回 `(grad_x, None)` | Atlas A2系列产品, Atlas A3系列产品, Ascend 950PR&950DT系列产品 | | 69 | | [index_select_for_rank1_backward](../../ascendc/embedding/gather_for_rank1/index_select_for_rank1_backward/docs/torch_index_select_for_rank1_backward.md) | `ops_rec.ascendc.embedding.index_select_for_rank1_backward(grad_y, x, index)` | rank-1 嵌入查表 `y = x[index]` 的反向传播,按 scatter_add 语义累加权重表梯度,返回 `(grad_x, None)` | Atlas A2系列产品, Atlas A3系列产品, Ascend 950PR&950DT系列产品 | |
| 69 | 70 | ||
| 70 | ### mixing(混合算子) | 71 | ### mixing(混合算子) |
| @@ -102,6 +103,7 @@ ascendc/ | |||
| 102 | │ └── fa_infer_with_arbitrary_func/ # Flash Attention 推理(任意区间掩码) | 103 | │ └── fa_infer_with_arbitrary_func/ # Flash Attention 推理(任意区间掩码) |
| 103 | ├── embedding/ # embedding 算子组(嵌入算子) | 104 | ├── embedding/ # embedding 算子组(嵌入算子) |
| 104 | │ └── gather_for_rank1/ # rank-1 嵌入查表(y = x[index]) | 105 | │ └── gather_for_rank1/ # rank-1 嵌入查表(y = x[index]) |
| 106 | +│ ├── gather_for_rank1_forward/ # 前向(y = x[index]) | ||
| 105 | │ └── index_select_for_rank1_backward/ # 反向(scatter_add 语义累加权重梯度) | 107 | │ └── index_select_for_rank1_backward/ # 反向(scatter_add 语义累加权重梯度) |
| 106 | ├── mixing/ # mixing 算子组(混合算子) | 108 | ├── mixing/ # mixing 算子组(混合算子) |
| 107 | │ └── token_mixing/ # Token 混合归一化 | 109 | │ └── token_mixing/ # Token 混合归一化 |
代码问题(按严重程度):
1)index 取值未做任何范围校验(P0) plugin 是唯一能拿到实际张量值的位置,但这里只校验了 dtype/shape,没有校验 index 的取值范围。kernel 里会把 int64 索引 Cast 成 int32 再乘 sizeof 当 uint32 偏移做 Gather:负值会回绕成巨大偏移,>= x.size(0) 会读到未拷贝区域,>= 2^31 还会被截断。建议在 EXEC_NPU_CMD 前加
TORCH_CHECK(index.min().item<int64_t>() >= 0 && index.max().item<int64_t>() < x.size(0))(或至少校验上界,与 op_host 的 MAX_DIM 约束对齐)。2)输出用 zeros_like 预清零(P2) kernel 对 Out 的 [0, indexLen) 区间是全量写,zeros_like 多了一次整块 GM 写;如果保留 zeros_like 是为了兼容 kernel 未覆盖的尾块,建议在 kernel 侧补齐写入,否则用
at::empty_like(index, x_conti.options())。