已合并
【社区任务】02-03 Tile算子开发任务贡献 #2417
刘十一创建于 4月24日
【社区任务】02-03 Tile算子开发任务贡献 #2417
已合并
共 25 个文件变更+5360-0
| @@ -0,0 +1,9 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +add_all_modules_sources(OPTYPE tile ACLNNTYPE aclnn_exclude DEPENDENCIES broadcast_to unsqueeze) | ||
| @@ -0,0 +1,81 @@ | |||
| 1 | +# Tile | ||
| 2 | + | ||
| 3 | +## 贡献说明 | ||
| 4 | + | ||
| 5 | +| 贡献者 | 贡献方 | 贡献算子 | 贡献时间 | 贡献内容 | | ||
| 6 | +|--------|--------|----------|----------|----------| | ||
| 7 | +| ElevenLiu | 智子芯元(深圳)科技有限责任公司 | Tile | 2026/04 | 新增Tile算子适配开源仓 | | ||
| 8 | + | ||
| 9 | +## 支持的产品型号 | ||
| 10 | + | ||
| 11 | +- Atlas A2系列产品 | ||
| 12 | + | ||
| 13 | +## 算子描述 | ||
| 14 | + | ||
| 15 | +- 功能描述 | ||
| 16 | + | ||
| 17 | + `Tile`算子将输入张量按指定倍数在各维度重复拼接,等价于 `numpy.tile(input, multiples)` 或 PyTorch `tensor.repeat(multiples)`。 | ||
| 18 | + | ||
| 19 | +- 计算公式 | ||
| 20 | + | ||
| 21 | + $output[i_0, i_1, ..., i_N] = input[i_0 \bmod s_0, i_1 \bmod s_1, ..., i_N \bmod s_N]$ | ||
| 22 | + | ||
| 23 | + 其中 $s_0, s_1, ..., s_N$ 为输入各维度大小。 | ||
| 24 | + | ||
| 25 | +- 原型信息 | ||
| 26 | + | ||
| 27 | + <table> | ||
| 28 | + <tr><th align="center">算子类型(OpType)</th><th colspan="4" align="center">Tile</th></tr> | ||
| 29 | + <tr><td align="center"> </td><td align="center">name</td><td align="center">Type</td><td align="center">data type</td><td align="center">format</td></tr> | ||
| 30 | + <tr><td rowspan="2" align="center">算子输入</td> | ||
| 31 | + <td align="center">x</td><td align="center">tensor</td><td align="center">float32, float16, bfloat16, int32, int16, int8, uint8, uint16, uint32, uint64, bool, complex64</td><td align="center">ND</td></tr> | ||
| 32 | + <tr><td align="center">multiples</td><td align="center">tensor</td><td align="center">int32, int64</td><td align="center">ND</td></tr> | ||
| 33 | + <tr><td rowspan="1" align="center">算子输出</td> | ||
| 34 | + <td align="center">y</td><td align="center">tensor</td><td align="center">与x一致</td><td align="center">ND</td></tr> | ||
| 35 | + <tr><td rowspan="1" align="center">核函数名</td><td colspan="4" align="center">tile</td></tr> | ||
| 36 | + </table> | ||
| 37 | + | ||
| 38 | +## 约束与限制 | ||
| 39 | + | ||
| 40 | +- 输入张量维度范围 1-8,仅支持 ND 格式 | ||
| 41 | +- int64、double 数据类型暂不支持 | ||
R | |||
| 42 | +- multiples 各维度值须为正整数 (≥1) | ||
| 43 | +- 输入/输出总元素数须在 int32 范围内 | ||
| 44 | + | ||
| 45 | +## 算子使用 | ||
| 46 | + | ||
| 47 | +使用该算子前,请参考[社区版CANN开发套件包安装文档](../../../docs/zh/invocation/quick_op_invocation.md)完成开发运行环境的部署。 | ||
| 48 | + | ||
| 49 | +### 编译部署 | ||
| 50 | + | ||
| 51 | + - 进入到仓库目录 | ||
| 52 | + | ||
| 53 | + ```bash | ||
| 54 | + cd ${git_clone_path}/ops-math | ||
| 55 | + ``` | ||
| 56 | + | ||
| 57 | + - 执行编译 | ||
| 58 | + | ||
| 59 | + ```bash | ||
| 60 | + bash build.sh --pkg --experimental --soc=ascend910b --ops=tile | ||
| 61 | + ``` | ||
| 62 | + | ||
| 63 | + - 部署算子包 | ||
| 64 | + | ||
| 65 | + ```bash | ||
| 66 | + ./build_out/cann-ops-<vendor_name>-linux.<arch>.run | ||
| 67 | + ``` | ||
| 68 | + | ||
| 69 | +## 调用说明 | ||
| 70 | + | ||
| 71 | +Tile 算子对应 aclnn 层接口为 `aclnnRepeat`(已内置于 CANN 包中),通过 `tensor.repeat(multiples)` 方式调用。 | ||
| 72 | + | ||
| 73 | +| 调用方式 | 调用样例 | 说明 | | ||
| 74 | +|----------|----------|------| | ||
| 75 | +| aclnn调用 | [test_aclnn_tile](./examples/test_aclnn_tile.cpp) | 通过[aclnnRepeat](./docs/aclnnRepeat.md)接口方式调用Tile算子 | | ||
| 76 | + | ||
| 77 | +### 执行调用 | ||
| 78 | + | ||
| 79 | +```bash | ||
| 80 | +bash build.sh --experimental --run_example tile eager cust --vendor_name=custom | ||
| 81 | +``` | ||
| @@ -0,0 +1,87 @@ | |||
| 1 | +# aclnnRepeat | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +| 产品 | 是否支持 | | ||
| 6 | +| :--- | :---: | | ||
| 7 | +| Atlas A2系列产品 | √ | | ||
| 8 | + | ||
| 9 | +## 功能描述 | ||
| 10 | + | ||
| 11 | +- 算子功能:将输入张量按指定倍数在各维度重复拼接,等价于 `numpy.tile(input, multiples)` 或 PyTorch `tensor.repeat(multiples)`。 | ||
| 12 | +- 计算公式: | ||
| 13 | + | ||
| 14 | + $$out[i_0, i_1, ..., i_N] = self[i_0 \bmod s_0, i_1 \bmod s_1, ..., i_N \bmod s_N]$$ | ||
| 15 | + | ||
| 16 | + 其中 $s_0, s_1, ..., s_N$ 为输入各维度大小。 | ||
| 17 | + | ||
| 18 | +## 实现原理 | ||
| 19 | + | ||
| 20 | +调用 Ascend C 的 DataCopy 接口实现纯数据搬运,通过 Host 侧维度合并优化和多核并行来提升性能。 | ||
| 21 | + | ||
| 22 | +## 函数原型 | ||
| 23 | + | ||
| 24 | +Tile 算子对应内置 aclnnRepeat 接口([两段式接口](../../../../docs/zh/context/两段式接口.md)): | ||
| 25 | + | ||
| 26 | +```cpp | ||
| 27 | +aclnnStatus aclnnRepeatGetWorkspaceSize( | ||
| 28 | + const aclTensor *self, | ||
| 29 | + const aclIntArray *repeats, | ||
| 30 | + aclTensor *out, | ||
| 31 | + uint64_t *workspaceSize, | ||
| 32 | + aclOpExecutor **executor) | ||
| 33 | +``` | ||
| 34 | + | ||
| 35 | +```cpp | ||
| 36 | +aclnnStatus aclnnRepeat( | ||
| 37 | + void *workspace, | ||
| 38 | + uint64_t workspaceSize, | ||
| 39 | + aclOpExecutor *executor, | ||
| 40 | + const aclrtStream stream) | ||
| 41 | +``` | ||
| 42 | + | ||
| 43 | +### aclnnRepeatGetWorkspaceSize | ||
| 44 | + | ||
| 45 | +| 参数名 | 输入/输出 | 描述 | 数据类型 | 是否必选 | | ||
| 46 | +|--------|----------|------|----------|----------| | ||
| 47 | +| self | 输入 | 输入张量 | aclTensor* | 是 | | ||
| 48 | +| repeats | 输入 | 各维度重复倍数 | aclIntArray* | 是 | | ||
| 49 | +| out | 输出 | 输出张量 | aclTensor* | 是 | | ||
| 50 | +| workspaceSize | 输出 | workspace 大小 | uint64_t* | 是 | | ||
| 51 | +| executor | 输出 | 算子执行器 | aclOpExecutor** | 是 | | ||
| 52 | + | ||
| 53 | +### aclnnRepeat | ||
| 54 | + | ||
| 55 | +| 参数名 | 输入/输出 | 描述 | 数据类型 | 是否必选 | | ||
| 56 | +|--------|----------|------|----------|----------| | ||
| 57 | +| workspace | 输入 | workspace 内存地址 | void* | 是 | | ||
| 58 | +| workspaceSize | 输入 | workspace 大小 | uint64_t | 是 | | ||
| 59 | +| executor | 输入 | 算子执行器 | aclOpExecutor* | 是 | | ||
| 60 | +| stream | 输入 | 计算流 | aclrtStream | 是 | | ||
| 61 | + | ||
| 62 | +## 支持的数据类型 | ||
| 63 | + | ||
| 64 | +| self 数据类型 | out 数据类型 | | ||
| 65 | +|-------------|------------| | ||
| 66 | +| FLOAT | FLOAT | | ||
| 67 | +| FLOAT16 | FLOAT16 | | ||
| 68 | +| BF16 | BF16 | | ||
| 69 | +| INT32 | INT32 | | ||
| 70 | +| INT16 | INT16 | | ||
| 71 | +| INT8 | INT8 | | ||
| 72 | +| UINT8 | UINT8 | | ||
| 73 | +| UINT16 | UINT16 | | ||
| 74 | +| UINT32 | UINT32 | | ||
| 75 | +| UINT64 | UINT64 | | ||
| 76 | +| BOOL | BOOL | | ||
| 77 | +| COMPLEX64 | COMPLEX64 | | ||
| 78 | + | ||
| 79 | +## 约束说明 | ||
| 80 | + | ||
| 81 | +- 输入张量维度范围 1-8,仅支持 ND 格式 | ||
| 82 | +- repeats 各维度值须为非负整数(≥0),出现 0 时输出对应维度为 0 | ||
| 83 | +- int64、double 数据类型暂不支持 | ||
| 84 | + | ||
| 85 | +## 调用示例 | ||
| 86 | + | ||
| 87 | +请参考 [test_aclnn_tile.cpp](../examples/test_aclnn_tile.cpp)。 | ||
| @@ -0,0 +1,128 @@ | |||
| 1 | +# Tile 算子设计文档 | ||
| 2 | + | ||
| 3 | +## 一、需求背景 | ||
| 4 | + | ||
| 5 | +### 1.1 需求来源 | ||
| 6 | + | ||
| 7 | +参考版本内置算子的 TBE 实现,在昇腾 NPU 上使用 Ascend C 编程语言实现相同功能的 Tile 算子,提交到算子开源仓。 | ||
| 8 | + | ||
| 9 | +### 1.2 TBE 算子源码参考 | ||
| 10 | + | ||
| 11 | +- kernel 实现:`/usr/local/Ascend/cann-8.5.0/opp/built-in/op_impl/ai_core/tbe/impl/ops_legacy/dynamic/tile.py` | ||
| 12 | +- 算子信息库:`/usr/local/Ascend/cann-8.5.0/opp/built-in/op_impl/ai_core/tbe/config/ascend910b/aic-ascend910b-ops-info-legacy.json`(Tile 条目) | ||
| 13 | +- aclnn 接口:`/usr/local/Ascend/cann-8.5.0/aarch64-linux/include/aclnnop/level2/aclnn_repeat.h`(aclnnRepeat) | ||
| 14 | + | ||
| 15 | +### 1.3 TBE 实现概述 | ||
| 16 | + | ||
| 17 | +TBE Tile 算子核心逻辑: | ||
| 18 | + | ||
| 19 | +1. 输入验证:检查 x 数据类型、multiples 类型和维度 | ||
| 20 | +2. Shape 适配:维度对齐,为每个维度拆分为 (repeat_count, original_size) 两层 | ||
| 21 | +3. 计算核心:通过 `tbe.broadcast` 完成广播运算 | ||
| 22 | +4. int8/uint8 类型先 cast 到 float16 再 broadcast 再 cast 回原类型 | ||
| 23 | + | ||
| 24 | +AscendC 实现与 TBE 语义完全一致,但采用直接数据搬运方式,无需类型转换。 | ||
| 25 | + | ||
| 26 | +## 二、算子实现设计 | ||
| 27 | + | ||
| 28 | +### 2.1 工程结构 | ||
| 29 | + | ||
| 30 | +``` | ||
| 31 | +tile/ | ||
| 32 | +├── op_host/ | ||
| 33 | +│ ├── tile_def.cpp # 算子信息库 (OP_ADD 注册) | ||
| 34 | +│ ├── tile_infershape.cpp # InferShape 实现 (IMPL_OP_INFERSHAPE) | ||
| 35 | +│ └── tile_tiling.cpp # Tiling 实现 (IMPL_OP_OPTILING) | ||
| 36 | +├── op_api/ | ||
| 37 | +│ ├── aclnn_repeat.cpp # aclnnRepeat 接口实现 | ||
| 38 | +│ ├── aclnn_repeat.h # aclnnRepeat 接口声明 | ||
| 39 | +│ ├── tile.cpp # l0op::Tile 实现 | ||
| 40 | +│ └── tile.h # l0op::Tile 声明 | ||
| 41 | +├── op_kernel/ | ||
| 42 | +│ ├── tile.cpp # Kernel 入口(模板函数 + REGISTER_TILING_DEFAULT) | ||
| 43 | +│ ├── tile.h # Kernel 实现(TileOpImpl 模板类) | ||
| 44 | +│ ├── tile_tiling_data.h # TilingData 结构体 | ||
| 45 | +│ └── tile_tiling_key.h # TilingKey 模板注册 | ||
| 46 | +├── examples/ | ||
| 47 | +│ └── test_aclnn_tile.cpp # aclnnRepeat 调用示例 | ||
| 48 | +├── tests/ | ||
| 49 | +│ └── ut/ | ||
| 50 | +│ ├── op_host/ # InferShape 单元测试 | ||
| 51 | +│ └── op_kernel/ # Kernel 测试数据生成/比对脚本 | ||
| 52 | +├── docs/ | ||
| 53 | +│ ├── design_doc.md # 算子设计文档 | ||
| 54 | +│ └── aclnnRepeat.md # aclnn 接口文档 | ||
| 55 | +├── CMakeLists.txt | ||
| 56 | +└── README.md | ||
| 57 | +``` | ||
| 58 | + | ||
| 59 | +### 2.2 Host 侧设计 | ||
| 60 | + | ||
| 61 | +#### 2.2.1 算子定义 (tile_def.cpp) | ||
| 62 | + | ||
| 63 | +通过 `OP_ADD(Tile)` 注册算子信息库: | ||
| 64 | +- 输入 x:支持 12 种数据类型 (float32/float16/bfloat16/int32/int16/int8/uint8/uint16/uint32/uint64/bool/complex64) | ||
| 65 | +- 输入 multiples:int32 类型 1-D tensor | ||
| 66 | +- 输出 y:与 x 类型一致 | ||
| 67 | + | ||
| 68 | +#### 2.2.2 Tiling 策略 (tile_tiling.cpp) | ||
| 69 | + | ||
| 70 | +通过 `IMPL_OP_OPTILING(Tile)` 注册,核心逻辑: | ||
| 71 | + | ||
| 72 | +1. 获取平台信息(核数、UB 大小) | ||
| 73 | +2. 读取输入 shape 和 multiples tensor 数据 | ||
| 74 | +3. 维度合并优化:合并 mult=1 的连续维度以扩大 innerDim | ||
| 75 | +4. 计算 TileTilingData 并通过 `GET_TPL_TILING_KEY` 设置 tilingKey | ||
| 76 | +5. 设置 workspace 和 blockDim | ||
| 77 | + | ||
| 78 | +#### 2.2.3 InferShape (tile_infershape.cpp) | ||
| 79 | + | ||
| 80 | +通过 `IMPL_OP_INFERSHAPE(Tile)` 注册,根据输入 shape 和 multiples 计算输出 shape:`output_shape[i] = input_shape[i] * multiples[i]`。当输入维度与 multiples 长度不一致时,自动左侧补 1 对齐到较大维度数。 | ||
| 81 | + | ||
| 82 | +### 2.3 Device 侧设计 | ||
| 83 | + | ||
| 84 | +#### 2.3.1 Kernel 入口 (tile.cpp) | ||
| 85 | + | ||
| 86 | +使用单一模板函数 `tile<schMode>()`,通过 `DTYPE_X` 推导数据类型,通过 `REGISTER_TILING_DEFAULT` + `GET_TILING_DATA_WITH_STRUCT` 获取 tiling 数据。 | ||
| 87 | + | ||
| 88 | +#### 2.3.2 Kernel 实现 (tile.h) | ||
| 89 | + | ||
| 90 | +模板类 `TileOpImpl<T>` 支持所有 12 种数据类型。根据运行时数据特征动态选择 5 条优化路径: | ||
| 91 | + | ||
| 92 | +| 条件 | 路径 | 说明 | | ||
| 93 | +|------|------|------| | ||
| 94 | +| innerDim 超过 UB 容量 | ProcessLargeInner | 分块搬运大 innerDim | | ||
| 95 | +| outerCount < blockDim 且 innerMult > 1 | ProcessSplitMult | 按 mult 维度细粒度分核 | | ||
| 96 | +| innerDim 对齐且 innerMult ≥ 4 | ProcessDoubling | UB 内倍增写出 | | ||
| 97 | +| outputInnerDim 可放入 UB | ProcessBuild | UB 内构建完整输出行 | | ||
| 98 | +| 以上均不满足 | ProcessPerRow | 逐行处理(通用兜底) | | ||
| 99 | + | ||
| 100 | +核心优化技术: | ||
| 101 | +- **UB 内倍增**:小块翻倍复制后大块写出,减少 GM 写次数 | ||
| 102 | +- **多核分 mult**:outerCount 不足时按 innerMult 维度切分工作 | ||
| 103 | +- **维度合并**:Host 端合并 mult=1 维度扩大 innerDim | ||
| 104 | + | ||
| 105 | +### 2.4 aclnn 接口 | ||
| 106 | + | ||
| 107 | +Tile 算子对应已有内置 aclnn 接口 `aclnnRepeat`,CMakeLists 配置 `ACLNNTYPE aclnn_exclude` 表示不生成新的 aclnn 接口,复用已有接口。 | ||
| 108 | + | ||
| 109 | +## 三、精度与性能 | ||
| 110 | + | ||
| 111 | +### 3.1 精度 | ||
| 112 | + | ||
| 113 | +Tile 为纯数据搬运算子,精度误差恒为 0 12 种数据类型 × 13 种 shape = 156 组,全部通过。 | ||
| 114 | + | ||
| 115 | +### 3.2 性能 | ||
| 116 | + | ||
| 117 | +测试环境:Atlas A2 系列, blockDim=24, warmup=50, repeat=200 | ||
| 118 | + | ||
| 119 | +| 数据类型 | (1024²)×(2,2) | (256²×4)×(2³) | (1×1024)×(1024,1) | (128)×(8192) | | ||
| 120 | +|----------|-----------|----------|------------|----------| | ||
| 121 | +| float32 | 111% | 102% | 241% | 238% | | ||
| 122 | +| float16 | 161% | 104% | 241% | 238% | | ||
| 123 | +| int32 | 115% | 103% | 242% | 236% | | ||
| 124 | +| int16 | 165% | 106% | 235% | 234% | | ||
| 125 | +| int8 | 196% | 423% | 233% | 226% | | ||
| 126 | +| uint8 | 184% | 412% | 227% | 232% | | ||
| 127 | + | ||
| 128 | +24 组全部 ≥ 95%,最低 102%,最高 423%。 | ||
| @@ -0,0 +1,139 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + do { \ | ||
| 18 | + if (!(cond)) { \ | ||
| 19 | + return_expr; \ | ||
| 20 | + } \ | ||
| 21 | + } while (0) | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + do { \ | ||
| 25 | + printf(message, ##__VA_ARGS__); \ | ||
| 26 | + } while (0) | ||
| 27 | + | ||
| 28 | +int64_t GetShapeSize(const std::vector<int64_t>& shape) | ||
| 29 | +{ | ||
| 30 | + int64_t shapeSize = 1; | ||
| 31 | + for (auto i : shape) { | ||
| 32 | + shapeSize *= i; | ||
| 33 | + } | ||
| 34 | + return shapeSize; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +int Init(int32_t deviceId, aclrtStream* stream) | ||
| 38 | +{ | ||
| 39 | + auto ret = aclInit(nullptr); | ||
| 40 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); | ||
| 41 | + ret = aclrtSetDevice(deviceId); | ||
| 42 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); | ||
| 43 | + ret = aclrtCreateStream(stream); | ||
| 44 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); | ||
| 45 | + return 0; | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +template <typename T> | ||
| 49 | +int CreateAclTensor( | ||
| 50 | + const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType, | ||
| 51 | + aclTensor** tensor) | ||
| 52 | +{ | ||
| 53 | + auto size = GetShapeSize(shape) * sizeof(T); | ||
| 54 | + auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 55 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); | ||
| 56 | + ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 57 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); | ||
| 58 | + | ||
| 59 | + std::vector<int64_t> strides(shape.size(), 1); | ||
| 60 | + for (int64_t i = shape.size() - 2; i >= 0; i--) { | ||
| 61 | + strides[i] = shape[i + 1] * strides[i + 1]; | ||
| 62 | + } | ||
| 63 | + *tensor = aclCreateTensor( | ||
| 64 | + shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), | ||
| 65 | + *deviceAddr); | ||
| 66 | + return 0; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +int main() | ||
| 70 | +{ | ||
| 71 | + int32_t deviceId = 0; | ||
| 72 | + aclrtStream stream; | ||
| 73 | + auto ret = Init(deviceId, &stream); | ||
| 74 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); | ||
| 75 | + | ||
| 76 | + // input: shape [2, 3], repeats [3, 2] -> output shape [6, 6] | ||
| 77 | + std::vector<int64_t> selfShape = {2, 3}; | ||
| 78 | + std::vector<int64_t> outShape = {6, 6}; | ||
| 79 | + std::vector<float> selfHostData = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; | ||
| 80 | + std::vector<float> outHostData(GetShapeSize(outShape), 0.0f); | ||
| 81 | + std::vector<int64_t> repeatsData = {3, 2}; | ||
| 82 | + | ||
| 83 | + void* selfDeviceAddr = nullptr; | ||
| 84 | + void* outDeviceAddr = nullptr; | ||
| 85 | + aclTensor* self = nullptr; | ||
| 86 | + aclTensor* out = nullptr; | ||
| 87 | + | ||
| 88 | + ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self); | ||
| 89 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 90 | + ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out); | ||
| 91 | + CHECK_RET(ret == ACL_SUCCESS, return ret); | ||
| 92 | + | ||
| 93 | + aclIntArray* repeats = aclCreateIntArray(repeatsData.data(), repeatsData.size()); | ||
| 94 | + | ||
| 95 | + uint64_t workspaceSize = 0; | ||
| 96 | + aclOpExecutor* executor; | ||
| 97 | + ret = aclnnRepeatGetWorkspaceSize(self, repeats, out, &workspaceSize, &executor); | ||
| 98 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRepeatGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); | ||
| 99 | + | ||
| 100 | + void* workspaceAddr = nullptr; | ||
| 101 | + if (workspaceSize > 0) { | ||
| 102 | + ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); | ||
| 103 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + ret = aclnnRepeat(workspaceAddr, workspaceSize, executor, stream); | ||
| 107 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnRepeat failed. ERROR: %d\n", ret); return ret); | ||
| 108 | + | ||
| 109 | + ret = aclrtSynchronizeStream(stream); | ||
| 110 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); | ||
| 111 | + | ||
| 112 | + auto size = GetShapeSize(outShape); | ||
| 113 | + std::vector<float> resultData(size, 0); | ||
| 114 | + ret = aclrtMemcpy( | ||
| 115 | + resultData.data(), resultData.size() * sizeof(float), outDeviceAddr, size * sizeof(float), | ||
| 116 | + ACL_MEMCPY_DEVICE_TO_HOST); | ||
| 117 | + CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result failed. ERROR: %d\n", ret); return ret); | ||
| 118 | + | ||
| 119 | + LOG_PRINT("aclnnRepeat (Tile) result:\n"); | ||
| 120 | + for (int64_t i = 0; i < outShape[0]; i++) { | ||
| 121 | + for (int64_t j = 0; j < outShape[1]; j++) { | ||
| 122 | + LOG_PRINT("%6.1f ", resultData[i * outShape[1] + j]); | ||
| 123 | + } | ||
| 124 | + LOG_PRINT("\n"); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + aclDestroyTensor(self); | ||
| 128 | + aclDestroyTensor(out); | ||
| 129 | + aclDestroyIntArray(repeats); | ||
| 130 | + aclrtFree(selfDeviceAddr); | ||
| 131 | + aclrtFree(outDeviceAddr); | ||
| 132 | + if (workspaceSize > 0) { | ||
| 133 | + aclrtFree(workspaceAddr); | ||
| 134 | + } | ||
| 135 | + aclrtDestroyStream(stream); | ||
| 136 | + aclrtResetDevice(deviceId); | ||
| 137 | + aclFinalize(); | ||
| 138 | + return 0; | ||
| 139 | +} | ||
| @@ -0,0 +1,249 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | +using namespace op; | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +extern "C" { | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +static const std::initializer_list<op::DataType> DTYPE_SUPPORT_LIST = { | ||
| 42 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_COMPLEX64, op::DataType::DT_UINT8, | ||
| 43 | + op::DataType::DT_INT8, op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_BOOL, | ||
| 44 | + op::DataType::DT_BF16, op::DataType::DT_UINT32, op::DataType::DT_UINT64}; | ||
| 45 | + | ||
| 46 | +static const std::initializer_list<op::DataType> DTYPE_SUPPORT_910B310P_LIST = { | ||
| 47 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_COMPLEX64, op::DataType::DT_UINT8, | ||
| 48 | + op::DataType::DT_INT8, op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_BOOL, | ||
| 49 | + op::DataType::DT_BF16, op::DataType::DT_UINT32, op::DataType::DT_UINT64, op::DataType::DT_UINT16}; | ||
| 50 | + | ||
| 51 | +static const std::initializer_list<op::DataType> DTYPE_SUPPORT_950_LIST = { | ||
| 52 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_COMPLEX64, op::DataType::DT_UINT8, | ||
| 53 | + op::DataType::DT_INT8, op::DataType::DT_INT16, op::DataType::DT_INT32, op::DataType::DT_BOOL, | ||
| 54 | + op::DataType::DT_BF16, op::DataType::DT_UINT32, op::DataType::DT_UINT64, op::DataType::DT_UINT16, | ||
| 55 | + op::DataType::DT_HIFLOAT8, op::DataType::DT_FLOAT8_E5M2, op::DataType::DT_FLOAT8_E4M3FN}; | ||
| 56 | + | ||
| 57 | +// 检查tensor是否为nullptr | ||
| 58 | +static inline bool CheckNotNull(const aclTensor* self, const aclIntArray* repeats, const aclTensor* out) | ||
| 59 | +{ | ||
| 60 | + OP_CHECK_NULL(self, return false); | ||
| 61 | + OP_CHECK_NULL(repeats, return false); | ||
| 62 | + OP_CHECK_NULL(out, return false); | ||
| 63 | + return true; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +static bool CheckDtypeValid(const aclTensor* self, const aclTensor* out) | ||
| 67 | +{ | ||
| 68 | + if (GetCurrentPlatformInfo().GetSocVersion() == SocVersion::ASCEND910B || | ||
| 69 | + GetCurrentPlatformInfo().GetSocVersion() == SocVersion::ASCEND910_93 || | ||
| 70 | + GetCurrentPlatformInfo().GetSocVersion() == SocVersion::ASCEND310P) { | ||
| 71 | + OP_CHECK_DTYPE_NOT_SUPPORT(self, DTYPE_SUPPORT_910B310P_LIST, return false); | ||
| 72 | + } else if (IsRegBase()) { | ||
| 73 | + OP_CHECK_DTYPE_NOT_SUPPORT(self, DTYPE_SUPPORT_950_LIST, return false); | ||
| 74 | + } else { | ||
| 75 | + OP_CHECK_DTYPE_NOT_SUPPORT(self, DTYPE_SUPPORT_LIST, return false); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + OP_CHECK_DTYPE_NOT_SAME(self, out, return false); | ||
| 79 | + return true; | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +// tensor维度数不能超过8维 | ||
| 83 | +static inline bool CheckTensorDimSize(const aclTensor* self, const aclIntArray* repeats) | ||
| 84 | +{ | ||
| 85 | + int64_t tensorDimSize = self->GetViewShape().GetDimNum(); | ||
| 86 | + int64_t repeatsSize = repeats->Size(); | ||
| 87 | + OP_CHECK_MAX_DIM(self, MAX_SUPPORT_DIMS_NUMS, return false); | ||
| 88 | + if (repeatsSize > static_cast<int64_t>(MAX_SUPPORT_DIMS_NUMS)) { | ||
| 89 | + OP_LOGE( | ||
| 90 | + ACLNN_ERR_PARAM_INVALID, "repeats size should not be larger than 8. self %ld, repeats %ld", tensorDimSize, | ||
| 91 | + repeatsSize); | ||
| 92 | + return false; | ||
| 93 | + } | ||
| 94 | + return true; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +static inline bool CheckRepeatsSize(const aclTensor* self, const aclIntArray* repeats) | ||
| 98 | +{ | ||
| 99 | + if (self->GetViewShape().GetDimNum() > repeats->Size()) { | ||
| 100 | + OP_LOGE( | ||
| 101 | + ACLNN_ERR_PARAM_INVALID, | ||
| 102 | + "Number of dimensions of repeat dims can not be smaller than number of dimensions of input tensor, self " | ||
| 103 | + "%lu, repeats %lu.", | ||
| 104 | + self->GetViewShape().GetDimNum(), repeats->Size()); | ||
| 105 | + return false; | ||
| 106 | + } | ||
| 107 | + return true; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +// 校验repeat中的值均大于等于0 | ||
| 111 | +static inline aclnnStatus CheckRepeatsValue(const aclIntArray* repeats) | ||
| 112 | +{ | ||
| 113 | + for (size_t i = 0; i < repeats->Size(); ++i) { | ||
| 114 | + if ((*repeats)[i] < 0) { | ||
| 115 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, "repeats expected %luth value >= 0, but get %ld", i + 1, (*repeats)[i]); | ||
| 116 | + return false; | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + return true; | ||
| 120 | +} | ||
| 121 | + | ||
| 122 | +// 校验repeat中的值存在0 | ||
| 123 | +static inline aclnnStatus CheckRepeatsZero(const aclIntArray* repeats) | ||
| 124 | +{ | ||
| 125 | + for (size_t i = 0; i < repeats->Size(); ++i) { | ||
| 126 | + if ((*repeats)[i] == 0) { | ||
| 127 | + return true; | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + return false; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +// 校验repeat算子计算后生成的tensor与out的shape一致 | ||
| 134 | +static inline bool CheckRepeatOutShape(const aclTensor* repeatRes, const aclTensor* out) | ||
| 135 | +{ | ||
| 136 | + OP_CHECK_SHAPE_NOT_EQUAL(repeatRes, out, return false); | ||
| 137 | + | ||
| 138 | + return true; | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +// 返回为[1]的intarray shape | ||
| 142 | +static inline aclIntArray* GetBaseShape(aclOpExecutor* executor) | ||
| 143 | +{ | ||
| 144 | + int64_t tensorShape[1] = {}; | ||
| 145 | + tensorShape[0] = 1; | ||
| 146 | + auto res = executor->AllocIntArray(tensorShape, 1); | ||
| 147 | + return res; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +// 如果self为0维tensor,那么转换为1维tensor。其余情况转成连续tensor | ||
| 151 | +static inline const aclTensor* InitializeTensor(const aclTensor* x, aclOpExecutor* executor) | ||
| 152 | +{ | ||
| 153 | + auto xContiguous = l0op::Contiguous(x, executor); | ||
| 154 | + // 如果tensor为0维,则转换为1维tensor | ||
| 155 | + if (xContiguous->GetViewShape().GetDimNum() == 0) { | ||
| 156 | + auto baseShape = GetBaseShape(executor); | ||
| 157 | + xContiguous = l0op::BroadcastTo(xContiguous, baseShape, executor); | ||
| 158 | + } | ||
| 159 | + return xContiguous; | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +static const aclTensor* ViewToRepeatsSize(const aclTensor* self, const aclIntArray* repeats, aclOpExecutor* executor) | ||
| 163 | +{ | ||
| 164 | + auto Dims = (int64_t)repeats->Size() - (int64_t)self->GetViewShape().GetDimNum(); | ||
| 165 | + std::vector<int64_t> unsqueezeDim(Dims); | ||
| 166 | + for (int64_t idx = 0; idx < Dims; idx++) { | ||
| 167 | + unsqueezeDim[idx] = idx; | ||
| 168 | + } | ||
| 169 | + aclIntArray* dim = executor->AllocIntArray(unsqueezeDim.data(), Dims); | ||
| 170 | + auto unsqeezeSelf = l0op::UnsqueezeNd(self, dim, executor); | ||
| 171 | + return unsqeezeSelf; | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +// 校验开始 | ||
| 175 | +static aclnnStatus CheckParams(const aclTensor* self, const aclIntArray* repeats, const aclTensor* out) | ||
| 176 | +{ | ||
| 177 | + CHECK_RET(CheckNotNull(self, repeats, out), ACLNN_ERR_PARAM_NULLPTR); | ||
| 178 | + | ||
| 179 | + CHECK_RET(CheckDtypeValid(self, out), ACLNN_ERR_PARAM_INVALID); | ||
| 180 | + | ||
| 181 | + CHECK_RET(CheckRepeatsSize(self, repeats), ACLNN_ERR_PARAM_INVALID); | ||
| 182 | + | ||
| 183 | + CHECK_RET(CheckRepeatsValue(repeats), ACLNN_ERR_PARAM_INVALID); | ||
| 184 | + | ||
| 185 | + CHECK_RET(CheckTensorDimSize(self, repeats), ACLNN_ERR_PARAM_INVALID); | ||
| 186 | + | ||
| 187 | + return ACLNN_SUCCESS; | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +aclnnStatus aclnnRepeatGetWorkspaceSize( | ||
| 191 | + const aclTensor* self, const aclIntArray* repeats, aclTensor* out, uint64_t* workspaceSize, | ||
| 192 | + aclOpExecutor** executor) | ||
| 193 | +{ | ||
| 194 | + L2_DFX_PHASE_1(aclnnRepeat, DFX_IN(self, repeats), DFX_OUT(out)); | ||
| 195 | + | ||
| 196 | + auto uniqueExecutor = CREATE_EXECUTOR(); | ||
| 197 | + CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR); | ||
| 198 | + | ||
| 199 | + auto ret = CheckParams(self, repeats, out); | ||
| 200 | + CHECK_RET(ret == ACLNN_SUCCESS, ret); | ||
| 201 | + | ||
| 202 | + if (self->IsEmpty() || out->IsEmpty() || CheckRepeatsZero(repeats)) { | ||
| 203 | + *workspaceSize = 0; | ||
| 204 | + uniqueExecutor.ReleaseTo(executor); | ||
| 205 | + return ACLNN_SUCCESS; | ||
| 206 | + } | ||
| 207 | + if (self->GetStorageFormat() != Format::FORMAT_ND) { | ||
| 208 | + OP_LOGW("Format only support ND"); | ||
| 209 | + } | ||
| 210 | + if (repeats->Size() == 0) { | ||
| 211 | + auto viewCopyOut = l0op::ViewCopy(self, out, uniqueExecutor.get()); | ||
| 212 | + CHECK_RET(viewCopyOut != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 213 | + | ||
| 214 | + *workspaceSize = uniqueExecutor->GetWorkspaceSize(); | ||
| 215 | + uniqueExecutor.ReleaseTo(executor); | ||
| 216 | + return ACLNN_SUCCESS; | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + auto selfContiguous = InitializeTensor(self, uniqueExecutor.get()); | ||
| 220 | + CHECK_RET(selfContiguous != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 221 | + auto selfUnsqueeze = selfContiguous; | ||
| 222 | + | ||
| 223 | + if (selfContiguous->GetViewShape().GetDimNum() < repeats->Size()) { | ||
| 224 | + selfUnsqueeze = ViewToRepeatsSize(selfContiguous, repeats, uniqueExecutor.get()); | ||
| 225 | + CHECK_RET(selfUnsqueeze != nullptr, ACLNN_ERR_PARAM_INVALID); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + auto repeatOut = l0op::Tile(selfUnsqueeze, repeats, uniqueExecutor.get()); | ||
| 229 | + CHECK_RET(repeatOut != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 230 | + CHECK_RET(CheckRepeatOutShape(repeatOut, out), ACLNN_ERR_PARAM_INVALID); | ||
| 231 | + | ||
| 232 | + auto viewCopyOut = l0op::ViewCopy(repeatOut, out, uniqueExecutor.get()); | ||
| 233 | + CHECK_RET(viewCopyOut != nullptr, ACLNN_ERR_INNER_NULLPTR); | ||
| 234 | + | ||
| 235 | + *workspaceSize = uniqueExecutor->GetWorkspaceSize(); | ||
| 236 | + uniqueExecutor.ReleaseTo(executor); | ||
| 237 | + return ACLNN_SUCCESS; | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | +aclnnStatus aclnnRepeat(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream) | ||
| 241 | +{ | ||
| 242 | + L2_DFX_PHASE_2(aclnnRepeat); | ||
| 243 | + | ||
| 244 | + return CommonOpExecutorRun(workspace, workspaceSize, executor, stream); | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | + | ||
| 248 | +} | ||
| 249 | + | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +extern "C" { | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * @brief aclnnRepeat的第一段接口,根据具体的计算流程,计算workspace大小。 | ||
| 22 | + * @domain aclnn_ops_infer | ||
| 23 | + */ | ||
| 24 | +ACLNN_API aclnnStatus aclnnRepeatGetWorkspaceSize( | ||
| 25 | + const aclTensor* self, const aclIntArray* repeats, aclTensor* out, uint64_t* workspaceSize, | ||
| 26 | + aclOpExecutor** executor); | ||
| 27 | + | ||
| 28 | +/** | ||
| 29 | + * @brief aclnnRepeat的第二段接口,用于执行计算。 | ||
| 30 | + */ | ||
| 31 | +ACLNN_API aclnnStatus aclnnRepeat(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream); | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile.cpp | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +using namespace op; | ||
| 28 | +namespace l0op { | ||
| 29 | +OP_TYPE_REGISTER(Tile); | ||
| 30 | + | ||
| 31 | +static inline const std::initializer_list<op::DataType> AICORE_DTYPE_SUPPORT_LIST = { | ||
| 32 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_INT32, | ||
| 33 | + op::DataType::DT_INT64, op::DataType::DT_BOOL, op::DataType::DT_BF16}; | ||
| 34 | + | ||
| 35 | +static inline const std::initializer_list<op::DataType> AICORE_DTYPE_SUPPORT_310P_LIST = { | ||
| 36 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_INT32, op::DataType::DT_INT64, | ||
| 37 | + op::DataType::DT_BOOL, op::DataType::DT_BF16, op::DataType::DT_INT8, op::DataType::DT_UINT8, | ||
| 38 | + op::DataType::DT_INT16, op::DataType::DT_UINT16, op::DataType::DT_UINT32, op::DataType::DT_UINT64}; | ||
| 39 | + | ||
| 40 | +static inline const std::initializer_list<op::DataType> AICORE_DTYPE_SUPPORT_910B_LIST = { | ||
| 41 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_INT32, op::DataType::DT_INT64, | ||
| 42 | + op::DataType::DT_BOOL, op::DataType::DT_BF16, op::DataType::DT_INT8, op::DataType::DT_UINT8, | ||
| 43 | + op::DataType::DT_INT16, op::DataType::DT_UINT16, op::DataType::DT_UINT32, op::DataType::DT_UINT64, | ||
| 44 | + op::DataType::DT_COMPLEX64}; | ||
| 45 | + | ||
| 46 | +static inline const std::initializer_list<op::DataType> ASCEND610LITE_DTYPE_SUPPORT_LIST = { | ||
| 47 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_INT32, op::DataType::DT_BOOL}; | ||
| 48 | + | ||
| 49 | +static const std::initializer_list<DataType> ASCEND950_AICORE_DTYPE_SUPPORT_LIST = { | ||
| 50 | + op::DataType::DT_FLOAT, op::DataType::DT_FLOAT16, op::DataType::DT_INT32, | ||
| 51 | + op::DataType::DT_INT64, op::DataType::DT_BOOL, op::DataType::DT_BF16, | ||
| 52 | + op::DataType::DT_INT8, op::DataType::DT_UINT8, op::DataType::DT_INT16, | ||
| 53 | + op::DataType::DT_UINT16, op::DataType::DT_UINT32, op::DataType::DT_UINT64, | ||
| 54 | + op::DataType::DT_COMPLEX64, op::DataType::DT_HIFLOAT8, op::DataType::DT_FLOAT8_E5M2, | ||
| 55 | + op::DataType::DT_FLOAT8_E4M3FN}; | ||
| 56 | + | ||
| 57 | +static inline bool IsAiCoreSupport(const aclTensor* self) | ||
| 58 | +{ | ||
| 59 | + if (GetCurrentPlatformInfo().GetSocVersion() == SocVersion::ASCEND910B) { | ||
| 60 | + return CheckType(self->GetDataType(), AICORE_DTYPE_SUPPORT_910B_LIST); | ||
| 61 | + } | ||
| 62 | + return CheckType(self->GetDataType(), AICORE_DTYPE_SUPPORT_LIST); | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +static inline const aclTensor* TileAiCore( | ||
| 66 | + const aclTensor* self, const aclTensor* repeats, aclTensor* out, aclOpExecutor* executor) | ||
| 67 | +{ | ||
| 68 | + L0_DFX(TileAiCore, self, repeats, out); | ||
| 69 | + ADD_TO_LAUNCHER_LIST_AICORE(Tile, OP_INPUT(self, repeats), OP_OUTPUT(out)); | ||
| 70 | + return out; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +static inline const aclTensor* TileAiCpu( | ||
| 74 | + const aclTensor* self, const aclTensor* repeats, aclTensor* out, aclOpExecutor* executor) | ||
| 75 | +{ | ||
| 76 | + L0_DFX(TileAiCpu, self, repeats, out); | ||
| 77 | + static internal::AicpuTaskSpace space("Tile", ge::DEPEND_IN_SHAPE, true); | ||
| 78 | + op::DataType dtype = repeats->GetDataType(); | ||
| 79 | + auto ret = ADD_TO_LAUNCHER_LIST_AICPU( | ||
| 80 | + Tile, OP_ATTR_NAMES({"Tmultiples"}), OP_INPUT(self, repeats), OP_OUTPUT(out), OP_ATTR(dtype)); | ||
| 81 | + CHECK_RET(ret == ACLNN_SUCCESS, nullptr); | ||
| 82 | + return out; | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +const aclTensor* Tile(const aclTensor* self, const aclIntArray* repeats, aclOpExecutor* executor) | ||
| 86 | +{ | ||
| 87 | + int64_t expandDim = static_cast<int64_t>(repeats->Size() - self->GetViewShape().GetDimNum()); | ||
| 88 | + op::Shape broadcastShape; | ||
| 89 | + if (expandDim < 0) { | ||
| 90 | + OP_LOGE(ACLNN_ERR_PARAM_INVALID, "repeats size should not be smaller than input tensor dim"); | ||
| 91 | + return nullptr; | ||
| 92 | + } else { | ||
| 93 | + for (int64_t i = 0; i < expandDim; i++) { | ||
| 94 | + broadcastShape.AppendDim((*repeats)[i]); | ||
| 95 | + } | ||
| 96 | + for (size_t i = 0; i < self->GetViewShape().GetDimNum(); i++) { | ||
| 97 | + broadcastShape.AppendDim(self->GetViewShape()[i] * (*repeats)[i + expandDim]); | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + auto out = executor->AllocTensor(broadcastShape, self->GetDataType()); | ||
| 101 | + auto repeatTensor = executor->ConvertToTensor(repeats, op::DataType::DT_INT64); | ||
| 102 | + | ||
| 103 | + if (IsAiCoreSupport(self)) { | ||
| 104 | + return TileAiCore(self, repeatTensor, out, executor); | ||
| 105 | + } else { | ||
| 106 | + return TileAiCpu(self, repeatTensor, out, executor); | ||
| 107 | + } | ||
| 108 | +} | ||
| 109 | +} // namespace l0op | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace l0op { | ||
| 16 | +const aclTensor* Tile(const aclTensor* self, const aclIntArray* repeats, aclOpExecutor* executor); | ||
| 17 | +} // namespace l0op | ||
| 18 | + | ||
| 19 | + | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile_def.cpp | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace ops { | ||
| 21 | + | ||
| 22 | +static const std::vector<ge::DataType> kXDataTypes = { | ||
| 23 | + ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, | ||
| 24 | + | ||
| 25 | + ge::DT_INT32, ge::DT_INT32, ge::DT_INT16, ge::DT_INT16, ge::DT_INT8, ge::DT_INT8, | ||
| 26 | + | ||
| 27 | + ge::DT_UINT8, ge::DT_UINT8, ge::DT_UINT16, ge::DT_UINT16, ge::DT_UINT32, ge::DT_UINT32, | ||
| 28 | + ge::DT_UINT64, ge::DT_UINT64, | ||
| 29 | + | ||
| 30 | + ge::DT_BOOL, ge::DT_BOOL, ge::DT_COMPLEX64, ge::DT_COMPLEX64}; | ||
| 31 | + | ||
| 32 | +static const std::vector<ge::DataType> kMultiplesDataTypes = { | ||
| 33 | + ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT64, ge::DT_INT64, ge::DT_INT64, | ||
| 34 | + | ||
| 35 | + ge::DT_INT32, ge::DT_INT64, ge::DT_INT32, ge::DT_INT64, ge::DT_INT32, ge::DT_INT64, | ||
| 36 | + | ||
| 37 | + ge::DT_INT32, ge::DT_INT64, ge::DT_INT32, ge::DT_INT64, ge::DT_INT32, ge::DT_INT64, ge::DT_INT32, ge::DT_INT64, | ||
| 38 | + | ||
| 39 | + ge::DT_INT32, ge::DT_INT64, ge::DT_INT32, ge::DT_INT64}; | ||
| 40 | + | ||
| 41 | +static const std::vector<ge::Format> kNdFormats(24, ge::FORMAT_ND); | ||
| 42 | + | ||
| 43 | +class Tile : public OpDef { | ||
| 44 | +public: | ||
| 45 | + explicit Tile(const char* name) : OpDef(name) | ||
| 46 | + { | ||
| 47 | + this->Input("x").ParamType(REQUIRED).DataType(kXDataTypes).Format(kNdFormats).UnknownShapeFormat(kNdFormats); | ||
| 48 | + | ||
| 49 | + this->Input("multiples") | ||
| 50 | + .ParamType(REQUIRED) | ||
| 51 | + .DataType(kMultiplesDataTypes) | ||
| 52 | + .Format(kNdFormats) | ||
| 53 | + .UnknownShapeFormat(kNdFormats) | ||
| 54 | + .ValueDepend(REQUIRED); | ||
| 55 | + | ||
| 56 | + this->Output("y").ParamType(REQUIRED).DataType(kXDataTypes).Format(kNdFormats).UnknownShapeFormat(kNdFormats); | ||
| 57 | + | ||
| 58 | + this->AICore().AddConfig("ascend910b"); | ||
| 59 | + } | ||
| 60 | +}; | ||
| 61 | + | ||
| 62 | +OP_ADD(Tile); | ||
| 63 | + | ||
| 64 | +} // namespace ops | ||
| @@ -0,0 +1,119 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile_infershape.cpp | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +using namespace ge; | ||
| 21 | + | ||
| 22 | +namespace ops { | ||
| 23 | +static constexpr size_t IDX_X = 0; | ||
| 24 | +static constexpr size_t IDX_MULTIPLES = 1; | ||
| 25 | +static constexpr size_t IDX_Y = 0; | ||
| 26 | + | ||
| 27 | +static ge::graphStatus SetUnknownShape(gert::InferShapeContext* context, gert::Shape* yShape, size_t outputDims) | ||
| 28 | +{ | ||
| 29 | + OP_CHECK_NULL_WITH_CONTEXT(context, yShape); | ||
| 30 | + yShape->SetDimNum(outputDims); | ||
| 31 | + for (size_t idx = 0; idx < outputDims; ++idx) { | ||
| 32 | + yShape->SetDim(idx, -1); | ||
| 33 | + } | ||
| 34 | + return GRAPH_SUCCESS; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +template <typename T> | ||
| 38 | +static ge::graphStatus GetMultiples( | ||
| 39 | + gert::InferShapeContext* context, const gert::Tensor* multiplesTensor, size_t multiplesSize, | ||
| 40 | + std::vector<int64_t>& multiples) | ||
| 41 | +{ | ||
| 42 | + const T* multiplesData = multiplesTensor->GetData<T>(); | ||
| 43 | + OP_CHECK_NULL_WITH_CONTEXT(context, multiplesData); | ||
| 44 | + multiples.reserve(multiplesSize); | ||
| 45 | + for (size_t idx = 0; idx < multiplesSize; ++idx) { | ||
| 46 | + multiples.push_back(static_cast<int64_t>(multiplesData[idx])); | ||
| 47 | + } | ||
| 48 | + return GRAPH_SUCCESS; | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +static ge::graphStatus InferShapeTile(gert::InferShapeContext* context) | ||
| 52 | +{ | ||
| 53 | + OP_CHECK_IF(context == nullptr, OP_LOGE(context, "context is nullptr"), return ge::GRAPH_FAILED); | ||
| 54 | + | ||
| 55 | + const gert::Shape* xShape = context->GetInputShape(IDX_X); | ||
| 56 | + OP_CHECK_NULL_WITH_CONTEXT(context, xShape); | ||
| 57 | + gert::Shape* yShape = context->GetOutputShape(IDX_Y); | ||
| 58 | + OP_CHECK_NULL_WITH_CONTEXT(context, yShape); | ||
| 59 | + | ||
| 60 | + const gert::Shape* multiplesShape = context->GetInputShape(IDX_MULTIPLES); | ||
| 61 | + OP_CHECK_NULL_WITH_CONTEXT(context, multiplesShape); | ||
| 62 | + const gert::Tensor* multiplesTensor = context->GetInputTensor(IDX_MULTIPLES); | ||
| 63 | + | ||
| 64 | + size_t xDims = xShape->GetDimNum(); | ||
| 65 | + size_t multiplesSize = 0; | ||
| 66 | + if (multiplesShape->GetDimNum() > 0) { | ||
| 67 | + multiplesSize = static_cast<size_t>(multiplesShape->GetDim(0)); | ||
| 68 | + } | ||
| 69 | + if (multiplesSize == 0) { | ||
| 70 | + *yShape = *xShape; | ||
| 71 | + return GRAPH_SUCCESS; | ||
| 72 | + } | ||
| 73 | + size_t outputDims = (xDims > multiplesSize) ? xDims : multiplesSize; | ||
| 74 | + if (multiplesTensor == nullptr) { | ||
| 75 | + return SetUnknownShape(context, yShape, outputDims); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + ge::DataType multiplesDtype = multiplesTensor->GetDataType(); | ||
| 79 | + bool hasData = false; | ||
| 80 | + if (multiplesDtype == ge::DT_INT32) { | ||
| 81 | + hasData = (multiplesTensor->GetData<int32_t>() != nullptr); | ||
| 82 | + } else if (multiplesDtype == ge::DT_INT64) { | ||
| 83 | + hasData = (multiplesTensor->GetData<int64_t>() != nullptr); | ||
| 84 | + } else { | ||
| 85 | + OP_LOGE(context, "multiples dtype must be int32 or int64"); | ||
| 86 | + return GRAPH_FAILED; | ||
| 87 | + } | ||
| 88 | + if (!hasData) { | ||
| 89 | + return SetUnknownShape(context, yShape, outputDims); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + std::vector<int64_t> multiples; | ||
| 93 | + if (multiplesDtype == ge::DT_INT32) { | ||
| 94 | + OP_CHECK_IF( | ||
| 95 | + GetMultiples<int32_t>(context, multiplesTensor, multiplesSize, multiples) != GRAPH_SUCCESS, | ||
| 96 | + OP_LOGE(context, "get int32 multiples failed"), return GRAPH_FAILED); | ||
| 97 | + } else { | ||
| 98 | + OP_CHECK_IF( | ||
| 99 | + GetMultiples<int64_t>(context, multiplesTensor, multiplesSize, multiples) != GRAPH_SUCCESS, | ||
| 100 | + OP_LOGE(context, "get int64 multiples failed"), return GRAPH_FAILED); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + size_t xOffset = outputDims - xDims; | ||
| 104 | + size_t multiplesOffset = outputDims - multiples.size(); | ||
| 105 | + yShape->SetDimNum(outputDims); | ||
| 106 | + for (size_t idx = 0; idx < outputDims; ++idx) { | ||
| 107 | + int64_t xDim = (idx < xOffset) ? 1 : xShape->GetDim(idx - xOffset); | ||
| 108 | + int64_t repeat = (idx < multiplesOffset) ? 1 : multiples[idx - multiplesOffset]; | ||
| 109 | + OP_CHECK_IF( | ||
| 110 | + repeat < 0, OP_LOGE(context, "multiples[%zu] must be >= 0, but got %ld", idx - multiplesOffset, repeat), | ||
| 111 | + return GRAPH_FAILED); | ||
| 112 | + yShape->SetDim(idx, xDim * repeat); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + return GRAPH_SUCCESS; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +IMPL_OP_INFERSHAPE(Tile).InferShape(InferShapeTile).InputsDataDependency({IDX_MULTIPLES}); | ||
| 119 | +} // namespace ops | ||
| @@ -0,0 +1,774 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile_tiling.cpp | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +namespace optiling { | ||
| 25 | + | ||
| 26 | +// ========== Hardware / platform constants ========== | ||
| 27 | +// UB is split into two buffers (input + output) for ping-pong operation | ||
| 28 | +constexpr int32_t TILE_BUFFER_NUM = 2; | ||
| 29 | +// Ascend DMA minimum transfer granularity: 32 bytes (one data block) | ||
| 30 | +constexpr int32_t DATA_BLOCK_BYTES = 32; | ||
| 31 | +// Gather offset table is uint32-aligned, grouped in units of 8 elements | ||
| 32 | +constexpr int32_t BYTE_ALIGN_SIZE = 8; | ||
| 33 | +// Vector register width is 256B: holds 128 fp16 elements or 64 fp32 elements | ||
| 34 | +constexpr int32_t VREG_WIDTH_FP16 = 128; | ||
| 35 | +constexpr int32_t VREG_WIDTH_FP32 = 64; | ||
| 36 | +// Each Gather offset table entry occupies 4 bytes (uint32) | ||
| 37 | +constexpr int32_t OFFSET_UINT32_SIZE = 4; | ||
| 38 | + | ||
| 39 | +// ========== Data type byte sizes ========== | ||
| 40 | +constexpr int32_t ELEM_BYTES_INT8 = 1; | ||
| 41 | +constexpr int32_t ELEM_BYTES_FP16 = 2; | ||
| 42 | +constexpr int32_t ELEM_BYTES_FP32 = 4; | ||
| 43 | + | ||
| 44 | +// ========== Scheduling mode selection thresholds ========== | ||
| 45 | +// The following thresholds are derived from performance profiling on Atlas A2 (Ascend 910B) | ||
| 46 | +// and are used to choose the most efficient processing path at runtime. | ||
| 47 | + | ||
| 48 | +// innerDim element-count thresholds that control whether the ScalarBuild path is enabled: | ||
| 49 | +// <=24: enabled for 1/2-byte types (SetValue loop overhead is acceptable) | ||
| 50 | +// <=32: relaxed for 1/2-byte types with even innerDim (uint32 packing optimization applies) | ||
| 51 | +// <=64: relaxed for 4-byte types (per-element SetValue cost is relatively lower) | ||
| 52 | +constexpr int32_t SMALL_INNER_DIM_THRESHOLD = 24; | ||
| 53 | +constexpr int32_t MODERATE_INNER_DIM_THRESHOLD = 32; | ||
| 54 | +constexpr int32_t LARGE_INNER_DIM_THRESHOLD = 64; | ||
| 55 | + | ||
| 56 | +// Minimum workload for the ScalarBuild path: total input elements or per-core rows must reach 256; | ||
| 57 | +// prevents using the scalar path on very small tensors where extra logic overhead dominates | ||
| 58 | +constexpr int32_t MIN_INPUT_ELEMS_FOR_SCALAR = 256; | ||
| 59 | +constexpr int32_t MIN_ROWS_PER_CORE_FOR_SCALAR = 256; | ||
| 60 | + | ||
| 61 | +// Minimum amplification ratio (repeatPeriod / repeatInputPeriod) for seed-amplify to pay off: | ||
| 62 | +// >=4: general paths (one seed write reused >=4 times justifies the extra read-back) | ||
| 63 | +// >=3: VecGather path has a lower bar (Gather instruction overhead is high, needs less reuse) | ||
| 64 | +constexpr int32_t MIN_PERIOD_RATIO_FOR_BENEFIT = 4; | ||
| 65 | +constexpr int32_t MIN_PERIOD_RATIO_FOR_VECGATHER = 3; | ||
| 66 | + | ||
| 67 | +// VecGather path constraints: | ||
| 68 | +// seed rows <=40 (offset table must stay in UB; too many rows squeeze data space) | ||
| 69 | +// output row width >=125 elements (short rows make Gather startup cost disproportionate) | ||
| 70 | +// only 2/4-byte types supported (Gather instruction requires element size >= 2 bytes) | ||
| 71 | +constexpr int32_t MAX_PERIOD_FOR_VECGATHER = 40; | ||
| 72 | +constexpr int32_t MIN_OUTPUT_INNER_DIM_FOR_VECGATHER = 125; | ||
| 73 | +constexpr int32_t MIN_ELEM_BYTES_FOR_VECGATHER = 2; | ||
| 74 | +constexpr int32_t MAX_ELEM_BYTES_FOR_VECGATHER = 4; | ||
| 75 | + | ||
| 76 | +// Readback path requires innerDim >= 12 elements so that each DMA transfer covers | ||
| 77 | +// several data blocks; below this threshold DMA startup cost exceeds scalar build | ||
| 78 | +constexpr int32_t MIN_INNER_DIM_FOR_READBACK = 12; | ||
| 79 | + | ||
| 80 | +// DmaBuild path requires innerDim >= 11 and seed rows >= 50: | ||
| 81 | +// too-small innerDim makes per-row DMA writes inefficient | ||
| 82 | +// too few seed rows means read-back amplification cannot recoup the extra GM I/O | ||
| 83 | +constexpr int32_t MIN_INNER_DIM_FOR_DMABUILD = 11; | ||
| 84 | +constexpr int32_t MIN_REP_INPUT_PERIOD_FOR_DMABUILD = 50; | ||
| 85 | + | ||
| 86 | +// ========== Multi-core grouping / period reuse thresholds ========== | ||
| 87 | + | ||
| 88 | +// Amplification >= 3 needed for source-grouping (periodsPerSource) to outweigh management overhead | ||
| 89 | +constexpr int32_t MIN_AMPLIFICATION_RATIO = 3; | ||
| 90 | +// When amplification <= 2, try to find a dimension with higher amplification | ||
| 91 | +constexpr int32_t MIN_AMP_THRESHOLD = 2; | ||
| 92 | + | ||
| 93 | +// Unique-source count thresholds for enabling source-grouping optimization: | ||
| 94 | +// >= 8: standard bar — enough sources to distribute evenly across cores | ||
| 95 | +// >= 4: relaxed for int8 / small-element types (small per-transfer volume needs more parallelism) | ||
| 96 | +constexpr int32_t MIN_NSRC_THRESHOLD = 8; | ||
| 97 | +constexpr int32_t MIN_NSRC_SMALL = 4; | ||
| 98 | + | ||
| 99 | +// Periods-per-source (pps) range: too large causes excessive serial work per core | ||
| 100 | +constexpr int32_t MAX_PPS_THRESHOLD = 20; | ||
| 101 | +// int8 types require pps >= 8 before small-source grouping is worthwhile | ||
| 102 | +constexpr int32_t MIN_PPS_THRESHOLD = 8; | ||
| 103 | + | ||
| 104 | +// Source grouping only shows clear benefit when seed rows (repeatInputPeriod) >= 100 | ||
| 105 | +constexpr int32_t MIN_RIP_FOR_BENEFIT = 100; | ||
| 106 | + | ||
| 107 | +// ========== Output size thresholds ========== | ||
| 108 | + | ||
| 109 | +// Total output <= 2 KB: force single core (multi-core scheduling overhead exceeds computation) | ||
| 110 | +constexpr int32_t SMALL_OUTPUT_BYTES_THRESHOLD = 2048; | ||
| 111 | +// Total output <= 4 KB: cap core count in splitByMult mode to avoid over-partitioning | ||
| 112 | +constexpr int32_t MEDIUM_OUTPUT_BYTES_THRESHOLD = 4096; | ||
| 113 | + | ||
| 114 | +// Seed template <= 256 B is considered too small; cap blockDim to AIC core count | ||
| 115 | +// to avoid repeated scheduling overhead for tiny templates | ||
| 116 | +constexpr int32_t SMALL_TEMPLATE_BYTES = 256; | ||
| 117 | +// Total output >= 64 KB: Readback path DMA read-back amplification has enough payoff | ||
| 118 | +constexpr int32_t LARGE_OUTPUT_BYTES_THRESHOLD = 65536; | ||
| 119 | + | ||
| 120 | +// In BuildOnce mode, merge cores only when period count >= 10 to ensure >= 2 periods per core | ||
| 121 | +constexpr int32_t MIN_PERIODS_FOR_MERGE = 10; | ||
| 122 | + | ||
| 123 | +struct TileCompileInfo {}; | ||
| 124 | + | ||
| 125 | +static ge::graphStatus TilingParseForTile([[maybe_unused]] gert::TilingParseContext* context) | ||
| 126 | +{ | ||
| 127 | + OP_CHECK_IF(context == nullptr, OP_LOGE(context, "context is nullptr"), return ge::GRAPH_FAILED); | ||
| 128 | + return ge::GRAPH_SUCCESS; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +static ge::graphStatus GetPlatformInfo( | ||
| 132 | + gert::TilingContext* context, uint64_t& ubSize, int64_t& coreNum, int64_t& aicCoreNum) | ||
| 133 | +{ | ||
| 134 | + OP_CHECK_IF(context == nullptr, OP_LOGE(context, "context is nullptr"), return ge::GRAPH_FAILED); | ||
| 135 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo()); | ||
| 136 | + ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize); | ||
| 137 | + coreNum = ascendcPlatform.GetCoreNum(); | ||
| 138 | + aicCoreNum = ascendcPlatform.GetCoreNumAic(); | ||
| 139 | + if (aicCoreNum <= 0) { | ||
| 140 | + aicCoreNum = coreNum; | ||
| 141 | + } | ||
| 142 | + OP_CHECK_IF(coreNum <= 0, OP_LOGE(context, "coreNum is 0"), return ge::GRAPH_FAILED); | ||
| 143 | + OP_CHECK_IF(ubSize <= 0, OP_LOGE(context, "ubSize is 0"), return ge::GRAPH_FAILED); | ||
| 144 | + return ge::GRAPH_SUCCESS; | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +static ge::graphStatus GetWorkspaceSize(gert::TilingContext* context) | ||
| 148 | +{ | ||
| 149 | + OP_CHECK_IF(context == nullptr, OP_LOGE(context, "context is nullptr"), return ge::GRAPH_FAILED); | ||
| 150 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo()); | ||
| 151 | + uint32_t sysWorkspaceSize = ascendcPlatform.GetLibApiWorkSpaceSize(); | ||
| 152 | + size_t* currentWorkspace = context->GetWorkspaceSizes(1); | ||
| 153 | + OP_CHECK_NULL_WITH_CONTEXT(context, currentWorkspace); | ||
| 154 | + currentWorkspace[0] = sysWorkspaceSize; | ||
| 155 | + return ge::GRAPH_SUCCESS; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +static void MergeConsecutiveDims(std::vector<int32_t>& shape, std::vector<int32_t>& multiples) | ||
| 159 | +{ | ||
| 160 | + bool changed = true; | ||
| 161 | + while (changed) { | ||
| 162 | + changed = false; | ||
| 163 | + for (int32_t idx = static_cast<int32_t>(shape.size()) - 1; idx >= 1; idx--) { | ||
| 164 | + if (multiples[idx] == 1) { | ||
| 165 | + shape[idx - 1] *= shape[idx]; | ||
| 166 | + shape.erase(shape.begin() + idx); | ||
| 167 | + multiples.erase(multiples.begin() + idx); | ||
| 168 | + changed = true; | ||
| 169 | + break; | ||
| 170 | + } | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +static void AlignLastDimForDMA(std::vector<int32_t>& shape, std::vector<int32_t>& multiples, int32_t elemBytes) | ||
| 176 | +{ | ||
| 177 | + while (shape.size() >= 2) { | ||
| 178 | + int32_t lastIdx = static_cast<int32_t>(shape.size()) - 1; | ||
| 179 | + if (shape[lastIdx] * elemBytes % DATA_BLOCK_BYTES == 0) { | ||
| 180 | + break; | ||
| 181 | + } | ||
| 182 | + int32_t secLast = lastIdx - 1; | ||
| 183 | + if (multiples[secLast] != 1) { | ||
| 184 | + break; | ||
| 185 | + } | ||
| 186 | + shape[lastIdx] = shape[secLast] * shape[lastIdx]; | ||
| 187 | + shape.erase(shape.begin() + secLast); | ||
| 188 | + multiples.erase(multiples.begin() + secLast); | ||
| 189 | + } | ||
| 190 | +} | ||
| 191 | + | ||
| 192 | +static void RemoveRedundantDims(std::vector<int32_t>& shape, std::vector<int32_t>& multiples) | ||
| 193 | +{ | ||
| 194 | + std::vector<int32_t> newShape; | ||
| 195 | + std::vector<int32_t> newMult; | ||
| 196 | + for (size_t idx = 0; idx < shape.size(); idx++) { | ||
| 197 | + if (shape[idx] == 1 && multiples[idx] == 1) { | ||
| 198 | + continue; | ||
| 199 | + } | ||
| 200 | + if (!newShape.empty() && newShape.back() == 1 && shape[idx] == 1) { | ||
| 201 | + newMult.back() *= multiples[idx]; | ||
| 202 | + } else { | ||
| 203 | + newShape.push_back(shape[idx]); | ||
| 204 | + newMult.push_back(multiples[idx]); | ||
| 205 | + } | ||
| 206 | + } | ||
| 207 | + if (newShape.empty()) { | ||
| 208 | + newShape.push_back(1); | ||
| 209 | + newMult.push_back(1); | ||
| 210 | + } | ||
| 211 | + shape = newShape; | ||
| 212 | + multiples = newMult; | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +static void MergeDims(std::vector<int32_t>& shape, std::vector<int32_t>& multiples, int32_t elemBytes) | ||
| 216 | +{ | ||
| 217 | + MergeConsecutiveDims(shape, multiples); | ||
| 218 | + AlignLastDimForDMA(shape, multiples, elemBytes); | ||
| 219 | + RemoveRedundantDims(shape, multiples); | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +static void ComputeTilingParams( | ||
| 223 | + TileTilingData& tilingData, const std::vector<int32_t>& origShape, const std::vector<int32_t>& origMult, | ||
| 224 | + int32_t elemBytes, int32_t blockDim, int32_t ubSize, int32_t aicBlockDim) | ||
| 225 | +{ | ||
| 226 | + (void)aicBlockDim; | ||
| 227 | + std::vector<int32_t> shape = origShape; | ||
| 228 | + std::vector<int32_t> multiples = origMult; | ||
| 229 | + MergeDims(shape, multiples, elemBytes); | ||
| 230 | + | ||
| 231 | + int32_t ndim = static_cast<int32_t>(shape.size()); | ||
| 232 | + tilingData.numDims = ndim; | ||
| 233 | + tilingData.elemBytes = elemBytes; | ||
| 234 | + tilingData.blockDim = blockDim; | ||
| 235 | + tilingData.ubSize = ubSize; | ||
| 236 | + tilingData.totalInputElems = 1; | ||
| 237 | + tilingData.totalOutputElems = 1; | ||
| 238 | + for (int32_t idx = 0; idx < ndim; idx++) { | ||
| 239 | + tilingData.inputShape[idx] = shape[idx]; | ||
| 240 | + tilingData.multiples[idx] = multiples[idx]; | ||
| 241 | + tilingData.outputShape[idx] = shape[idx] * multiples[idx]; | ||
| 242 | + tilingData.totalInputElems *= shape[idx]; | ||
| 243 | + tilingData.totalOutputElems *= tilingData.outputShape[idx]; | ||
| 244 | + } | ||
| 245 | + tilingData.inputStrides[ndim - 1] = 1; | ||
| 246 | + tilingData.outputStrides[ndim - 1] = 1; | ||
| 247 | + for (int32_t idx = ndim - 2; idx >= 0; idx--) { | ||
| 248 | + tilingData.inputStrides[idx] = tilingData.inputStrides[idx + 1] * tilingData.inputShape[idx + 1]; | ||
| 249 | + tilingData.outputStrides[idx] = tilingData.outputStrides[idx + 1] * tilingData.outputShape[idx + 1]; | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + int32_t innerDim = tilingData.inputShape[ndim - 1]; | ||
| 253 | + int32_t innerMult = tilingData.multiples[ndim - 1]; | ||
| 254 | + int32_t outputInnerDim = innerDim * innerMult; | ||
| 255 | + int32_t outerCount = (outputInnerDim > 0) ? (tilingData.totalOutputElems / outputInnerDim) : 0; | ||
| 256 | + int32_t alignElems = (elemBytes > 0) ? (DATA_BLOCK_BYTES / elemBytes) : 1; | ||
| 257 | + if (alignElems < 1) { | ||
| 258 | + alignElems = 1; | ||
| 259 | + } | ||
| 260 | + int32_t innerDimAligned = ((innerDim + alignElems - 1) / alignElems) * alignElems; | ||
| 261 | + int32_t bufElems = (elemBytes > 0) ? (ubSize / elemBytes) : 0; | ||
| 262 | + bool splitByMult = (outerCount < blockDim && innerMult > 1 && innerDimAligned <= bufElems); | ||
| 263 | + int32_t totalWork = splitByMult ? (outerCount * innerMult) : outerCount; | ||
| 264 | + if (!splitByMult && innerDimAligned > bufElems && outerCount < blockDim) { | ||
| 265 | + int32_t chunkSize = (bufElems / alignElems) * alignElems; | ||
| 266 | + if (chunkSize > 0) { | ||
| 267 | + int32_t numChunks = (innerDim + chunkSize - 1) / chunkSize; | ||
| 268 | + totalWork = outerCount * numChunks; | ||
| 269 | + } | ||
| 270 | + } | ||
| 271 | + if (totalWork < blockDim) { | ||
| 272 | + tilingData.blockDim = (totalWork > 0) ? totalWork : 1; | ||
| 273 | + } | ||
| 274 | + if (!splitByMult) { | ||
| 275 | + int64_t outputBytes = static_cast<int64_t>(tilingData.totalOutputElems) * elemBytes; | ||
| 276 | + if (outputBytes <= SMALL_OUTPUT_BYTES_THRESHOLD && tilingData.blockDim > 1) { | ||
| 277 | + tilingData.blockDim = 1; | ||
| 278 | + } | ||
| 279 | + } | ||
| 280 | + if (splitByMult && tilingData.blockDim > outerCount && outerCount >= 2) { | ||
| 281 | + int64_t outputBytes = static_cast<int64_t>(tilingData.totalOutputElems) * elemBytes; | ||
| 282 | + if (outputBytes <= MEDIUM_OUTPUT_BYTES_THRESHOLD || | ||
| 283 | + (innerDim * elemBytes >= DATA_BLOCK_BYTES && | ||
| 284 | + static_cast<int64_t>(outerCount) * outputInnerDim * elemBytes <= ubSize)) { | ||
| 285 | + tilingData.blockDim = outerCount; | ||
| 286 | + } | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + tilingData.repeatPeriod = 0; | ||
| 290 | + tilingData.repeatInputPeriod = 0; | ||
| 291 | + tilingData.periodsPerSource = 0; | ||
| 292 | + tilingData.nUniqueSources = 0; | ||
| 293 | + if (!splitByMult && ndim >= 2) { | ||
| 294 | + int32_t rowsPerCore = (outerCount + tilingData.blockDim - 1) / tilingData.blockDim; | ||
| 295 | + int32_t bestIPeriod = rowsPerCore + 1; | ||
| 296 | + int32_t bestRepeatDim = -1; | ||
| 297 | + for (int32_t d = ndim - 2; d >= 0; d--) { | ||
| 298 | + if (multiples[d] <= 1) { | ||
| 299 | + continue; | ||
| 300 | + } | ||
| 301 | + int32_t stride_d = 1; | ||
| 302 | + for (int32_t dd = d + 1; dd < ndim - 1; dd++) { | ||
| 303 | + stride_d *= tilingData.outputShape[dd]; | ||
| 304 | + } | ||
| 305 | + int32_t iPeriod = shape[d] * stride_d; | ||
| 306 | + int32_t oPeriod = tilingData.outputShape[d] * stride_d; | ||
| 307 | + if (iPeriod <= 0 || iPeriod > rowsPerCore) { | ||
| 308 | + continue; | ||
| 309 | + } | ||
| 310 | + if (oPeriod <= iPeriod) { | ||
| 311 | + continue; | ||
| 312 | + } | ||
| 313 | + if (iPeriod < bestIPeriod) { | ||
| 314 | + bestIPeriod = iPeriod; | ||
| 315 | + tilingData.repeatPeriod = oPeriod; | ||
| 316 | + tilingData.repeatInputPeriod = iPeriod; | ||
| 317 | + bestRepeatDim = d; | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | + if (bestRepeatDim >= 0) { | ||
| 321 | + int32_t bestAmp = tilingData.repeatPeriod / tilingData.repeatInputPeriod; | ||
| 322 | + if (bestAmp <= MIN_AMP_THRESHOLD) { | ||
| 323 | + for (int32_t d = ndim - 2; d >= 0; d--) { | ||
| 324 | + if (multiples[d] <= 1) { | ||
| 325 | + continue; | ||
| 326 | + } | ||
| 327 | + int32_t stride_d = 1; | ||
| 328 | + for (int32_t dd = d + 1; dd < ndim - 1; dd++) { | ||
| 329 | + stride_d *= tilingData.outputShape[dd]; | ||
| 330 | + } | ||
| 331 | + int32_t iPeriod = shape[d] * stride_d; | ||
| 332 | + int32_t oPeriod = tilingData.outputShape[d] * stride_d; | ||
| 333 | + if (iPeriod <= 0 || iPeriod > rowsPerCore || oPeriod <= iPeriod) { | ||
| 334 | + continue; | ||
| 335 | + } | ||
| 336 | + int32_t amp = oPeriod / iPeriod; | ||
| 337 | + if (amp <= bestAmp) { | ||
| 338 | + continue; | ||
| 339 | + } | ||
| 340 | + int32_t totalP = outerCount / oPeriod; | ||
| 341 | + if (totalP < tilingData.blockDim / MIN_NSRC_THRESHOLD) { | ||
| 342 | + continue; | ||
| 343 | + } | ||
| 344 | + tilingData.repeatPeriod = oPeriod; | ||
| 345 | + tilingData.repeatInputPeriod = iPeriod; | ||
| 346 | + bestRepeatDim = d; | ||
| 347 | + break; | ||
| 348 | + } | ||
| 349 | + } | ||
| 350 | + int32_t pps = 1; | ||
| 351 | + bool ppsValid = true; | ||
| 352 | + for (int32_t dd = 0; dd < bestRepeatDim; dd++) { | ||
| 353 | + if (shape[dd] == 1) { | ||
| 354 | + pps *= multiples[dd]; | ||
| 355 | + } else { | ||
| 356 | + ppsValid = false; | ||
| 357 | + break; | ||
| 358 | + } | ||
| 359 | + } | ||
| 360 | + if (!ppsValid) { | ||
| 361 | + pps = 1; | ||
| 362 | + int32_t candidatePPS = 1; | ||
| 363 | + for (int32_t dd = 0; dd < bestRepeatDim; dd++) { | ||
| 364 | + candidatePPS *= multiples[dd]; | ||
| 365 | + } | ||
| 366 | + int32_t nUnique = 1; | ||
| 367 | + for (int32_t dd = 0; dd < bestRepeatDim; dd++) { | ||
| 368 | + nUnique *= shape[dd]; | ||
| 369 | + } | ||
| 370 | + int32_t rP_local = tilingData.repeatPeriod; | ||
| 371 | + int32_t totalP_local = (rP_local > 0) ? (outerCount / rP_local) : 0; | ||
| 372 | + int32_t candidateNSrc = (candidatePPS > 0) ? (totalP_local / candidatePPS) : 0; | ||
| 373 | + if (candidateNSrc > 1 && candidateNSrc == nUnique && totalP_local == candidateNSrc * candidatePPS) { | ||
| 374 | + int32_t checkIdx = candidateNSrc * rP_local; | ||
| 375 | + int32_t srcOff1 = 0; | ||
| 376 | + int32_t tmpVal = checkIdx; | ||
| 377 | + for (int32_t d2 = ndim - 2; d2 >= 0; d2--) { | ||
| 378 | + int32_t oc = tmpVal % tilingData.outputShape[d2]; | ||
| 379 | + tmpVal /= tilingData.outputShape[d2]; | ||
| 380 | + srcOff1 += (oc % tilingData.inputShape[d2]) * tilingData.inputStrides[d2]; | ||
| 381 | + } | ||
| 382 | + if (srcOff1 == 0) { | ||
| 383 | + pps = candidatePPS; | ||
| 384 | + } | ||
| 385 | + } | ||
| 386 | + } | ||
| 387 | + int32_t riP = tilingData.repeatInputPeriod; | ||
| 388 | + int32_t rP = tilingData.repeatPeriod; | ||
| 389 | + int32_t totalP = (rP > 0) ? (outerCount / rP) : 0; | ||
| 390 | + int32_t nSrc = (pps > 0 && totalP > 0 && totalP % pps == 0) ? totalP / pps : 0; | ||
| 391 | + if ((nSrc >= MIN_NSRC_THRESHOLD && nSrc <= tilingData.blockDim && riP >= MIN_RIP_FOR_BENEFIT && | ||
| 392 | + pps <= MAX_PPS_THRESHOLD) || | ||
| 393 | + (nSrc >= MIN_NSRC_THRESHOLD && nSrc <= tilingData.blockDim && elemBytes == ELEM_BYTES_INT8) || | ||
| 394 | + (nSrc >= MIN_NSRC_SMALL && nSrc <= tilingData.blockDim && elemBytes == ELEM_BYTES_INT8 && | ||
| 395 | + pps >= MIN_PPS_THRESHOLD) || | ||
| 396 | + (nSrc >= MIN_NSRC_SMALL && nSrc <= tilingData.blockDim && pps > MIN_NSRC_SMALL && | ||
| 397 | + (rP / riP) >= MIN_AMPLIFICATION_RATIO && static_cast<int64_t>(rP) * outputInnerDim <= bufElems) || | ||
| 398 | + (nSrc >= MIN_NSRC_THRESHOLD && nSrc <= tilingData.blockDim && pps <= MAX_PPS_THRESHOLD && | ||
| 399 | + totalP >= tilingData.blockDim * 2) || | ||
| 400 | + (nSrc > tilingData.blockDim && elemBytes <= ELEM_BYTES_FP16 && pps <= MAX_PPS_THRESHOLD && | ||
| 401 | + (nSrc + tilingData.blockDim - 1) / tilingData.blockDim <= pps)) { | ||
| 402 | + tilingData.periodsPerSource = pps; | ||
| 403 | + } | ||
| 404 | + if (bestRepeatDim >= 0) { | ||
| 405 | + int32_t nUniqueCalc = 1; | ||
| 406 | + for (int32_t dd = 0; dd < bestRepeatDim; dd++) { | ||
| 407 | + nUniqueCalc *= shape[dd]; | ||
| 408 | + } | ||
| 409 | + if (nUniqueCalc > 1 && nUniqueCalc < totalP) { | ||
| 410 | + tilingData.nUniqueSources = nUniqueCalc; | ||
| 411 | + } | ||
| 412 | + } | ||
| 413 | + } | ||
| 414 | + if (bestRepeatDim < 0 && innerDimAligned <= bufElems) { | ||
| 415 | + bool smallID = | ||
| 416 | + (innerDim <= SMALL_INNER_DIM_THRESHOLD || | ||
| 417 | + (elemBytes >= ELEM_BYTES_FP32 && innerDim <= LARGE_INNER_DIM_THRESHOLD)); | ||
| 418 | + int32_t candidateIP = outerCount + 1; | ||
| 419 | + for (int32_t d = ndim - 2; d >= 0; d--) { | ||
| 420 | + if (multiples[d] <= 1) { | ||
| 421 | + continue; | ||
| 422 | + } | ||
| 423 | + int32_t stride_d = 1; | ||
| 424 | + for (int32_t dd = d + 1; dd < ndim - 1; dd++) { | ||
| 425 | + stride_d *= tilingData.outputShape[dd]; | ||
| 426 | + } | ||
| 427 | + int32_t iPeriod = shape[d] * stride_d; | ||
| 428 | + int32_t oPeriod = tilingData.outputShape[d] * stride_d; | ||
| 429 | + if (iPeriod <= 0 || oPeriod <= iPeriod) { | ||
| 430 | + continue; | ||
| 431 | + } | ||
| 432 | + int32_t amp = oPeriod / iPeriod; | ||
| 433 | + if (amp < MIN_AMPLIFICATION_RATIO) { | ||
| 434 | + continue; | ||
| 435 | + } | ||
| 436 | + int32_t neededBD = outerCount / iPeriod; | ||
| 437 | + if (neededBD < 1) | ||
| 438 | + neededBD = 1; | ||
| 439 | + if (neededBD >= tilingData.blockDim) { | ||
| 440 | + continue; | ||
| 441 | + } | ||
| 442 | + if (!smallID) { | ||
| 443 | + continue; | ||
| 444 | + } | ||
| 445 | + if (iPeriod < candidateIP) { | ||
| 446 | + candidateIP = iPeriod; | ||
| 447 | + tilingData.repeatPeriod = oPeriod; | ||
| 448 | + tilingData.repeatInputPeriod = iPeriod; | ||
| 449 | + bestRepeatDim = d; | ||
| 450 | + tilingData.blockDim = neededBD; | ||
| 451 | + } | ||
| 452 | + } | ||
| 453 | + } | ||
| 454 | + } | ||
| 455 | + if (!splitByMult && tilingData.blockDim > 1 && outerCount > 0 && innerDimAligned <= bufElems) { | ||
| 456 | + int32_t rPC = (outerCount + tilingData.blockDim - 1) / tilingData.blockDim; | ||
| 457 | + int32_t rowBytes = outputInnerDim * elemBytes; | ||
| 458 | + if (rowBytes > 0 && rowBytes % DATA_BLOCK_BYTES != 0 && rPC > 1) { | ||
| 459 | + int32_t g = rowBytes, bv = DATA_BLOCK_BYTES; | ||
| 460 | + while (bv) { | ||
| 461 | + int32_t t = bv; | ||
| 462 | + bv = g % bv; | ||
| 463 | + g = t; | ||
| 464 | + } | ||
| 465 | + int32_t aStep = DATA_BLOCK_BYTES / g; | ||
| 466 | + if (aStep > 1 && rPC > aStep) { | ||
| 467 | + rPC = ((rPC + aStep - 1) / aStep) * aStep; | ||
| 468 | + } | ||
| 469 | + } | ||
| 470 | + int32_t effBD = (rPC > 0) ? ((outerCount + rPC - 1) / rPC) : 1; | ||
| 471 | + if (effBD > 0 && effBD < tilingData.blockDim) { | ||
| 472 | + tilingData.blockDim = effBD; | ||
| 473 | + } | ||
| 474 | + } | ||
| 475 | +} | ||
| 476 | + | ||
| 477 | +static ge::graphStatus TileTilingFunc(gert::TilingContext* context) | ||
| 478 | +{ | ||
| 479 | + OP_CHECK_IF(context == nullptr, OP_LOGE(context, "context is nullptr"), return ge::GRAPH_FAILED); | ||
| 480 | + | ||
| 481 | + uint64_t ubSize; | ||
| 482 | + int64_t coreNum; | ||
| 483 | + int64_t aicCoreNum; | ||
| 484 | + ge::graphStatus ret = GetPlatformInfo(context, ubSize, coreNum, aicCoreNum); | ||
| 485 | + OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetPlatformInfo error"), return ge::GRAPH_FAILED); | ||
| 486 | + | ||
| 487 | + const gert::StorageShape* inputShape = context->GetInputShape(0); | ||
| 488 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputShape); | ||
| 489 | + | ||
| 490 | + auto inputDesc = context->GetInputDesc(0); | ||
| 491 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); | ||
| 492 | + ge::DataType dtype = inputDesc->GetDataType(); | ||
| 493 | + uint32_t typeLength = 0; | ||
| 494 | + ge::TypeUtils::GetDataTypeLength(dtype, typeLength); | ||
| 495 | + int32_t elemBytes = static_cast<int32_t>(typeLength); | ||
| 496 | + OP_CHECK_IF(elemBytes <= 0, OP_LOGE(context, "unsupported data type, elemBytes <= 0"), return ge::GRAPH_FAILED); | ||
| 497 | + | ||
| 498 | + int32_t ndim = static_cast<int32_t>(inputShape->GetStorageShape().GetDimNum()); | ||
| 499 | + std::vector<int32_t> shape(ndim); | ||
| 500 | + for (int32_t idx = 0; idx < ndim; idx++) { | ||
| 501 | + shape[idx] = static_cast<int32_t>(inputShape->GetStorageShape().GetDim(idx)); | ||
| 502 | + } | ||
| 503 | + | ||
| 504 | + const gert::Tensor* multiplesTensor = context->GetInputTensor(1); | ||
| 505 | + OP_CHECK_NULL_WITH_CONTEXT(context, multiplesTensor); | ||
| 506 | + const void* multiplesAddr = multiplesTensor->GetAddr(); | ||
| 507 | + OP_CHECK_NULL_WITH_CONTEXT(context, multiplesAddr); | ||
| 508 | + | ||
| 509 | + auto multiplesDesc = context->GetInputDesc(1); | ||
| 510 | + OP_CHECK_NULL_WITH_CONTEXT(context, multiplesDesc); | ||
| 511 | + ge::DataType multiplesDtype = multiplesDesc->GetDataType(); | ||
| 512 | + | ||
| 513 | + const gert::StorageShape* multiplesShape = context->GetInputShape(1); | ||
| 514 | + OP_CHECK_NULL_WITH_CONTEXT(context, multiplesShape); | ||
| 515 | + int32_t multiplesLen = static_cast<int32_t>(multiplesShape->GetStorageShape().GetDim(0)); | ||
| 516 | + std::vector<int32_t> mult(multiplesLen); | ||
| 517 | + if (multiplesDtype == ge::DT_INT64) { | ||
| 518 | + const int64_t* multiplesData64 = reinterpret_cast<const int64_t*>(multiplesAddr); | ||
| 519 | + for (int32_t idx = 0; idx < multiplesLen; idx++) { | ||
| 520 | + mult[idx] = static_cast<int32_t>(multiplesData64[idx]); | ||
| 521 | + } | ||
| 522 | + } else { | ||
| 523 | + const int32_t* multiplesData32 = reinterpret_cast<const int32_t*>(multiplesAddr); | ||
| 524 | + for (int32_t idx = 0; idx < multiplesLen; idx++) { | ||
| 525 | + mult[idx] = multiplesData32[idx]; | ||
| 526 | + } | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + while (static_cast<int32_t>(shape.size()) < multiplesLen) { | ||
| 530 | + shape.insert(shape.begin(), 1); | ||
| 531 | + } | ||
| 532 | + while (static_cast<int32_t>(mult.size()) < static_cast<int32_t>(shape.size())) { | ||
| 533 | + mult.insert(mult.begin(), 1); | ||
| 534 | + } | ||
| 535 | + | ||
| 536 | + int32_t blockDim = static_cast<int32_t>(coreNum); | ||
| 537 | + int32_t aicBlockDim = static_cast<int32_t>(aicCoreNum); | ||
| 538 | + int32_t bufferSize = static_cast<int32_t>(ubSize) / TILE_BUFFER_NUM; | ||
| 539 | + | ||
| 540 | + TileTilingData* tiling = context->GetTilingData<TileTilingData>(); | ||
| 541 | + OP_CHECK_NULL_WITH_CONTEXT(context, tiling); | ||
| 542 | + OP_CHECK_IF( | ||
| 543 | + memset_s(tiling, sizeof(TileTilingData), 0, sizeof(TileTilingData)) != EOK, | ||
| 544 | + OP_LOGE(context, "set tiling data error"), return ge::GRAPH_FAILED); | ||
| 545 | + | ||
| 546 | + ComputeTilingParams(*tiling, shape, mult, elemBytes, blockDim, bufferSize, aicBlockDim); | ||
| 547 | + | ||
| 548 | + OP_CHECK_IF( | ||
| 549 | + GetWorkspaceSize(context) != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetWorkspaceSize error"), | ||
| 550 | + return ge::GRAPH_FAILED); | ||
| 551 | + | ||
| 552 | + int32_t ae = (elemBytes > 0) ? (DATA_BLOCK_BYTES / elemBytes) : 1; | ||
| 553 | + if (ae < 1) { | ||
| 554 | + ae = 1; | ||
| 555 | + } | ||
| 556 | + int32_t fID = tiling->inputShape[tiling->numDims - 1]; | ||
| 557 | + int32_t fIM = tiling->multiples[tiling->numDims - 1]; | ||
| 558 | + int32_t fIDA = ((fID + ae - 1) / ae) * ae; | ||
| 559 | + int32_t fOD = fID * fIM; | ||
| 560 | + bool fAligned = (fID == fIDA); | ||
| 561 | + int32_t fOC = (fOD > 0) ? (tiling->totalOutputElems / fOD) : 0; | ||
| 562 | + bool fSplit = (fOC < tiling->blockDim && fIM > 1 && fIDA <= bufferSize / elemBytes); | ||
| 563 | + int32_t fRowsPerCore = (tiling->blockDim > 0) ? ((fOC + tiling->blockDim - 1) / tiling->blockDim) : 0; | ||
| 564 | + int32_t rPre = tiling->repeatPeriod; | ||
| 565 | + int32_t riPre = tiling->repeatInputPeriod; | ||
| 566 | + bool hasRepeatBenefit = | ||
| 567 | + (rPre > riPre && riPre > 0 && (rPre / riPre) >= MIN_PERIOD_RATIO_FOR_BENEFIT && tiling->totalInputElems > 1); | ||
| 568 | + bool useScalarMode = | ||
| 569 | + !fSplit && !fAligned && fIM > 1 && fOD <= (bufferSize / elemBytes) && | ||
| 570 | + (tiling->totalInputElems > MIN_INPUT_ELEMS_FOR_SCALAR || fRowsPerCore >= MIN_ROWS_PER_CORE_FOR_SCALAR || | ||
| 571 | + hasRepeatBenefit) && | ||
| 572 | + (fID <= SMALL_INNER_DIM_THRESHOLD || (elemBytes >= ELEM_BYTES_FP32 && fID <= LARGE_INNER_DIM_THRESHOLD)); | ||
| 573 | + | ||
| 574 | + int32_t rP = tiling->repeatPeriod; | ||
| 575 | + int32_t riP = tiling->repeatInputPeriod; | ||
| 576 | + int32_t fBufElems = (elemBytes > 0) ? (bufferSize / elemBytes) : 0; | ||
| 577 | + bool canBuildOnce = | ||
| 578 | + rP > riP && riP > 1 && (rP / riP) > 1 && riP * fOD <= fBufElems && (riP - 1) * fID + fIDA <= fBufElems; | ||
| 579 | + if (!useScalarMode && canBuildOnce && !fSplit && !fAligned && fIM > 1 && fOD <= (bufferSize / elemBytes) && | ||
| 580 | + (tiling->totalInputElems > MIN_INPUT_ELEMS_FOR_SCALAR || fRowsPerCore >= MIN_ROWS_PER_CORE_FOR_SCALAR || | ||
| 581 | + hasRepeatBenefit) && | ||
| 582 | + elemBytes <= ELEM_BYTES_FP16 && fID <= MODERATE_INNER_DIM_THRESHOLD && fID % 2 == 0) { | ||
| 583 | + useScalarMode = true; | ||
| 584 | + } | ||
| 585 | + if (!useScalarMode && canBuildOnce && !fSplit && !fAligned && fIM > 1 && fOD <= (bufferSize / elemBytes) && | ||
| 586 | + hasRepeatBenefit && elemBytes == ELEM_BYTES_INT8 && fID <= LARGE_INNER_DIM_THRESHOLD) { | ||
| 587 | + useScalarMode = true; | ||
| 588 | + } | ||
| 589 | + bool useBuildOnce = useScalarMode && canBuildOnce; | ||
| 590 | + | ||
| 591 | + bool useVecGather = false; | ||
| 592 | + if (useBuildOnce && elemBytes >= MIN_ELEM_BYTES_FOR_VECGATHER && elemBytes <= MAX_ELEM_BYTES_FOR_VECGATHER && | ||
| 593 | + fIM > 1 && !fAligned && (rP / riP) >= MIN_PERIOD_RATIO_FOR_VECGATHER && riP <= MAX_PERIOD_FOR_VECGATHER && | ||
| 594 | + fID * fIM >= MIN_OUTPUT_INNER_DIM_FOR_VECGATHER) { | ||
| 595 | + int32_t vreg = (elemBytes == ELEM_BYTES_FP16) ? VREG_WIDTH_FP16 : VREG_WIDTH_FP32; | ||
| 596 | + int32_t gRow = (fOD > vreg) ? (((fOD + ae - 1) / ae) * ae) : vreg; | ||
| 597 | + int32_t offU = ((fOD + BYTE_ALIGN_SIZE - 1) / BYTE_ALIGN_SIZE) * BYTE_ALIGN_SIZE; | ||
| 598 | + int32_t offT = (offU * OFFSET_UINT32_SIZE + elemBytes - 1) / elemBytes; | ||
| 599 | + int32_t offA = ((offT + ae - 1) / ae) * ae; | ||
| 600 | + int32_t total = offA + riP * gRow; | ||
| 601 | + if (total <= fBufElems && riP * fIDA <= fBufElems) { | ||
| 602 | + useVecGather = true; | ||
| 603 | + } | ||
| 604 | + } | ||
| 605 | + | ||
| 606 | + bool useDmaBuild = useBuildOnce && elemBytes >= ELEM_BYTES_FP32 && (fID % 2) == 1 && | ||
| 607 | + fID >= MIN_INNER_DIM_FOR_DMABUILD && riP >= MIN_REP_INPUT_PERIOD_FOR_DMABUILD && | ||
| 608 | + riP * fIDA <= fBufElems; | ||
| 609 | + | ||
| 610 | + bool readbackOverride = false; | ||
| 611 | + if (useDmaBuild && rP > riP && riP > 0 && (rP / riP) >= MIN_PERIOD_RATIO_FOR_BENEFIT && riP * fOD <= fBufElems && | ||
| 612 | + fOD <= fBufElems) { | ||
| 613 | + readbackOverride = true; | ||
| 614 | + } | ||
| 615 | + | ||
| 616 | + int32_t gRP = rPre, gRiP = riPre; | ||
| 617 | + if (gRP <= gRiP && tiling->numDims >= 2) { | ||
| 618 | + int32_t bestIP = INT32_MAX; | ||
| 619 | + for (int32_t d = tiling->numDims - 2; d >= 0; d--) { | ||
| 620 | + if (tiling->multiples[d] <= 1) | ||
| 621 | + continue; | ||
| 622 | + int32_t sd = 1; | ||
| 623 | + for (int32_t dd = d + 1; dd < tiling->numDims - 1; dd++) | ||
| 624 | + sd *= tiling->outputShape[dd]; | ||
| 625 | + int32_t ip = tiling->inputShape[d] * sd; | ||
| 626 | + int32_t op = tiling->outputShape[d] * sd; | ||
| 627 | + if (ip <= 0 || op <= ip) | ||
| 628 | + continue; | ||
| 629 | + if (ip < bestIP) { | ||
| 630 | + bestIP = ip; | ||
| 631 | + gRP = op; | ||
| 632 | + gRiP = ip; | ||
| 633 | + } | ||
| 634 | + } | ||
| 635 | + } | ||
| 636 | + | ||
| 637 | + bool useReadbackAmplify = !useScalarMode && !fSplit && !fAligned && fIM > 1 && gRP > gRiP && gRiP > 0 && | ||
| 638 | + (gRP / gRiP) >= MIN_PERIOD_RATIO_FOR_BENEFIT && fOD <= (bufferSize / elemBytes) && | ||
| 639 | + fID > MIN_INNER_DIM_FOR_READBACK && static_cast<int64_t>(fIDA) * fIM <= fBufElems && | ||
| 640 | + (static_cast<int64_t>(fID) * elemBytes <= SMALL_TEMPLATE_BYTES || | ||
| 641 | + static_cast<int64_t>(fOC) * fOD * elemBytes >= LARGE_OUTPUT_BYTES_THRESHOLD); | ||
| 642 | + | ||
| 643 | + bool useInnerOneFill = fSplit && fID == 1 && fIM > ae; | ||
| 644 | + | ||
| 645 | + bool buildOnceToReadback = false; | ||
| 646 | + if (useBuildOnce && gRP > gRiP && gRiP > 0) { | ||
| 647 | + int32_t totalPeriods_bo = (gRP > 0) ? (fOC / gRP) : 0; | ||
| 648 | + int32_t totalSeeds_bo = totalPeriods_bo * gRiP; | ||
| 649 | + int32_t seedsPerCore_bo = | ||
| 650 | + (tiling->blockDim > 0) ? (totalSeeds_bo + tiling->blockDim - 1) / tiling->blockDim : 0; | ||
| 651 | + if (totalPeriods_bo > 0 && totalPeriods_bo < tiling->blockDim / 2 && seedsPerCore_bo >= MIN_NSRC_THRESHOLD && | ||
| 652 | + fOD <= fBufElems && static_cast<int64_t>(fIDA) * fIM <= fBufElems) { | ||
| 653 | + useBuildOnce = false; | ||
| 654 | + buildOnceToReadback = true; | ||
| 655 | + } | ||
| 656 | + } | ||
| 657 | + | ||
| 658 | + if (useInnerOneFill) { | ||
| 659 | + tiling->blockDim = 1; | ||
| 660 | + } | ||
| 661 | + | ||
| 662 | + uint64_t tilingKey; | ||
| 663 | + if (readbackOverride) { | ||
| 664 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_READBACK); | ||
| 665 | + } else if (useDmaBuild) { | ||
| 666 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_DMABUILD); | ||
| 667 | + } else if (useVecGather) { | ||
| 668 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_VECGATHER); | ||
| 669 | + } else if (useBuildOnce) { | ||
| 670 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_BUILDONCE); | ||
| 671 | + } else if (buildOnceToReadback) { | ||
| 672 | + if (gRP != rPre || gRiP != riPre) { | ||
| 673 | + tiling->repeatPeriod = gRP; | ||
| 674 | + tiling->repeatInputPeriod = gRiP; | ||
| 675 | + } | ||
| 676 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_READBACK); | ||
| 677 | + if (tiling->blockDim * MIN_AMPLIFICATION_RATIO < static_cast<int32_t>(coreNum)) { | ||
| 678 | + tiling->blockDim = static_cast<int32_t>(coreNum); | ||
| 679 | + } | ||
| 680 | + } else if (useInnerOneFill) { | ||
| 681 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_DMABUILD); | ||
| 682 | + } else if (useReadbackAmplify) { | ||
| 683 | + if (gRP != rPre || gRiP != riPre) { | ||
| 684 | + tiling->repeatPeriod = gRP; | ||
| 685 | + tiling->repeatInputPeriod = gRiP; | ||
| 686 | + } | ||
| 687 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_READBACK); | ||
| 688 | + } else { | ||
| 689 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_DEFAULT); | ||
| 690 | + if (!fSplit && !fAligned && fIM > 1 && fOD <= fBufElems && tiling->numDims >= 2 && | ||
| 691 | + (fID <= MIN_INNER_DIM_FOR_READBACK || (elemBytes >= ELEM_BYTES_FP32 && fID <= LARGE_INNER_DIM_THRESHOLD))) { | ||
| 692 | + int32_t bestIP2 = INT32_MAX; | ||
| 693 | + int32_t foundRP = 0, foundRiP = 0; | ||
| 694 | + for (int32_t d = tiling->numDims - 2; d >= 0; d--) { | ||
| 695 | + if (tiling->multiples[d] <= 1) | ||
| 696 | + continue; | ||
| 697 | + int32_t sd = 1; | ||
| 698 | + for (int32_t dd = d + 1; dd < tiling->numDims - 1; dd++) | ||
| 699 | + sd *= tiling->outputShape[dd]; | ||
| 700 | + int32_t ip = tiling->inputShape[d] * sd; | ||
| 701 | + int32_t op = tiling->outputShape[d] * sd; | ||
| 702 | + if (ip <= 0 || op <= ip) | ||
| 703 | + continue; | ||
| 704 | + if ((op / ip) < MIN_PERIOD_RATIO_FOR_BENEFIT) | ||
| 705 | + continue; | ||
| 706 | + bool bo = ip > 1 && ip * fOD <= fBufElems && (ip - 1) * fID + fIDA <= fBufElems; | ||
| 707 | + if (!bo) | ||
| 708 | + continue; | ||
| 709 | + int32_t tp = fOC / op; | ||
| 710 | + if (tp < 1) | ||
| 711 | + continue; | ||
| 712 | + if (ip < bestIP2) { | ||
| 713 | + bestIP2 = ip; | ||
| 714 | + foundRP = op; | ||
| 715 | + foundRiP = ip; | ||
| 716 | + } | ||
| 717 | + } | ||
| 718 | + if (foundRP > 0 && foundRiP > 0) { | ||
| 719 | + tiling->repeatPeriod = foundRP; | ||
| 720 | + tiling->repeatInputPeriod = foundRiP; | ||
| 721 | + tilingKey = GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_BUILDONCE); | ||
| 722 | + } | ||
| 723 | + } | ||
| 724 | + } | ||
| 725 | + context->SetTilingKey(tilingKey); | ||
| 726 | + | ||
| 727 | + bool isPeriodBased = tilingKey == GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_BUILDONCE) || | ||
| 728 | + tilingKey == GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_DMABUILD) || | ||
| 729 | + tilingKey == GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_VECGATHER); | ||
| 730 | + if (isPeriodBased && tiling->repeatPeriod > 0) { | ||
| 731 | + int32_t totalPeriods = fOC / tiling->repeatPeriod; | ||
| 732 | + int32_t remainder = fOC - totalPeriods * tiling->repeatPeriod; | ||
| 733 | + int32_t pps = tiling->periodsPerSource; | ||
| 734 | + int32_t neededCores = 0; | ||
| 735 | + if (pps > 0 && totalPeriods > 0 && totalPeriods % pps == 0) { | ||
| 736 | + int32_t nSrc = totalPeriods / pps; | ||
| 737 | + if (nSrc > 0 && nSrc < tiling->blockDim) { | ||
| 738 | + neededCores = nSrc + (remainder > 0 ? 1 : 0); | ||
| 739 | + } | ||
| 740 | + } else if (pps == 0) { | ||
| 741 | + neededCores = totalPeriods + (remainder > 0 ? 1 : 0); | ||
| 742 | + } | ||
| 743 | + if (neededCores > 0 && neededCores < tiling->blockDim) { | ||
| 744 | + int32_t periodsPerCore = (totalPeriods + neededCores - 1) / neededCores; | ||
| 745 | + if (periodsPerCore < 2 && tilingKey == GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_BUILDONCE) && | ||
| 746 | + totalPeriods > MIN_PERIODS_FOR_MERGE) { | ||
| 747 | + neededCores = (totalPeriods + 1) / 2; | ||
| 748 | + } | ||
| 749 | + if (neededCores > 0 && neededCores < tiling->blockDim) { | ||
| 750 | + tiling->blockDim = neededCores; | ||
| 751 | + } | ||
| 752 | + } | ||
| 753 | + } | ||
| 754 | + | ||
| 755 | + if (isPeriodBased && tilingKey == GET_TPL_TILING_KEY(TILE_TPL_SCH_MODE_BUILDONCE) && tiling->repeatPeriod > 0 && | ||
| 756 | + tiling->repeatInputPeriod > 0) { | ||
| 757 | + int32_t boRiP = tiling->repeatInputPeriod; | ||
| 758 | + int32_t boOID = tiling->inputShape[tiling->numDims - 1] * tiling->multiples[tiling->numDims - 1]; | ||
| 759 | + int64_t templateBytes = static_cast<int64_t>(boRiP) * boOID * elemBytes; | ||
| 760 | + if (templateBytes < SMALL_TEMPLATE_BYTES && tiling->blockDim > aicBlockDim && aicBlockDim > 0) { | ||
| 761 | + tiling->blockDim = aicBlockDim; | ||
| 762 | + } | ||
| 763 | + } | ||
| 764 | + | ||
| 765 | + context->SetBlockDim(static_cast<uint32_t>(tiling->blockDim)); | ||
| 766 | + | ||
| 767 | + return ge::GRAPH_SUCCESS; | ||
| 768 | +} | ||
| 769 | + | ||
| 770 | +IMPL_OP_OPTILING(Tile) | ||
| 771 | + .Tiling(TileTilingFunc) | ||
| 772 | + .TilingParse<TileCompileInfo>(TilingParseForTile) | ||
| 773 | + .InputsDataDependency({1}); | ||
| 774 | +} // namespace optiling | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile.cpp | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +template <uint32_t schMode> | ||
| 19 | +__global__ __aicore__ void tile(GM_ADDR x, GM_ADDR multiples, GM_ADDR y, GM_ADDR workspace, GM_ADDR tiling) | ||
| 20 | +{ | ||
| 21 | + REGISTER_TILING_DEFAULT(TileTilingData); | ||
| 22 | + GET_TILING_DATA_WITH_STRUCT(TileTilingData, tilingData, tiling); | ||
| 23 | + AscendC::TPipe pipe; | ||
| 24 | + TileKernel::TileOpImpl<DTYPE_X> op; | ||
| 25 | + op.template Init<schMode>(x, y, &tilingData, &pipe); | ||
| 26 | + op.template Process<schMode>(); | ||
| 27 | +} | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile_tiling_data.h | ||
| 13 | + * \brief tiling data struct | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +constexpr int32_t TILE_MAX_DIM = 8; | ||
| 21 | + | ||
| 22 | +struct TileTilingData { | ||
| 23 | + int32_t numDims; | ||
| 24 | + int32_t inputShape[TILE_MAX_DIM]; | ||
| 25 | + int32_t multiples[TILE_MAX_DIM]; | ||
| 26 | + int32_t outputShape[TILE_MAX_DIM]; | ||
| 27 | + int32_t inputStrides[TILE_MAX_DIM]; | ||
| 28 | + int32_t outputStrides[TILE_MAX_DIM]; | ||
| 29 | + int32_t totalInputElems; | ||
| 30 | + int32_t totalOutputElems; | ||
| 31 | + int32_t elemBytes; | ||
| 32 | + int32_t blockDim; | ||
| 33 | + int32_t ubSize; | ||
| 34 | + int32_t repeatPeriod; | ||
| 35 | + int32_t repeatInputPeriod; | ||
| 36 | + int32_t periodsPerSource; | ||
| 37 | + int32_t nUniqueSources; | ||
| 38 | +}; | ||
| 39 | + | ||
| 40 | + | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile_tiling_key.h | ||
| 13 | + * \brief tile tiling key declare | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +ASCENDC_TPL_ARGS_DECL( | ||
| 27 | + Tile, ASCENDC_TPL_UINT_DECL( | ||
| 28 | + schMode, 3, ASCENDC_TPL_UI_LIST, TILE_TPL_SCH_MODE_DEFAULT, TILE_TPL_SCH_MODE_BUILDONCE, | ||
| 29 | + TILE_TPL_SCH_MODE_DMABUILD, TILE_TPL_SCH_MODE_READBACK, TILE_TPL_SCH_MODE_VECGATHER)); | ||
| 30 | + | ||
| 31 | +ASCENDC_TPL_SEL( | ||
| 32 | + ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_UINT_SEL( | ||
| 33 | + schMode, ASCENDC_TPL_UI_LIST, TILE_TPL_SCH_MODE_DEFAULT, TILE_TPL_SCH_MODE_BUILDONCE, | ||
| 34 | + TILE_TPL_SCH_MODE_DMABUILD, TILE_TPL_SCH_MODE_READBACK, TILE_TPL_SCH_MODE_VECGATHER)), ); | ||
| 35 | + | ||
| 36 | + | ||
| @@ -0,0 +1,67 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file test_aclnn_repeat.cpp | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +class l2_repeat_test : public testing::Test { | ||
| 27 | +protected: | ||
| 28 | + static void SetUpTestCase() | ||
| 29 | + { | ||
| 30 | + cout << "l2_repeat_test SetUp" << endl; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + static void TearDownTestCase() | ||
| 34 | + { | ||
| 35 | + cout << "l2_repeat_test TearDown" << endl; | ||
| 36 | + } | ||
| 37 | +}; | ||
| 38 | + | ||
| 39 | +TEST_F(l2_repeat_test, case_01_float_2d) | ||
| 40 | +{ | ||
| 41 | + op::SetPlatformSocVersion(op::SocVersion::ASCEND910B); | ||
| 42 | + | ||
| 43 | + auto selfDesc = TensorDesc({2, 3}, ACL_FLOAT, ACL_FORMAT_ND).Value(vector<float>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}); | ||
| 44 | + auto repeatsDesc = IntArrayDesc(vector<int64_t>{3, 2}); | ||
| 45 | + auto outDesc = TensorDesc({6, 6}, ACL_FLOAT, ACL_FORMAT_ND); | ||
| 46 | + | ||
| 47 | + auto ut = OP_API_UT(aclnnRepeat, INPUT(selfDesc, repeatsDesc), OUTPUT(outDesc)); | ||
| 48 | + | ||
| 49 | + uint64_t workspace_size = 0; | ||
| 50 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspace_size); | ||
| 51 | + EXPECT_EQ(aclRet, ACLNN_SUCCESS); | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +TEST_F(l2_repeat_test, case_02_float16_1d) | ||
| 55 | +{ | ||
| 56 | + op::SetPlatformSocVersion(op::SocVersion::ASCEND910B); | ||
| 57 | + | ||
| 58 | + auto selfDesc = TensorDesc({4}, ACL_FLOAT16, ACL_FORMAT_ND).Value(vector<double>{1.0, 2.0, 3.0, 4.0}); | ||
| 59 | + auto repeatsDesc = IntArrayDesc(vector<int64_t>{3}); | ||
| 60 | + auto outDesc = TensorDesc({12}, ACL_FLOAT16, ACL_FORMAT_ND); | ||
| 61 | + | ||
| 62 | + auto ut = OP_API_UT(aclnnRepeat, INPUT(selfDesc, repeatsDesc), OUTPUT(outDesc)); | ||
| 63 | + | ||
| 64 | + uint64_t workspace_size = 0; | ||
| 65 | + aclnnStatus aclRet = ut.TestGetWorkspaceSize(&workspace_size); | ||
| 66 | + EXPECT_EQ(aclRet, ACLNN_SUCCESS); | ||
| 67 | +} | ||
| @@ -0,0 +1,112 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +class TileInfershape : public testing::Test { | ||
| 17 | +protected: | ||
| 18 | + static void SetUpTestCase() | ||
| 19 | + { | ||
| 20 | + std::cout << "TileInfershape SetUp" << std::endl; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + static void TearDownTestCase() | ||
| 24 | + { | ||
| 25 | + std::cout << "TileInfershape TearDown" << std::endl; | ||
| 26 | + } | ||
| 27 | +}; | ||
| 28 | + | ||
| 29 | +TEST_F(TileInfershape, tile_infershape_2d_dynamic) | ||
| 30 | +{ | ||
| 31 | + gert::InfershapeContextPara infershapeContextPara( | ||
| 32 | + "Tile", | ||
| 33 | + { | ||
| 34 | + {{{2, 3}, {2, 3}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 35 | + {{{2}, {2}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 36 | + }, | ||
| 37 | + { | ||
| 38 | + {{{}, {}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 39 | + }); | ||
| 40 | + std::vector<std::vector<int64_t>> expectOutputShape = { | ||
| 41 | + {-1, -1}, | ||
| 42 | + }; | ||
| 43 | + ExecuteTestCase(infershapeContextPara, ge::GRAPH_SUCCESS, expectOutputShape); | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +TEST_F(TileInfershape, tile_infershape_1d_dynamic) | ||
| 47 | +{ | ||
| 48 | + gert::InfershapeContextPara infershapeContextPara( | ||
| 49 | + "Tile", | ||
| 50 | + { | ||
| 51 | + {{{128}, {128}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 52 | + {{{1}, {1}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 53 | + }, | ||
| 54 | + { | ||
| 55 | + {{{}, {}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 56 | + }); | ||
| 57 | + std::vector<std::vector<int64_t>> expectOutputShape = { | ||
| 58 | + {-1}, | ||
| 59 | + }; | ||
| 60 | + ExecuteTestCase(infershapeContextPara, ge::GRAPH_SUCCESS, expectOutputShape); | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +TEST_F(TileInfershape, tile_infershape_4d_dynamic) | ||
| 64 | +{ | ||
| 65 | + gert::InfershapeContextPara infershapeContextPara( | ||
| 66 | + "Tile", | ||
| 67 | + { | ||
| 68 | + {{{1, 1, 1, 8}, {1, 1, 1, 8}}, ge::DT_FLOAT16, ge::FORMAT_ND}, | ||
| 69 | + {{{4}, {4}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 70 | + }, | ||
| 71 | + { | ||
| 72 | + {{{}, {}}, ge::DT_FLOAT16, ge::FORMAT_ND}, | ||
| 73 | + }); | ||
| 74 | + std::vector<std::vector<int64_t>> expectOutputShape = { | ||
| 75 | + {-1, -1, -1, -1}, | ||
| 76 | + }; | ||
| 77 | + ExecuteTestCase(infershapeContextPara, ge::GRAPH_SUCCESS, expectOutputShape); | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +TEST_F(TileInfershape, tile_infershape_broadcast_dims) | ||
| 81 | +{ | ||
| 82 | + gert::InfershapeContextPara infershapeContextPara( | ||
| 83 | + "Tile", | ||
| 84 | + { | ||
| 85 | + {{{3}, {3}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 86 | + {{{3}, {3}}, ge::DT_INT64, ge::FORMAT_ND}, | ||
| 87 | + }, | ||
| 88 | + { | ||
| 89 | + {{{}, {}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 90 | + }); | ||
| 91 | + std::vector<std::vector<int64_t>> expectOutputShape = { | ||
| 92 | + {-1, -1, -1}, | ||
| 93 | + }; | ||
| 94 | + ExecuteTestCase(infershapeContextPara, ge::GRAPH_SUCCESS, expectOutputShape); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +TEST_F(TileInfershape, tile_infershape_empty_multiples) | ||
| 98 | +{ | ||
| 99 | + gert::InfershapeContextPara infershapeContextPara( | ||
| 100 | + "Tile", | ||
| 101 | + { | ||
| 102 | + {{{2, 3}, {2, 3}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 103 | + {{{0}, {0}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 104 | + }, | ||
| 105 | + { | ||
| 106 | + {{{}, {}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 107 | + }); | ||
| 108 | + std::vector<std::vector<int64_t>> expectOutputShape = { | ||
| 109 | + {2, 3}, | ||
| 110 | + }; | ||
| 111 | + ExecuteTestCase(infershapeContextPara, ge::GRAPH_SUCCESS, expectOutputShape); | ||
| 112 | +} | ||
| @@ -0,0 +1,159 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +class TileTiling : public testing::Test { | ||
| 21 | +protected: | ||
| 22 | + static void SetUpTestCase() | ||
| 23 | + { | ||
| 24 | + std::cout << "TileTiling SetUp" << std::endl; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + static void TearDownTestCase() | ||
| 28 | + { | ||
| 29 | + std::cout << "TileTiling TearDown" << std::endl; | ||
| 30 | + } | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | +TEST_F(TileTiling, tile_tiling_2d_float32) | ||
| 34 | +{ | ||
| 35 | + optiling::TileCompileInfo compileInfo; | ||
| 36 | + int32_t multiplesData[] = {3, 2}; | ||
| 37 | + gert::TilingContextPara tilingContextPara( | ||
| 38 | + "Tile", | ||
| 39 | + { | ||
| 40 | + {{{2, 3}, {2, 3}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 41 | + {{{2}, {2}}, ge::DT_INT32, ge::FORMAT_ND, true, multiplesData}, | ||
| 42 | + }, | ||
| 43 | + { | ||
| 44 | + {{{6, 6}, {6, 6}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 45 | + }, | ||
| 46 | + &compileInfo); | ||
| 47 | + | ||
| 48 | + TilingInfo tilingInfo; | ||
| 49 | + bool ret = ExecuteTiling(tilingContextPara, tilingInfo); | ||
| 50 | + ASSERT_TRUE(ret); | ||
| 51 | + | ||
| 52 | + ASSERT_GE(tilingInfo.tilingDataSize, sizeof(TileTilingData)); | ||
| 53 | + const TileTilingData* tiling = reinterpret_cast<const TileTilingData*>(tilingInfo.tilingData.get()); | ||
| 54 | + EXPECT_EQ(tiling->numDims, 2); | ||
| 55 | + EXPECT_EQ(tiling->inputShape[0], 2); | ||
| 56 | + EXPECT_EQ(tiling->inputShape[1], 3); | ||
| 57 | + EXPECT_EQ(tiling->multiples[0], 3); | ||
| 58 | + EXPECT_EQ(tiling->multiples[1], 2); | ||
| 59 | + EXPECT_EQ(tiling->outputShape[0], 6); | ||
| 60 | + EXPECT_EQ(tiling->outputShape[1], 6); | ||
| 61 | + EXPECT_EQ(tiling->totalInputElems, 6); | ||
| 62 | + EXPECT_EQ(tiling->totalOutputElems, 36); | ||
| 63 | + EXPECT_EQ(tiling->elemBytes, 4); | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +TEST_F(TileTiling, tile_tiling_1d_int32) | ||
| 67 | +{ | ||
| 68 | + optiling::TileCompileInfo compileInfo; | ||
| 69 | + int32_t multiplesData[] = {8}; | ||
| 70 | + gert::TilingContextPara tilingContextPara( | ||
| 71 | + "Tile", | ||
| 72 | + { | ||
| 73 | + {{{128}, {128}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 74 | + {{{1}, {1}}, ge::DT_INT32, ge::FORMAT_ND, true, multiplesData}, | ||
| 75 | + }, | ||
| 76 | + { | ||
| 77 | + {{{1024}, {1024}}, ge::DT_INT32, ge::FORMAT_ND}, | ||
| 78 | + }, | ||
| 79 | + &compileInfo); | ||
| 80 | + | ||
| 81 | + TilingInfo tilingInfo; | ||
| 82 | + bool ret = ExecuteTiling(tilingContextPara, tilingInfo); | ||
| 83 | + ASSERT_TRUE(ret); | ||
| 84 | + | ||
| 85 | + ASSERT_GE(tilingInfo.tilingDataSize, sizeof(TileTilingData)); | ||
| 86 | + const TileTilingData* tiling = reinterpret_cast<const TileTilingData*>(tilingInfo.tilingData.get()); | ||
| 87 | + EXPECT_EQ(tiling->numDims, 1); | ||
| 88 | + EXPECT_EQ(tiling->inputShape[0], 128); | ||
| 89 | + EXPECT_EQ(tiling->multiples[0], 8); | ||
| 90 | + EXPECT_EQ(tiling->outputShape[0], 1024); | ||
| 91 | + EXPECT_EQ(tiling->totalInputElems, 128); | ||
| 92 | + EXPECT_EQ(tiling->totalOutputElems, 1024); | ||
| 93 | + EXPECT_EQ(tiling->elemBytes, 4); | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +TEST_F(TileTiling, tile_tiling_int64_multiples) | ||
| 97 | +{ | ||
| 98 | + optiling::TileCompileInfo compileInfo; | ||
| 99 | + int64_t multiplesData[] = {2, 4}; | ||
| 100 | + gert::TilingContextPara tilingContextPara( | ||
| 101 | + "Tile", | ||
| 102 | + { | ||
| 103 | + {{{3, 5}, {3, 5}}, ge::DT_FLOAT16, ge::FORMAT_ND}, | ||
| 104 | + {{{2}, {2}}, ge::DT_INT64, ge::FORMAT_ND, true, multiplesData}, | ||
| 105 | + }, | ||
| 106 | + { | ||
| 107 | + {{{6, 20}, {6, 20}}, ge::DT_FLOAT16, ge::FORMAT_ND}, | ||
| 108 | + }, | ||
| 109 | + &compileInfo); | ||
| 110 | + | ||
| 111 | + TilingInfo tilingInfo; | ||
| 112 | + bool ret = ExecuteTiling(tilingContextPara, tilingInfo); | ||
| 113 | + ASSERT_TRUE(ret); | ||
| 114 | + | ||
| 115 | + ASSERT_GE(tilingInfo.tilingDataSize, sizeof(TileTilingData)); | ||
| 116 | + const TileTilingData* tiling = reinterpret_cast<const TileTilingData*>(tilingInfo.tilingData.get()); | ||
| 117 | + EXPECT_EQ(tiling->numDims, 2); | ||
| 118 | + EXPECT_EQ(tiling->inputShape[0], 3); | ||
| 119 | + EXPECT_EQ(tiling->inputShape[1], 5); | ||
| 120 | + EXPECT_EQ(tiling->multiples[0], 2); | ||
| 121 | + EXPECT_EQ(tiling->multiples[1], 4); | ||
| 122 | + EXPECT_EQ(tiling->outputShape[0], 6); | ||
| 123 | + EXPECT_EQ(tiling->outputShape[1], 20); | ||
| 124 | + EXPECT_EQ(tiling->totalInputElems, 15); | ||
| 125 | + EXPECT_EQ(tiling->totalOutputElems, 120); | ||
| 126 | + EXPECT_EQ(tiling->elemBytes, 2); | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +TEST_F(TileTiling, tile_tiling_dim_merge) | ||
| 130 | +{ | ||
| 131 | + optiling::TileCompileInfo compileInfo; | ||
| 132 | + int32_t multiplesData[] = {3, 1, 2}; | ||
| 133 | + gert::TilingContextPara tilingContextPara( | ||
| 134 | + "Tile", | ||
| 135 | + { | ||
| 136 | + {{{2, 4, 5}, {2, 4, 5}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 137 | + {{{3}, {3}}, ge::DT_INT32, ge::FORMAT_ND, true, multiplesData}, | ||
| 138 | + }, | ||
| 139 | + { | ||
| 140 | + {{{6, 4, 10}, {6, 4, 10}}, ge::DT_FLOAT, ge::FORMAT_ND}, | ||
| 141 | + }, | ||
| 142 | + &compileInfo); | ||
| 143 | + | ||
| 144 | + TilingInfo tilingInfo; | ||
| 145 | + bool ret = ExecuteTiling(tilingContextPara, tilingInfo); | ||
| 146 | + ASSERT_TRUE(ret); | ||
| 147 | + | ||
| 148 | + ASSERT_GE(tilingInfo.tilingDataSize, sizeof(TileTilingData)); | ||
| 149 | + const TileTilingData* tiling = reinterpret_cast<const TileTilingData*>(tilingInfo.tilingData.get()); | ||
| 150 | + EXPECT_EQ(tiling->numDims, 2); | ||
| 151 | + EXPECT_EQ(tiling->inputShape[0], 8); | ||
| 152 | + EXPECT_EQ(tiling->inputShape[1], 5); | ||
| 153 | + EXPECT_EQ(tiling->multiples[0], 3); | ||
| 154 | + EXPECT_EQ(tiling->multiples[1], 2); | ||
| 155 | + EXPECT_EQ(tiling->outputShape[0], 24); | ||
| 156 | + EXPECT_EQ(tiling->outputShape[1], 10); | ||
| 157 | + EXPECT_EQ(tiling->totalInputElems, 40); | ||
| 158 | + EXPECT_EQ(tiling->totalOutputElems, 240); | ||
| 159 | +} | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +namespace optiling { | ||
| 15 | +struct TileCompileInfo {}; | ||
| 16 | +} // namespace optiling | ||
| 17 | + | ||
| 18 | + | ||
| @@ -0,0 +1,15 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +if (UT_TEST_ALL OR OP_KERNEL_UT) | ||
| 10 | + set(tile_tiling_files | ||
| 11 | + ${CMAKE_CURRENT_SOURCE_DIR}/../../../op_host/tile_tiling.cpp | ||
| 12 | + ${CMAKE_CURRENT_SOURCE_DIR}/../../../op_host/tile_infershape.cpp | ||
| 13 | + ) | ||
| 14 | + AddOpTestCase(tile "ascend910b" "-DDTYPE_X=float" "${tile_tiling_files}") | ||
| 15 | +endif() | ||
| @@ -0,0 +1,189 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +using namespace std; | ||
| 24 | + | ||
| 25 | +class TileTest : public testing::Test { | ||
| 26 | +protected: | ||
| 27 | + static void SetUpTestCase() | ||
| 28 | + { | ||
| 29 | + std::cout << "TileTest SetUp" << std::endl; | ||
| 30 | + const string cmd = "cp -rf " + dataPath + " ./"; | ||
| 31 | + system(cmd.c_str()); | ||
| 32 | + system("chmod -R 755 ./tile_data/"); | ||
| 33 | + } | ||
| 34 | + static void TearDownTestCase() | ||
| 35 | + { | ||
| 36 | + std::cout << "TileTest TearDown" << std::endl; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | +private: | ||
| 40 | + const static std::string rootPath; | ||
| 41 | + const static std::string dataPath; | ||
| 42 | +}; | ||
| 43 | + | ||
| 44 | +const std::string TileTest::rootPath = "../../../../experimental/"; | ||
| 45 | +const std::string TileTest::dataPath = rootPath + "math/tile/tests/ut/op_kernel/tile_data"; | ||
| 46 | + | ||
| 47 | +template <typename T1, typename T2> | ||
| 48 | +inline T1 CeilAlign(T1 a, T2 b) | ||
| 49 | +{ | ||
| 50 | + if (b == 0) { | ||
| 51 | + return a; | ||
| 52 | + } | ||
| 53 | + return (a + b - 1) / b * b; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +static void BuildTilingData( | ||
| 57 | + TileTilingData* tilingData, const std::vector<int32_t>& shape, const std::vector<int32_t>& multiples, | ||
| 58 | + int32_t elemBytes, int32_t blockDim, int32_t ubSize) | ||
| 59 | +{ | ||
| 60 | + memset(tilingData, 0, sizeof(TileTilingData)); | ||
| 61 | + int32_t ndim = static_cast<int32_t>(shape.size()); | ||
| 62 | + tilingData->numDims = ndim; | ||
| 63 | + tilingData->elemBytes = elemBytes; | ||
| 64 | + tilingData->blockDim = blockDim; | ||
| 65 | + tilingData->ubSize = ubSize; | ||
| 66 | + tilingData->totalInputElems = 1; | ||
| 67 | + tilingData->totalOutputElems = 1; | ||
| 68 | + for (int32_t idx = 0; idx < ndim; idx++) { | ||
| 69 | + tilingData->inputShape[idx] = shape[idx]; | ||
| 70 | + tilingData->multiples[idx] = multiples[idx]; | ||
| 71 | + tilingData->outputShape[idx] = shape[idx] * multiples[idx]; | ||
| 72 | + tilingData->totalInputElems *= shape[idx]; | ||
| 73 | + tilingData->totalOutputElems *= tilingData->outputShape[idx]; | ||
| 74 | + } | ||
| 75 | + tilingData->inputStrides[ndim - 1] = 1; | ||
| 76 | + tilingData->outputStrides[ndim - 1] = 1; | ||
| 77 | + for (int32_t idx = ndim - 2; idx >= 0; idx--) { | ||
| 78 | + tilingData->inputStrides[idx] = tilingData->inputStrides[idx + 1] * tilingData->inputShape[idx + 1]; | ||
| 79 | + tilingData->outputStrides[idx] = tilingData->outputStrides[idx + 1] * tilingData->outputShape[idx + 1]; | ||
| 80 | + } | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +TEST_F(TileTest, test_case_float32_2d) | ||
| 84 | +{ | ||
| 85 | + std::vector<int32_t> shape = {2, 3}; | ||
| 86 | + std::vector<int32_t> mult = {3, 2}; | ||
| 87 | + | ||
| 88 | + uint8_t* tiling = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(sizeof(TileTilingData), 32))); | ||
| 89 | + TileTilingData* tilingData = reinterpret_cast<TileTilingData*>(tiling); | ||
| 90 | + BuildTilingData(tilingData, shape, mult, 4, 1, 87381); | ||
| 91 | + | ||
| 92 | + system("cd ./tile_data/ && python3 gen_data.py '2,3' '3,2' float32"); | ||
| 93 | + uint32_t inputCount = static_cast<uint32_t>(tilingData->totalInputElems); | ||
| 94 | + uint32_t outputCount = static_cast<uint32_t>(tilingData->totalOutputElems); | ||
| 95 | + size_t inputByteSize = inputCount * sizeof(float); | ||
| 96 | + size_t outputByteSize = outputCount * sizeof(float); | ||
| 97 | + | ||
| 98 | + uint8_t* x = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(inputByteSize + 32, 32))); | ||
| 99 | + ReadFile("./tile_data/float32_input_t_tile.bin", inputByteSize, x, inputByteSize); | ||
| 100 | + uint8_t* y = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(outputByteSize, 32))); | ||
| 101 | + uint8_t* multGm = static_cast<uint8_t*>(AscendC::GmAlloc(32)); | ||
| 102 | + uint8_t* workspace = static_cast<uint8_t*>(AscendC::GmAlloc(32 * 1024 * 1024)); | ||
| 103 | + | ||
| 104 | + AscendC::SetKernelMode(KernelMode::AIV_MODE); | ||
| 105 | + auto func = tile<TILE_TPL_SCH_MODE_DEFAULT>; | ||
| 106 | + ICPU_RUN_KF(func, tilingData->blockDim, x, multGm, y, workspace, tiling); | ||
| 107 | + | ||
| 108 | + WriteFile("./tile_data/float32_output_t_tile.bin", y, outputByteSize); | ||
| 109 | + | ||
| 110 | + AscendC::GmFree(static_cast<void*>(x)); | ||
| 111 | + AscendC::GmFree(static_cast<void*>(y)); | ||
| 112 | + AscendC::GmFree(static_cast<void*>(multGm)); | ||
| 113 | + AscendC::GmFree(static_cast<void*>(workspace)); | ||
| 114 | + AscendC::GmFree(static_cast<void*>(tiling)); | ||
| 115 | + | ||
| 116 | + system("cd ./tile_data/ && python3 compare_data.py float32_golden_t_tile.bin float32_output_t_tile.bin float32"); | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +TEST_F(TileTest, test_case_int32_1d) | ||
| 120 | +{ | ||
| 121 | + std::vector<int32_t> shape = {128}; | ||
| 122 | + std::vector<int32_t> mult = {8}; | ||
| 123 | + | ||
| 124 | + uint8_t* tiling = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(sizeof(TileTilingData), 32))); | ||
| 125 | + TileTilingData* tilingData = reinterpret_cast<TileTilingData*>(tiling); | ||
| 126 | + BuildTilingData(tilingData, shape, mult, 4, 1, 87381); | ||
| 127 | + | ||
| 128 | + system("cd ./tile_data/ && python3 gen_data.py '128' '8' int32"); | ||
| 129 | + uint32_t inputCount = static_cast<uint32_t>(tilingData->totalInputElems); | ||
| 130 | + uint32_t outputCount = static_cast<uint32_t>(tilingData->totalOutputElems); | ||
| 131 | + size_t inputByteSize = inputCount * sizeof(int32_t); | ||
| 132 | + size_t outputByteSize = outputCount * sizeof(int32_t); | ||
| 133 | + | ||
| 134 | + uint8_t* x = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(inputByteSize + 32, 32))); | ||
| 135 | + ReadFile("./tile_data/int32_input_t_tile.bin", inputByteSize, x, inputByteSize); | ||
| 136 | + uint8_t* y = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(outputByteSize, 32))); | ||
| 137 | + uint8_t* multGm = static_cast<uint8_t*>(AscendC::GmAlloc(32)); | ||
| 138 | + uint8_t* workspace = static_cast<uint8_t*>(AscendC::GmAlloc(32 * 1024 * 1024)); | ||
| 139 | + | ||
| 140 | + AscendC::SetKernelMode(KernelMode::AIV_MODE); | ||
| 141 | + auto func = tile<TILE_TPL_SCH_MODE_DEFAULT>; | ||
| 142 | + ICPU_RUN_KF(func, tilingData->blockDim, x, multGm, y, workspace, tiling); | ||
| 143 | + | ||
| 144 | + WriteFile("./tile_data/int32_output_t_tile.bin", y, outputByteSize); | ||
| 145 | + | ||
| 146 | + AscendC::GmFree(static_cast<void*>(x)); | ||
| 147 | + AscendC::GmFree(static_cast<void*>(y)); | ||
| 148 | + AscendC::GmFree(static_cast<void*>(multGm)); | ||
| 149 | + AscendC::GmFree(static_cast<void*>(workspace)); | ||
| 150 | + AscendC::GmFree(static_cast<void*>(tiling)); | ||
| 151 | + | ||
| 152 | + system("cd ./tile_data/ && python3 compare_data.py int32_golden_t_tile.bin int32_output_t_tile.bin int32"); | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +TEST_F(TileTest, test_case_float32_large_inner) | ||
| 156 | +{ | ||
| 157 | + std::vector<int32_t> shape = {1, 1, 25000}; | ||
| 158 | + std::vector<int32_t> mult = {3, 3, 12}; | ||
| 159 | + | ||
| 160 | + uint8_t* tiling = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(sizeof(TileTilingData), 32))); | ||
| 161 | + TileTilingData* tilingData = reinterpret_cast<TileTilingData*>(tiling); | ||
| 162 | + BuildTilingData(tilingData, shape, mult, 4, 1, 87381); | ||
| 163 | + | ||
| 164 | + system("cd ./tile_data/ && python3 gen_data.py '1,1,25000' '3,3,12' float32"); | ||
| 165 | + uint32_t inputCount = static_cast<uint32_t>(tilingData->totalInputElems); | ||
| 166 | + uint32_t outputCount = static_cast<uint32_t>(tilingData->totalOutputElems); | ||
| 167 | + size_t inputByteSize = inputCount * sizeof(float); | ||
| 168 | + size_t outputByteSize = outputCount * sizeof(float); | ||
| 169 | + | ||
| 170 | + uint8_t* x = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(inputByteSize + 32, 32))); | ||
| 171 | + ReadFile("./tile_data/float32_input_t_tile.bin", inputByteSize, x, inputByteSize); | ||
| 172 | + uint8_t* y = static_cast<uint8_t*>(AscendC::GmAlloc(CeilAlign(outputByteSize, 32))); | ||
| 173 | + uint8_t* multGm = static_cast<uint8_t*>(AscendC::GmAlloc(32)); | ||
| 174 | + uint8_t* workspace = static_cast<uint8_t*>(AscendC::GmAlloc(32 * 1024 * 1024)); | ||
| 175 | + | ||
| 176 | + AscendC::SetKernelMode(KernelMode::AIV_MODE); | ||
| 177 | + auto func = tile<TILE_TPL_SCH_MODE_DEFAULT>; | ||
| 178 | + ICPU_RUN_KF(func, tilingData->blockDim, x, multGm, y, workspace, tiling); | ||
| 179 | + | ||
| 180 | + WriteFile("./tile_data/float32_output_t_tile.bin", y, outputByteSize); | ||
| 181 | + | ||
| 182 | + AscendC::GmFree(static_cast<void*>(x)); | ||
| 183 | + AscendC::GmFree(static_cast<void*>(y)); | ||
| 184 | + AscendC::GmFree(static_cast<void*>(multGm)); | ||
| 185 | + AscendC::GmFree(static_cast<void*>(workspace)); | ||
| 186 | + AscendC::GmFree(static_cast<void*>(tiling)); | ||
| 187 | + | ||
| 188 | + system("cd ./tile_data/ && python3 compare_data.py float32_golden_t_tile.bin float32_output_t_tile.bin float32"); | ||
| 189 | +} | ||
| @@ -0,0 +1,46 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: utf-8 -*- | ||
| 3 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 5 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 6 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 7 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 8 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 10 | + | ||
| 11 | +import sys | ||
| 12 | +import numpy as np | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +def compare_data(golden_file, output_file, d_type="float32"): | ||
| 16 | + d_type_dict = { | ||
| 17 | + "float32": np.float32, | ||
| 18 | + "float16": np.float16, | ||
| 19 | + "int32": np.int32, | ||
| 20 | + "int8": np.int8, | ||
| 21 | + "uint8": np.uint8, | ||
| 22 | + } | ||
| 23 | + np_type = d_type_dict.get(d_type, np.float32) | ||
| 24 | + golden = np.fromfile(golden_file, dtype=np_type) | ||
| 25 | + output = np.fromfile(output_file, dtype=np_type) | ||
| 26 | + | ||
| 27 | + if golden.shape != output.shape: | ||
| 28 | + print(f"FAIL: shape mismatch golden={golden.shape} output={output.shape}") | ||
| 29 | + return False | ||
| 30 | + | ||
| 31 | + if np.issubdtype(np_type, np.integer): | ||
| 32 | + match = np.array_equal(golden, output) | ||
| 33 | + else: | ||
| 34 | + match = np.allclose(golden, output, atol=1e-4, rtol=1e-4) | ||
| 35 | + | ||
| 36 | + max_diff = np.max(np.abs(golden.astype(np.float64) - output.astype(np.float64))) | ||
| 37 | + print(f"{'PASS' if match else 'FAIL'}: max_diff={max_diff}") | ||
| 38 | + return match | ||
| 39 | + | ||
| 40 | + | ||
| 41 | +if __name__ == "__main__": | ||
| 42 | + if len(sys.argv) != 4: | ||
| 43 | + print("Usage: compare_data.py <golden_file> <output_file> <dtype>") | ||
| 44 | + exit(1) | ||
| 45 | + ok = compare_data(sys.argv[1], sys.argv[2], sys.argv[3]) | ||
| 46 | + exit(0 if ok else 1) | ||
| @@ -0,0 +1,52 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: utf-8 -*- | ||
| 3 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 5 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 6 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 7 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 8 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 10 | + | ||
| 11 | +import sys | ||
| 12 | +import os | ||
| 13 | +import numpy as np | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +def parse_str_to_shape_list(shape_str): | ||
| 17 | + shape_str = shape_str.strip('(').strip(')') | ||
| 18 | + shape_list = [int(x) for x in shape_str.split(",")] | ||
| 19 | + return np.array(shape_list) | ||
| 20 | + | ||
| 21 | + | ||
| 22 | +def gen_data_and_golden(shape_str, multiples_str, d_type="float32"): | ||
| 23 | + d_type_dict = { | ||
| 24 | + "float32": np.float32, | ||
| 25 | + "float16": np.float16, | ||
| 26 | + "int32": np.int32, | ||
| 27 | + "int8": np.int8, | ||
| 28 | + "uint8": np.uint8, | ||
| 29 | + } | ||
| 30 | + np_type = d_type_dict.get(d_type, np.float32) | ||
| 31 | + shape = parse_str_to_shape_list(shape_str) | ||
| 32 | + multiples = parse_str_to_shape_list(multiples_str) | ||
| 33 | + | ||
| 34 | + np.random.seed(42) | ||
| 35 | + if np.issubdtype(np_type, np.integer): | ||
| 36 | + tmp_input = np.random.randint(0, 100, size=shape).astype(np_type) | ||
| 37 | + else: | ||
| 38 | + tmp_input = np.random.randn(*shape).astype(np_type) | ||
| 39 | + | ||
| 40 | + tmp_golden = np.tile(tmp_input, multiples) | ||
| 41 | + | ||
| 42 | + tmp_input.tofile(f"{d_type}_input_t_tile.bin") | ||
| 43 | + tmp_golden.tofile(f"{d_type}_golden_t_tile.bin") | ||
| 44 | + | ||
| 45 | + | ||
| 46 | +if __name__ == "__main__": | ||
| 47 | + if len(sys.argv) != 4: | ||
| 48 | + print("Usage: gen_data.py <shape> <multiples> <dtype>") | ||
| 49 | + print("Example: gen_data.py '2,3' '3,2' float32") | ||
| 50 | + exit(1) | ||
| 51 | + os.system("rm -rf *.bin") | ||
| 52 | + gen_data_and_golden(sys.argv[1], sys.argv[2], sys.argv[3]) | ||
| @@ -0,0 +1,56 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +/*! | ||
| 12 | + * \file tile_tiling.h | ||
| 13 | + * \brief | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +using namespace TileKernel; | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +inline [aicore] void InitTilingData(const __gm__ uint8_t* tiling, TileTilingData* constData) { | ||
| 28 | + const __gm__ uint32_t* src = (const __gm__ uint32_t*)tiling; | ||
| 29 | + uint32_t* dst = (uint32_t*)constData; | ||
| 30 | + for (size_t i = 0; i < sizeof(TileTilingData) / 4; i++) { | ||
| 31 | + *(dst + i) = *(src + i); | ||
| 32 | + } | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +inline void InitTilingData(uint8_t* tiling, TileTilingData* constData) | ||
| 36 | +{ | ||
| 37 | + memcpy(constData, tiling, sizeof(TileTilingData)); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | + __ubuf__ tilingStruct* tilingDataPointer = \ | ||
| 43 | + reinterpret_cast<__ubuf__ tilingStruct*>((__ubuf__ uint8_t*)(tilingPointer)) | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + CONVERT_TILING_DATA(tilingStruct, tilingDataPointer, tilingPointer) | ||
| 47 | + | ||
| 48 | + | ||
| 49 | + tilingStruct tilingData; \ | ||
| 50 | + InitTilingData(tilingArg, &tilingData) | ||
| 51 | + | ||
| 52 | + | ||
| 53 | + TileTilingData tilingData; \ | ||
| 54 | + InitTilingData(tilingArg, &tilingData) | ||
| 55 | + | ||
| 56 | + | ||


这里写的int64、double数据类型不支持,和aclnn_repeat.cpp文件中DTYPE_SUPPORT_内容不一致