已合并
Add Ascend950 Basic SYRK example #1195
void_ptr创建于 8月28日
Add Ascend950 Basic SYRK example #1195
已合并
共 18 个文件变更+566-0
| @@ -0,0 +1,14 @@ | |||
| 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 | +set_source_files_properties(basic_syrk_tla.cpp PROPERTIES LANGUAGE ASC) | ||
| 12 | +catlass_example_add_executable(82_ascend950_basic_syrk cube basic_syrk_tla.cpp) | ||
| 13 | +target_compile_definitions(82_ascend950_basic_syrk PRIVATE L2_CACHE_HINT) | ||
| 14 | +target_link_libraries(82_ascend950_basic_syrk PRIVATE m) | ||
| @@ -0,0 +1,91 @@ | |||
| 1 | +# Ascend950 Basic SYRK Example Readme | ||
| 2 | + | ||
| 3 | +## 代码组织 | ||
| 4 | + | ||
| 5 | +```text | ||
| 6 | +├── 82_ascend950_basic_syrk | ||
| 7 | +│ ├── CMakeLists.txt # CMake 编译文件 | ||
| 8 | +│ ├── README.md | ||
| 9 | +│ └── basic_syrk_tla.cpp # 主文件(host 数据生成、kernel 调度、精度校验) | ||
| 10 | +``` | ||
| 11 | + | ||
| 12 | +## 使用示例 | ||
| 13 | + | ||
| 14 | +1. 编译样例(Ascend950 需指定 `CATLASS_ARCH=3510`),可参考[快速入门](../../docs/zh/1_Practice/01_quick_start.md#编译执行): | ||
| 15 | + | ||
| 16 | + ```bash | ||
| 17 | + bash scripts/build.sh -DCATLASS_ARCH=3510 82_ascend950_basic_syrk | ||
| 18 | + ``` | ||
| 19 | + | ||
| 20 | +2. 切换到可执行文件目录并运行。测试数据随机生成,尺寸由命令行传入: | ||
| 21 | + | ||
| 22 | + ```bash | ||
| 23 | + cd output/bin | ||
| 24 | + # 可执行文件名 | m | k | deviceId(可选,默认0) | ||
| 25 | + ./82_ascend950_basic_syrk 1024 1024 0 | ||
| 26 | + ``` | ||
| 27 | + | ||
| 28 | + - `1024`:矩阵 m 轴($X$ 行数 / $Y$ 边长) | ||
| 29 | + - `1024`:k 轴($X$ 列数) | ||
| 30 | + - `0`:Device ID | ||
| 31 | + | ||
| 32 | +3. 执行成功输出: | ||
| 33 | + | ||
| 34 | + ```text | ||
| 35 | + Compare success. | ||
| 36 | + ``` | ||
| 37 | + | ||
| 38 | +## 功能说明 | ||
| 39 | + | ||
| 40 | +- 算子功能:完成对称秩更新(SYRK),对输入矩阵 $X$ 做自乘,得到对称结果矩阵 $Y$。 | ||
| 41 | +- 计算公式: | ||
| 42 | + | ||
| 43 | + $$ | ||
| 44 | + Y = X \cdot X^{T} | ||
| 45 | + $$ | ||
| 46 | + | ||
| 47 | + 其中 $X$ 形如 `(M, K)`,$Y$ 形如 `(M, M)` 且满足 $Y = Y^{T}$。 | ||
| 48 | + | ||
| 49 | +- 本样例面向 Ascend950,在 Basic Matmul 路径上复用 L1/L0 pingpong,并通过 swizzle 调度只计算下三角 Block,再利用 nz2nd / nz2dn 双写补全上三角。 | ||
| 50 | + | ||
| 51 | +## 参数说明 | ||
| 52 | + | ||
| 53 | +本样例使用 `SyrkOptions`(定义于 `examples/common/options.hpp`),命令行参数为 `m k [device_id]`: | ||
| 54 | + | ||
| 55 | +| 参数名 | 描述 | 约束 | | ||
| 56 | +| ---------- | ----------------------------------------- | ---------------------------- | | ||
| 57 | +| `m` | 输入矩阵 $X$ 的行数(也即输出 $Y$ 的边长) | 正整数 | | ||
| 58 | +| `k` | 输入矩阵 $X$ 的列数 | 正整数 | | ||
| 59 | +| `deviceId` | 使用的 NPU 卡 ID(默认 0) | 在设备 NPU 有效范围内 | | ||
| 60 | + | ||
| 61 | +`SyrkOptions::Parse` 会将 problem shape 设为 `(m, m, k)`。 | ||
| 62 | + | ||
| 63 | +## 算子支持范围 | ||
| 64 | + | ||
| 65 | +| 参数 | 输入 / 输出 | 数据类型 | 维度 | 数据排布 | | ||
| 66 | +| --- | --- | --- | --- | --- | | ||
| 67 | +| X | 输入 | `bfloat16`,`float16` | `[M, K]` | `layout::RowMajor` | | ||
| 68 | +| Y | 输出 | `bfloat16`,`float16` | `[M, M]` | `layout::RowMajor` | | ||
| 69 | + | ||
| 70 | +### 关键模板参数(本样例当前固定值) | ||
使用optest+atk进行测试维护时会参考泛化的dtype和layout,所以这里建议写明整个样例内可泛化的范围,不只是说明示例代码内的dtype/layout取值。 ![]() ![]() | |||
| 71 | + | ||
| 72 | +| 模板参数 | 说明 | 本样例取值 | | ||
| 73 | +| ---------- | ------------------ | --------------------- | | ||
| 74 | +| `ElementX` | 输入矩阵数据类型 | `bfloat16_t` | | ||
| 75 | +| `ElementY` | 输出矩阵数据类型 | `bfloat16_t` | | ||
| 76 | +| `LayoutX` | 输入 $X$ 排布 | `layout::RowMajor` | | ||
| 77 | +| `LayoutXt` | $X^{T}$ 视图排布 | `layout::ColumnMajor` | | ||
| 78 | +| `LayoutY` | 输出 $Y$ 排布 | `layout::RowMajor` | | ||
| 79 | + | ||
| 80 | +## 实现方案 | ||
| 81 | + | ||
| 82 | +算子整体沿用 Ascend950 Basic Matmul,并采用 `GemmIdentityBlockSwizzle` 调度。由于 $Y$ 为对称阵,只需计算下三角(含对角)基本块,规则如下: | ||
| 83 | + | ||
| 84 | +1. `blockCoord.m() < blockCoord.n()` 时,跳过该基本块。 | ||
| 85 | +2. `blockCoord.m() == blockCoord.n()` 时,计算后仅用 nz2nd 写入对角位置一次。 | ||
| 86 | +3. `blockCoord.m() > blockCoord.n()` 时,计算后双写: | ||
| 87 | + | ||
| 88 | + - 使用 nz2nd 写入 `(blockCoord.m(), blockCoord.n())`; | ||
| 89 | + - 使用 nz2dn(转置)写入 `(blockCoord.n(), blockCoord.m())`。 | ||
| 90 | + | ||
| 91 | +L0C→GM 双写路径使用 `M_FIX` 同步,不启用 unitFlag(一次 mmad 的 unitFlag 只能配对一次 Fixpipe)。 | ||
| @@ -0,0 +1,137 @@ | |||
| 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 | +using namespace Catlass; | ||
| 30 | +using namespace tla; | ||
| 31 | + | ||
| 32 | +using Options = SyrkOptions; | ||
| 33 | + | ||
| 34 | +static void Run(const Options& options) | ||
| 35 | +{ | ||
| 36 | + aclrtStream stream{nullptr}; | ||
| 37 | + | ||
| 38 | + ACL_CHECK(aclInit(nullptr)); | ||
| 39 | + ACL_CHECK(aclrtSetDevice(options.deviceId)); | ||
| 40 | + ACL_CHECK(aclrtCreateStream(&stream)); | ||
| 41 | + | ||
| 42 | + // Y = X * X^T, X: [M, K], Y: [M, M] | ||
| 43 | + uint32_t m = options.problemShape.m(); | ||
| 44 | + uint32_t k = options.problemShape.k(); | ||
| 45 | + | ||
| 46 | + using ElementX = bfloat16_t; | ||
| 47 | + using ElementY = bfloat16_t; | ||
| 48 | + | ||
| 49 | + // Host-side tags for golden only; device layouts are fixed inside BlockMmadSyrkTla. | ||
| 50 | + using LayoutTagX = layout::RowMajor; | ||
| 51 | + using LayoutTagXt = layout::ColumnMajor; | ||
| 52 | + using LayoutTagY = layout::RowMajor; | ||
| 53 | + | ||
| 54 | + LayoutTagX tagX = LayoutTagX::MakeLayout<ElementX>(m, k); | ||
| 55 | + LayoutTagXt tagXt = LayoutTagXt::MakeLayout<ElementX>(k, m); | ||
| 56 | + LayoutTagY tagY = LayoutTagY::MakeLayout<ElementY>(m, m); | ||
| 57 | + | ||
| 58 | + size_t lenX = tagX.Capacity(); | ||
| 59 | + size_t lenY = tagY.Capacity(); | ||
| 60 | + | ||
| 61 | + size_t sizeX = lenX * sizeof(ElementX); | ||
| 62 | + size_t sizeY = lenY * sizeof(ElementY); | ||
| 63 | + | ||
| 64 | + std::vector<bfloat16> hostX(lenX); | ||
| 65 | + golden::FillRandomData<bfloat16>(hostX, -5.0f, 5.0f); | ||
| 66 | + | ||
| 67 | + uint8_t* deviceX{nullptr}; | ||
| 68 | + ACL_CHECK(aclrtMalloc(reinterpret_cast<void**>(&deviceX), sizeX, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 69 | + ACL_CHECK(aclrtMemcpy(deviceX, sizeX, hostX.data(), sizeX, ACL_MEMCPY_HOST_TO_DEVICE)); | ||
| 70 | + | ||
| 71 | + uint8_t* deviceY{nullptr}; | ||
| 72 | + ACL_CHECK(aclrtMalloc(reinterpret_cast<void**>(&deviceY), sizeY, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 73 | + | ||
| 74 | + uint8_t* deviceWorkspace{nullptr}; | ||
| 75 | + | ||
| 76 | + auto aicCoreNum = platform_ascendc::PlatformAscendCManager::GetInstance()->GetCoreNumAic(); | ||
| 77 | + | ||
| 78 | + using L1TileShape = Shape<Int<256>, Int<256>, Int<128>>; | ||
| 79 | + using L0TileShape = Shape<Int<256>, Int<256>, Int<64>>; | ||
| 80 | + | ||
| 81 | + // Layout tags are fixed by BlockMmadSyrkTla; use the default TileCopy. | ||
| 82 | + using BlockMmad = Gemm::Block::BlockMmadSyrkTla<L1TileShape, L0TileShape, ElementX, ElementY>; | ||
| 83 | + using BlockEpilogue = void; | ||
| 84 | + | ||
| 85 | + uint32_t taskNum = CeilDiv(m, tla::get<0>(L1TileShape{})) * CeilDiv(m, tla::get<1>(L1TileShape{})); | ||
| 86 | + uint32_t aicCoreUsed = min(aicCoreNum, taskNum); | ||
| 87 | + | ||
| 88 | + // Swizzle offset is 3 and direction is 1. | ||
| 89 | + using BlockScheduler = typename Gemm::Block::GemmIdentityBlockSwizzle<3, 1>; | ||
| 90 | + using MatmulKernel = Gemm::Kernel::BasicSyrkTla<BlockMmad, BlockEpilogue, BlockScheduler>; | ||
| 91 | + using MatmulAdapter = Gemm::Device::DeviceGemm<MatmulKernel>; | ||
| 92 | + | ||
| 93 | + MatmulKernel::Arguments arguments{options.problemShape, deviceX, deviceY}; | ||
| 94 | + | ||
| 95 | + MatmulAdapter matmulOp; | ||
| 96 | + matmulOp.CanImplement(arguments); | ||
| 97 | + size_t sizeWorkspace = matmulOp.GetWorkspaceSize(arguments); | ||
| 98 | + if (sizeWorkspace > 0) { | ||
| 99 | + ACL_CHECK(aclrtMalloc(reinterpret_cast<void**>(&deviceWorkspace), sizeWorkspace, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 100 | + } | ||
| 101 | + matmulOp.Initialize(arguments, deviceWorkspace); | ||
| 102 | + matmulOp(stream, aicCoreUsed); | ||
| 103 | + ACL_CHECK(aclrtSynchronizeStream(stream)); | ||
| 104 | + | ||
| 105 | + std::vector<bfloat16> hostY(lenY); | ||
| 106 | + ACL_CHECK(aclrtMemcpy(hostY.data(), sizeY, deviceY, sizeY, ACL_MEMCPY_DEVICE_TO_HOST)); | ||
| 107 | + | ||
| 108 | + std::vector<float> hostGolden(lenY); | ||
| 109 | + golden::ComputeMatmul(options.problemShape, hostX, tagX, hostX, tagXt, hostGolden, tagY); | ||
| 110 | + | ||
| 111 | + std::vector<uint64_t> errorIndices = golden::CompareData(hostY, hostGolden, k); | ||
| 112 | + if (errorIndices.empty()) { | ||
| 113 | + std::cout << "Compare success." << std::endl; | ||
| 114 | + } else { | ||
| 115 | + std::cerr << "Compare failed. Error count: " << errorIndices.size() << std::endl; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + ACL_CHECK(aclrtFree(deviceX)); | ||
| 119 | + ACL_CHECK(aclrtFree(deviceY)); | ||
| 120 | + if (sizeWorkspace > 0) { | ||
| 121 | + ACL_CHECK(aclrtFree(deviceWorkspace)); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + ACL_CHECK(aclrtDestroyStream(stream)); | ||
| 125 | + ACL_CHECK(aclrtResetDevice(options.deviceId)); | ||
| 126 | + ACL_CHECK(aclFinalize()); | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +int main(int argc, const char** argv) | ||
| 130 | +{ | ||
| 131 | + Options options; | ||
| 132 | + if (options.Parse(argc, argv) != 0) { | ||
| 133 | + return -1; | ||
| 134 | + } | ||
| 135 | + Run(options); | ||
| 136 | + return 0; | ||
| 137 | +} | ||
| @@ -179,6 +179,7 @@ set(EXAMPLE_ASCEND950 | |||
| 179 | 74_ascend950_weight_quant_a8w4_grouped_mx_matmul | 179 | 74_ascend950_weight_quant_a8w4_grouped_mx_matmul |
| 180 | 80_ascend950_grouped_matmul_slice_m_gelu | 180 | 80_ascend950_grouped_matmul_slice_m_gelu |
| 181 | 81_ascend950_rain_fusion_attention | 181 | 81_ascend950_rain_fusion_attention |
| 182 | + 82_ascend950_basic_syrk | ||
| 182 | ) | 183 | ) |
| 183 | 184 | ||
| 184 | if(CATLASS_ARCH STREQUAL "2201") | 185 | if(CATLASS_ARCH STREQUAL "2201") |
| @@ -202,4 +202,43 @@ struct TrmmOptions { | |||
| 202 | } | 202 | } |
| 203 | }; | 203 | }; |
| 204 | 204 | ||
| 205 | +/** | ||
| 206 | + * @struct SyrkOptions | ||
| 207 | + * @brief Options structuture for syrk examples. | ||
| 208 | + * @brief Arguments: `example_name m k [device_id]` | ||
| 209 | + */ | ||
| 210 | +struct SyrkOptions { | ||
| 211 | + const std::string HELPER = "m k [device_id]"; | ||
| 212 | + | ||
| 213 | + Catlass::GemmCoord problemShape{128, 128, 128}; | ||
| 214 | + int32_t deviceId{0}; | ||
| 215 | + | ||
| 216 | + SyrkOptions() = default; | ||
| 217 | + | ||
| 218 | + int Parse(int argc, const char** argv) | ||
| 219 | + { | ||
| 220 | + enum class ArgsIndex | ||
| 221 | + { | ||
| 222 | + M_INDEX = 1, | ||
| 223 | + K_INDEX, | ||
| 224 | + DEVICE_ID_INDEX, | ||
| 225 | + ARGS_MAX | ||
| 226 | + }; | ||
| 227 | + | ||
| 228 | + if (argc > static_cast<uint32_t>(ArgsIndex::ARGS_MAX) || | ||
| 229 | + argc < static_cast<uint32_t>(ArgsIndex::DEVICE_ID_INDEX)) { | ||
| 230 | + std::cerr << TOSTRING(CATLASS_EXAMPLE_NAME) << " " << HELPER << std::endl; | ||
| 231 | + return -1; | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + problemShape.m() = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::M_INDEX)]); | ||
| 235 | + problemShape.n() = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::M_INDEX)]); | ||
| 236 | + problemShape.k() = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::K_INDEX)]); | ||
| 237 | + if (argc == static_cast<uint32_t>(ArgsIndex::ARGS_MAX)) { | ||
| 238 | + deviceId = std::atoi(argv[static_cast<uint32_t>(ArgsIndex::DEVICE_ID_INDEX)]); | ||
| 239 | + } | ||
| 240 | + return 0; | ||
| 241 | + } | ||
| 242 | +}; | ||
| 243 | + | ||
| 205 | 244 | ||
| @@ -83,6 +83,7 @@ Python API (`torch_catlass.ops.*`) | |||
| 83 | - [x] 67_ascend950_batched_matmul (Ascend950) | 83 | - [x] 67_ascend950_batched_matmul (Ascend950) |
| 84 | - [x] 73_ascend950_matmul_full_loadA (Ascend950) | 84 | - [x] 73_ascend950_matmul_full_loadA (Ascend950) |
| 85 | - [x] 74_ascend950_weight_quant_a8w4_grouped_mx_matmul (Ascend950) | 85 | - [x] 74_ascend950_weight_quant_a8w4_grouped_mx_matmul (Ascend950) |
| 86 | +- [x] 82_ascend950_basic_syrk (Ascend950) | ||
| 86 | 87 | ||
| 87 | ### 暂未接入 | 88 | ### 暂未接入 |
| 88 | 89 | ||
| @@ -516,6 +516,15 @@ void Ascend950MatmulFullLoadA( | |||
| 516 | */ | 516 | */ |
| 517 | void Symm(const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const SymmParams& params); | 517 | void Symm(const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const SymmParams& params); |
| 518 | 518 | ||
| 519 | +/** | ||
| 520 | + * @brief JIT interface for example 82_ascend950_basic_syrk. | ||
| 521 | + * | ||
| 522 | + * Y = X * X^T. ``params.m`` must equal ``params.n``; only ``inputAddr[0]`` (X) and | ||
| 523 | + * ``outputAddr[0]`` (Y) are consumed. | ||
| 524 | + */ | ||
| 525 | +void Ascend950BasicSyrk( | ||
| 526 | + const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const MatmulParams& params); | ||
| 527 | + | ||
| 519 | /** | 528 | /** |
| 520 | * @brief Reserved JIT interface for example 74_ascend950_weight_quant_a8w4_grouped_mx_matmul. | 529 | * @brief Reserved JIT interface for example 74_ascend950_weight_quant_a8w4_grouped_mx_matmul. |
| 521 | * | 530 | * |
| @@ -0,0 +1,15 @@ | |||
| 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 | +add_kernel(NAME ascend950_basic_syrk | ||
| 12 | + NPU_ARCH_LIST 3510 | ||
| 13 | + KERNEL_TYPE jit | ||
| 14 | + ${CMAKE_CURRENT_SOURCE_DIR}/ascend950_basic_syrk.cpp | ||
| 15 | + TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/ascend950_basic_syrk_impl.cpp) | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | +/** | ||
| 2 | + * This program is free software, you can redistribute it and/or modify. | ||
| 3 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | + * This file is a part of the CANN Open Software. | ||
| 5 | + * Licensed under 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, INCLUDING | ||
| 8 | + * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of | ||
| 9 | + * the software repository for the full text of the License. | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace CatlassKernel { | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * @brief example 82_ascend950_basic_syrk: Resolve and launch JIT BasicSyrkTla. | ||
| 20 | + * | ||
| 21 | + * Y = X * X^T. Runtime params reuse MatmulParams with m == n (output is square). | ||
| 22 | + */ | ||
| 23 | +extern "C" void Ascend950BasicSyrk( | ||
| 24 | + const uint32_t blockNum, aclrtStream stream, const TParams& tParams, const MatmulParams& params) | ||
| 25 | +{ | ||
| 26 | + auto macros = JitMacroGenerator<TParams>::generate("ascend950_basic_syrk", tParams); | ||
| 27 | + // Match example: GemmIdentityBlockSwizzle<3, 1> | ||
| 28 | + macros["CATLASS_JIT_BLOCK_SCHEDULER"] = "31"; | ||
| 29 | + auto* entry = | ||
| 30 | + JitCompiler::instance().getKernel("ascend950_basic_syrk_impl.cpp", macros, JitKernelType::AIC); | ||
| 31 | + if (entry) { | ||
| 32 | + entry(blockNum, stream, ¶ms); | ||
| 33 | + } | ||
| 34 | + aclrtSynchronizeStream(stream); | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +} // namespace CatlassKernel | ||
| @@ -0,0 +1,65 @@ | |||
| 1 | +/** | ||
| 2 | + * This program is free software, you can redistribute it and/or modify. | ||
| 3 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | + * This file is a part of the CANN Open Software. | ||
| 5 | + * Licensed under 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, INCLUDING | ||
| 8 | + * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of | ||
| 9 | + * the software repository for the full text of the License. | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +// Device/kernel types are fixed by BlockMmadSyrkTla; only element dtypes are JIT-configurable. | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +using namespace Catlass; | ||
| 39 | +using namespace tla; | ||
| 40 | + | ||
| 41 | +using ElementX = CATLASS_JIT_ELEMENT_A; | ||
| 42 | +using ElementY = CATLASS_JIT_ELEMENT_C; | ||
| 43 | + | ||
| 44 | +using L1TileShape = tuple<C<256>, C<256>, C<128>>; | ||
| 45 | +using L0TileShape = tuple<C<256>, C<256>, C<64>>; | ||
| 46 | + | ||
| 47 | +using BlockMmad = Gemm::Block::BlockMmadSyrkTla<L1TileShape, L0TileShape, ElementX, ElementY>; | ||
| 48 | +using BlockEpilogue = void; | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + | ||
| 53 | +using BlockScheduler = typename Gemm::Block::GemmIdentityBlockSwizzle< | ||
| 54 | + (CATLASS_JIT_BLOCK_SCHEDULER / 10), (CATLASS_JIT_BLOCK_SCHEDULER % 10)>; | ||
| 55 | + | ||
| 56 | +using MatmulKernel = Gemm::Kernel::BasicSyrkTla<BlockMmad, BlockEpilogue, BlockScheduler>; | ||
| 57 | + | ||
| 58 | +extern "C" void run(uint32_t blockNum, aclrtStream stream, const CatlassKernel::MatmulParams* params) | ||
| 59 | +{ | ||
| 60 | + typename MatmulKernel::Arguments arguments{ | ||
| 61 | + GemmCoord{params->m, params->n, params->k}, | ||
| 62 | + params->inputAddr[0], | ||
| 63 | + params->outputAddr[0]}; | ||
| 64 | + Catlass::RunKernel<MatmulKernel>(arguments, stream, blockNum); | ||
| 65 | +} | ||
| @@ -283,6 +283,7 @@ add_subdirectory(77_planar_complex_matmul) | |||
| 283 | add_subdirectory(78_matrix_inverse) | 283 | add_subdirectory(78_matrix_inverse) |
| 284 | add_subdirectory(80_grouped_matmul_slice_m_gelu) | 284 | add_subdirectory(80_grouped_matmul_slice_m_gelu) |
| 285 | add_subdirectory(81_ascend950_rain_fusion_attention) | 285 | add_subdirectory(81_ascend950_rain_fusion_attention) |
| 286 | +add_subdirectory(82_ascend950_basic_syrk) | ||
| 286 | 287 | ||
| 287 | if(_JIT_KERNEL_ENTRIES) | 288 | if(_JIT_KERNEL_ENTRIES) |
| 288 | set(_JIT_TARGET catlass_kernel_jit) | 289 | set(_JIT_TARGET catlass_kernel_jit) |
| @@ -49,6 +49,7 @@ | |||
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | 51 | ||
| 52 | + | ||
| 52 | 53 | ||
| 53 | 54 | ||
| 54 | 55 | ||
| @@ -429,4 +430,9 @@ REGISTER_TORCH_FUNC(planar_complex_matmul); | |||
| 429 | static auto& matrix_inverse = MatrixInverseOp::Run; | 430 | static auto& matrix_inverse = MatrixInverseOp::Run; |
| 430 | REGISTER_TORCH_FUNC(matrix_inverse); | 431 | REGISTER_TORCH_FUNC(matrix_inverse); |
| 431 | 432 | ||
| 433 | +// ── example 82_ascend950_basic_syrk ── | ||
| 434 | +using Ascend950BasicSyrkOp = BasicSyrkLike<CatlassKernel::Ascend950BasicSyrk>; | ||
| 435 | +static auto& ascend950_basic_syrk = Ascend950BasicSyrkOp::Run; | ||
| 436 | +REGISTER_TORCH_FUNC(ascend950_basic_syrk); | ||
| 437 | + | ||
| 432 | } // namespace CatlassKernelWrapper | 438 | } // namespace CatlassKernelWrapper |
| @@ -0,0 +1,78 @@ | |||
| 1 | +/** | ||
| 2 | + * This program is free software, you can redistribute it and/or modify. | ||
| 3 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | + * This file is a part of the CANN Open Software. | ||
| 5 | + * Licensed under 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, INCLUDING | ||
| 8 | + * BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of | ||
| 9 | + * the software repository for the full text of the License. | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +namespace CatlassKernelWrapper { | ||
| 24 | + | ||
| 25 | +using BasicSyrkKernelFn = | ||
| 26 | + void (*)(const uint32_t, aclrtStream, const CatlassKernel::TParams&, const CatlassKernel::MatmulParams&); | ||
| 27 | + | ||
| 28 | +/** | ||
| 29 | + * @brief Torch adapter for Y = X * X^T (example 82_ascend950_basic_syrk). | ||
| 30 | + * | ||
| 31 | + * Reuses MatmulParams with m == n; only inputAddr[0] (X) and outputAddr[0] (Y) are used. | ||
| 32 | + */ | ||
| 33 | +template <BasicSyrkKernelFn KernelFunc> | ||
| 34 | +struct BasicSyrkLike { | ||
| 35 | + using OutputType = at::Tensor; | ||
| 36 | + | ||
| 37 | + static OutputType Run(const at::Tensor& matX, const c10::ScalarType& outDType) | ||
| 38 | + { | ||
| 39 | + TORCH_CHECK(matX.dim() == 2, "ascend950_basic_syrk expects a 2-D input X of shape (M, K)"); | ||
| 40 | + TORCH_CHECK( | ||
| 41 | + matX.scalar_type() == at::kBFloat16 || matX.scalar_type() == at::kHalf, | ||
| 42 | + "ascend950_basic_syrk currently supports bfloat16 / float16 inputs"); | ||
| 43 | + TORCH_CHECK( | ||
| 44 | + outDType == at::kBFloat16 || outDType == at::kHalf, | ||
| 45 | + "ascend950_basic_syrk currently supports bfloat16 / float16 output"); | ||
| 46 | + | ||
| 47 | + CatlassKernel::TParams tParams; | ||
| 48 | + CatlassKernel::MatmulParams params; | ||
| 49 | + | ||
| 50 | + // ELEMENT_A → X, ELEMENT_C → Y (layouts are fixed inside BlockMmadSyrkTla). | ||
| 51 | + tParams.element["A"] = TorchDtypeToAclDtype(matX.scalar_type()); | ||
| 52 | + tParams.element["C"] = TorchDtypeToAclDtype(outDType); | ||
| 53 | + tParams.transpose["A"] = false; | ||
| 54 | + tParams.transpose["C"] = false; | ||
| 55 | + tParams.useNz["A"] = false; | ||
| 56 | + tParams.useNz["C"] = false; | ||
| 57 | + | ||
| 58 | + params.m = static_cast<uint32_t>(matX.size(0)); | ||
| 59 | + params.k = static_cast<uint32_t>(matX.size(1)); | ||
| 60 | + params.n = params.m; // Y is [M, M] | ||
| 61 | + | ||
| 62 | + params.inputAddr.resize(1); | ||
| 63 | + params.inputAddr[0] = static_cast<uint8_t*>(const_cast<void*>(matX.storage().data())); | ||
| 64 | + | ||
| 65 | + OutputType output = GetOutputTensor({params.m, params.n}, AclDtypeToTorchDtype(tParams.elem("C"))); | ||
| 66 | + params.outputAddr.resize(1); | ||
| 67 | + params.outputAddr[0] = static_cast<uint8_t*>(const_cast<void*>(output.storage().data())); | ||
| 68 | + | ||
| 69 | + aclrtStream stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 70 | + uint32_t aicCoreNum = platform_ascendc::PlatformAscendCManager::GetInstance()->GetCoreNumAic(); | ||
| 71 | + RUN_NPU_FUNC(KernelFunc, aicCoreNum, stream, tParams, params); | ||
| 72 | + return output; | ||
| 73 | + } | ||
| 74 | +}; | ||
| 75 | + | ||
| 76 | +} // namespace CatlassKernelWrapper | ||
| 77 | + | ||
| 78 | + | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | +# This program is free software, you can redistribute it and/or modify. | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This file is a part of the CANN Open Software. | ||
| 4 | +# Licensed under 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, INCLUDING | ||
| 7 | +# BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. See LICENSE in the root of | ||
| 8 | +# the software repository for the full text of the License. | ||
| 9 | + | ||
| 10 | +import pytest | ||
| 11 | +import torch | ||
| 12 | +import torch_npu | ||
| 13 | +import torch_catlass | ||
| 14 | + | ||
| 15 | +from common import only_on_3510 | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +def test_ascend950_basic_syrk(m, k, dtype): | ||
| 22 | + """Compare CATLASS Ascend950 Basic SYRK against torch.matmul(X, X.T).""" | ||
| 23 | + x = torch.randn(m, k, dtype=dtype, device="npu") | ||
| 24 | + | ||
| 25 | + result = torch_catlass.ascend950_basic_syrk(x) | ||
| 26 | + expected = torch.matmul(x.float(), x.float().T).to(dtype) | ||
| 27 | + | ||
| 28 | + assert result.shape == (m, m) | ||
| 29 | + assert result.dtype == dtype | ||
| 30 | + assert result.device.type == "npu" | ||
| 31 | + assert torch.allclose(result.float(), expected.float(), rtol=1e-2, atol=1e-2) | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +if __name__ == "__main__": | ||
| 35 | + pytest.main([__file__, "-v", "-s"]) | ||
| @@ -85,6 +85,7 @@ __all__ = [ | |||
| 85 | "ascend950_rain_fusion_attention", | 85 | "ascend950_rain_fusion_attention", |
| 86 | "clear_jit_cache", | 86 | "clear_jit_cache", |
| 87 | "symm", | 87 | "symm", |
| 88 | + "ascend950_basic_syrk", | ||
| 88 | "__version__", | 89 | "__version__", |
| 89 | "__catlass_version__", | 90 | "__catlass_version__", |
| 90 | ] | 91 | ] |
| @@ -119,6 +119,7 @@ from .w4a8_matmul import w4a8_matmul # example 32 | |||
| 119 | from .w8a16_matmul import w8a16_matmul # example 30 | 119 | from .w8a16_matmul import w8a16_matmul # example 30 |
| 120 | from .grouped_matmul_slice_m_gelu import grouped_matmul_slice_m_gelu # example 80 | 120 | from .grouped_matmul_slice_m_gelu import grouped_matmul_slice_m_gelu # example 80 |
| 121 | from .matrix_inverse import matrix_inverse # example 78 | 121 | from .matrix_inverse import matrix_inverse # example 78 |
| 122 | +from .ascend950_basic_syrk import ascend950_basic_syrk # example 82 | ||
| 122 | 123 | ||
| 123 | __all__ = [ | 124 | __all__ = [ |
| 124 | "basic_matmul", # example 00 | 125 | "basic_matmul", # example 00 |
| @@ -206,4 +207,5 @@ __all__ = [ | |||
| 206 | "planar_complex_matmul", # example 77 | 207 | "planar_complex_matmul", # example 77 |
| 207 | "grouped_matmul_slice_m_gelu", # example 80 | 208 | "grouped_matmul_slice_m_gelu", # example 80 |
| 208 | "matrix_inverse", # example 78 | 209 | "matrix_inverse", # example 78 |
| 210 | + "ascend950_basic_syrk", # example 82 | ||
| 209 | ] | 211 | ] |
| @@ -0,0 +1,33 @@ | |||
| 1 | +import torch | ||
| 2 | +from torch import Tensor | ||
| 3 | + | ||
| 4 | + | ||
| 5 | +def ascend950_basic_syrk( | ||
| 6 | + matX: Tensor, | ||
| 7 | + outDType: str | torch.dtype | None = None, | ||
| 8 | +) -> Tensor: | ||
| 9 | + """Run CATLASS Ascend950 Basic SYRK: ``Y = X @ X.T``. | ||
| 10 | + | ||
| 11 | + Source: example 82_ascend950_basic_syrk. | ||
| 12 | + | ||
| 13 | + Args: | ||
| 14 | + matX: Input matrix ``X`` with shape ``(M, K)`` on NPU. | ||
| 15 | + outDType: Output dtype. Defaults to ``matX.dtype``. Accepted strings | ||
| 16 | + include ``float16`` / ``fp16`` and ``bfloat16`` / ``bf16``. | ||
| 17 | + | ||
| 18 | + Returns: | ||
| 19 | + Output tensor ``Y`` with shape ``(M, M)`` on the active NPU device. | ||
| 20 | + """ | ||
| 21 | + if outDType is None: | ||
| 22 | + outDType = matX.dtype | ||
| 23 | + if isinstance(outDType, str): | ||
| 24 | + dtype_lower = outDType.lower() | ||
| 25 | + if dtype_lower in ("bf16", "bfloat16"): | ||
| 26 | + outDType = torch.bfloat16 | ||
| 27 | + elif dtype_lower in ("fp16", "float16"): | ||
| 28 | + outDType = torch.float16 | ||
| 29 | + else: | ||
| 30 | + outDType = getattr(torch, dtype_lower, None) | ||
| 31 | + if outDType is None: | ||
| 32 | + raise ValueError(f"{outDType} is not a data type of torch") | ||
| 33 | + return torch.ops.catlass.ascend950_basic_syrk(matX, outDType) | ||
| @@ -845,6 +845,7 @@ normal_cases_3510 = [ | |||
| 845 | "64_ascend950_matmul_evg_add_ub 256 512 1024 0", | 845 | "64_ascend950_matmul_evg_add_ub 256 512 1024 0", |
| 846 | "68_ascend950_multi_core_splitk_matmul 256 512 1024 0", | 846 | "68_ascend950_multi_core_splitk_matmul 256 512 1024 0", |
| 847 | "69_ascend950_tail_multi_core_splitk_matmul 256 512 1024 0", | 847 | "69_ascend950_tail_multi_core_splitk_matmul 256 512 1024 0", |
| 848 | + "82_ascend950_basic_syrk 1024 1024 0", | ||
| 848 | ] | 849 | ] |
| 849 | 850 | ||
| 850 | 851 | ||


需要补充一下对应ci case到tests/test_example.py,我看optest子PR里也没有该动作