已合并
add fused operator finalRouting #5941
slj创建于 5月26日
add fused operator finalRouting #5941
已合并
共 4 个文件变更+428-0
| @@ -0,0 +1,18 @@ | |||
| 1 | +message(STATUS "BUILD_TORCH_OPS ON in final_routing") | ||
| 2 | +# FINAL_ROUTING operation sources | ||
| 3 | +file(GLOB FINAL_ROUTING_NPU_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") | ||
| 4 | + | ||
| 5 | +set(FINAL_ROUTING_SOURCES ${FINAL_ROUTING_NPU_SOURCES}) | ||
| 6 | +# Mark .cpp files with special properties | ||
| 7 | +set_source_files_properties( | ||
| 8 | + ${FINAL_ROUTING_NPU_SOURCES} PROPERTIES | ||
| 9 | + LANGUAGE CXX | ||
| 10 | + COMPILE_FLAGS "--cce-soc-version=Ascend910B1 --cce-soc-core-type=VecCore --cce-auto-sync -xcce" | ||
| 11 | +) | ||
| 12 | + | ||
| 13 | +# Create object library | ||
| 14 | +add_library(final_routing_objects OBJECT ${FINAL_ROUTING_SOURCES}) | ||
| 15 | + | ||
| 16 | +target_compile_options(final_routing_objects PRIVATE ${COMMON_COMPILE_OPTIONS}) | ||
| 17 | +target_include_directories(final_routing_objects PRIVATE ${COMMON_INCLUDE_DIRS}) | ||
| 18 | +return() | ||
| @@ -0,0 +1,85 @@ | |||
| 1 | +# 算子名称:FinalRouting | ||
| 2 | + | ||
| 3 | +## 产品支持情况 | ||
| 4 | + | ||
| 5 | +| 产品 | 是否支持 | | ||
| 6 | +| :----------------------------------------------------------- | :------: | | ||
| 7 | +| Atlas A2 训练系列产品 | 是 | | ||
| 8 | + | ||
| 9 | +## 功能说明 | ||
| 10 | + | ||
| 11 | +- 算子功能:用于MoE模型的最终路由阶段,将各个Expert计算的结果按照评分加权合并,得到每个Token的最终输出,完成MoE的Combine阶段。 | ||
| 12 | + | ||
| 13 | +- 计算公式: | ||
| 14 | +对于每个Token `t`: | ||
| 15 | + | ||
| 16 | +$$ | ||
| 17 | +out[t] = \sum_{e} \big(in[token\_table[t, e]] \cdot score\_table[t, e]\big) | ||
| 18 | +$$ | ||
| 19 | + | ||
| 20 | +其中,仅当 `token_table[t, e] >= 0` 时参与计算。 | ||
| 21 | + | ||
| 22 | +## 参数说明 | ||
| 23 | + | ||
| 24 | +<table style="undefined;table-layout: fixed; width: 820px"><colgroup> | ||
| 25 | + <col style="width: 100px"> | ||
| 26 | + <col style="width: 150px"> | ||
| 27 | + <col style="width: 190px"> | ||
| 28 | + <col style="width: 260px"> | ||
| 29 | + <col style="width: 120px"> | ||
| 30 | + </colgroup> | ||
| 31 | + <thead> | ||
| 32 | + <tr> | ||
| 33 | + <th>参数名</th> | ||
| 34 | + <th>输入/输出/属性</th> | ||
| 35 | + <th>描述</th> | ||
| 36 | + <th>数据类型</th> | ||
| 37 | + <th>数据格式</th> | ||
| 38 | + </tr></thead> | ||
| 39 | + <tbody> | ||
| 40 | + <tr> | ||
| 41 | + <td>blockDim</td> | ||
| 42 | + <td>输入</td> | ||
| 43 | + <td>AI CORE的数量,比如:Ascend910B是40。</td> | ||
| 44 | + <td>int64_t</td> | ||
| 45 | + <td>-</td> | ||
| 46 | + </tr> | ||
| 47 | + <tr> | ||
| 48 | + <td>in</td> | ||
| 49 | + <td>输入</td> | ||
| 50 | + <td>expert输出张量, shape为(expert_num*token_num, hidden_size)</td> | ||
| 51 | + <td>BFLOAT16</td> | ||
| 52 | + <td>ND</td> | ||
| 53 | + </tr> | ||
| 54 | + <tr> | ||
| 55 | + <td>token_table</td> | ||
| 56 | + <td>输入</td> | ||
| 57 | + <td>token到expert的映射表, shape为(token_num, expert_num)</td> | ||
| 58 | + <td>int32_t</td> | ||
| 59 | + <td>ND</td> | ||
| 60 | + </tr> | ||
| 61 | + <tr> | ||
| 62 | + <td>score_table</td> | ||
| 63 | + <td>输入</td> | ||
| 64 | + <td>每个token在每个expert的评分,shape为(token_num, expert_num)</td> | ||
| 65 | + <td>BFLOAT16</td> | ||
| 66 | + <td>ND</td> | ||
| 67 | + </tr> | ||
| 68 | + <tr> | ||
| 69 | + <td>out</td> | ||
| 70 | + <td>输出</td> | ||
| 71 | + <td>加权合并后的输出张量,shape为(token_num, hidden_size)</td> | ||
| 72 | + <td>BFLOAT16</td> | ||
| 73 | + <td>ND</td> | ||
| 74 | + </tr> | ||
| 75 | + </tbody></table> | ||
| 76 | + | ||
| 77 | +## 约束说明 | ||
| 78 | + | ||
| 79 | +- token_table中小于0的值表示该expert对此token无效 | ||
| 80 | + | ||
| 81 | +## 调用说明 | ||
| 82 | + | ||
| 83 | +``` | ||
| 84 | +torch.ops.npu_ops_transformer_ext.final_routing(block_dim, input, token_table,score_table, output) | ||
| 85 | +``` | ||
| @@ -0,0 +1,279 @@ | |||
| 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 | +namespace npu_ops_transformer_ext { | ||
| 20 | +namespace FinalRouting { | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +using namespace AscendC; | ||
| 26 | + | ||
| 27 | +constexpr int64_t UB_MAX_BYTES = 184*1024; | ||
| 28 | +constexpr int64_t BUFFER_NUM = 1; | ||
| 29 | + | ||
| 30 | +class final_routing { | ||
| 31 | +public: | ||
| 32 | + __aicore__ inline final_routing() {} | ||
| 33 | + | ||
| 34 | + __aicore__ inline void Init(GM_ADDR in, GM_ADDR token_table, GM_ADDR score_table, GM_ADDR out, | ||
| 35 | + const int64_t expert_num, const int64_t token_num, const int64_t copy_byte) | ||
| 36 | + { | ||
| 37 | + blockNum_ = GetBlockNum(); | ||
| 38 | + blockIdx_ = GetBlockIdx(); | ||
| 39 | + | ||
| 40 | + gbExpertNum_ = expert_num; | ||
| 41 | + gbTokenNum_ = token_num; | ||
| 42 | + gbCopyByte_ = copy_byte; | ||
| 43 | + gbCopyNum_ = gbCopyByte_ / sizeof(bfloat16_t); | ||
| 44 | + | ||
| 45 | + gbTokenNumAlign_ = AlignUp(gbTokenNum_, 64 / sizeof(int32_t)); | ||
| 46 | + gbExpertNumAlign_ = AlignUp(gbExpertNum_, 64 / sizeof(int32_t)); | ||
| 47 | + | ||
| 48 | + bkExpertNum_ = 1; | ||
| 49 | + bkTokenNum_ = 1; | ||
| 50 | + bkCopyByte_ = gbCopyByte_; | ||
| 51 | + bkCopyNum_ = bkCopyByte_ / sizeof(bfloat16_t); | ||
| 52 | + bkCopyByteAlign_ = AlignUp(bkCopyByte_, 64); | ||
| 53 | + bkCopyNumAlign_ = bkCopyByteAlign_ / sizeof(bfloat16_t); | ||
| 54 | + | ||
| 55 | + gmIn_.SetGlobalBuffer((__gm__ bfloat16_t*)(in), gbExpertNum_ * gbTokenNum_ * gbCopyNum_); | ||
| 56 | + gmTokenTable_.SetGlobalBuffer((__gm__ int32_t*)(token_table), gbTokenNum_ * gbExpertNum_); | ||
| 57 | + gmScoreTable_.SetGlobalBuffer((__gm__ bfloat16_t*)(score_table), gbTokenNum_ * gbExpertNum_); | ||
| 58 | + gmOut_.SetGlobalBuffer((__gm__ bfloat16_t*)(out), gbTokenNum_ * gbCopyNum_); | ||
| 59 | + | ||
| 60 | + pipe_.InitBuffer(inQueIn_, BUFFER_NUM, bkCopyByteAlign_ * 2); | ||
| 61 | + pipe_.InitBuffer(inQueTokenTable_, BUFFER_NUM, gbExpertNumAlign_ * sizeof(int32_t)); | ||
| 62 | + pipe_.InitBuffer(inQueScoreTable_, BUFFER_NUM, gbExpertNumAlign_ * sizeof(bfloat16_t)); | ||
| 63 | + pipe_.InitBuffer(outQueOut_, BUFFER_NUM, bkCopyByteAlign_ * 2); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + __aicore__ inline void Process() | ||
| 67 | + { | ||
| 68 | + for (int64_t tis = 0; tis < gbTokenNum_; tis += blockNum_) { | ||
| 69 | + int64_t token_idx = tis + blockIdx_; | ||
| 70 | + | ||
| 71 | + if (token_idx < gbTokenNum_) { | ||
| 72 | + LocalTensor<int32_t> local_token_table = inQueTokenTable_.AllocTensor<int32_t>(); | ||
| 73 | + LocalTensor<bfloat16_t> local_score_table = inQueScoreTable_.AllocTensor<bfloat16_t>(); | ||
| 74 | + | ||
| 75 | + DataCopyParams copy_pas{1, (uint16_t)(gbExpertNum_ * sizeof(int32_t)), 0, 0}; | ||
| 76 | + DataCopyPadParams pad_pas; | ||
| 77 | + | ||
| 78 | + DataCopyPad(local_token_table, gmTokenTable_[token_idx * gbExpertNum_], copy_pas, pad_pas); | ||
| 79 | + copy_pas.blockLen = (uint16_t)(gbExpertNum_ * sizeof(bfloat16_t)); | ||
| 80 | + DataCopyPad(local_score_table, gmScoreTable_[token_idx * gbExpertNum_], copy_pas, pad_pas); | ||
| 81 | + | ||
| 82 | + inQueTokenTable_.EnQue(local_token_table); | ||
| 83 | + inQueScoreTable_.EnQue(local_score_table); | ||
| 84 | + | ||
| 85 | + lmTokenTable_ = inQueTokenTable_.DeQue<int32_t>(); | ||
| 86 | + lmScoreTable_ = inQueScoreTable_.DeQue<bfloat16_t>(); | ||
| 87 | + | ||
| 88 | + lmOut_ = outQueOut_.AllocTensor<bfloat16_t>(); | ||
| 89 | + lmOutFloat_ = lmOut_.ReinterpretCast<float>(); | ||
| 90 | + Duplicate(lmOutFloat_, (float)0.0, bkCopyNumAlign_); | ||
| 91 | + | ||
| 92 | + for (int64_t expert_idx = 0; expert_idx < gbExpertNum_; expert_idx++) { | ||
| 93 | + int64_t routing_token_idx = lmTokenTable_.GetValue(expert_idx); | ||
| 94 | + if (routing_token_idx >= 0) { | ||
| 95 | + CopyIn(routing_token_idx); | ||
| 96 | + Compute(AscendC::ToFloat(lmScoreTable_.GetValue(expert_idx))); | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + Cast(lmOut_, lmOutFloat_, RoundMode::CAST_ROUND, bkCopyNum_); | ||
| 101 | + outQueOut_.EnQue(lmOut_); | ||
| 102 | + outQueOut_.DeQue<bfloat16_t>(); | ||
| 103 | + CopyOut(token_idx); | ||
| 104 | + | ||
| 105 | + inQueTokenTable_.FreeTensor(local_token_table); | ||
| 106 | + inQueScoreTable_.FreeTensor(local_score_table); | ||
| 107 | + outQueOut_.FreeTensor(lmOut_); | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | +private: | ||
| 113 | + __aicore__ inline void CopyIn(int64_t routing_token_idx) | ||
| 114 | + { | ||
| 115 | + LocalTensor<bfloat16_t> local_in = inQueIn_.AllocTensor<bfloat16_t>(); | ||
| 116 | + | ||
| 117 | + DataCopyExtParams copy_pas{1, (uint32_t)(bkCopyByte_), 0, 0, 0}; | ||
| 118 | + DataCopyPadExtParams<bfloat16_t> pad_pas; | ||
| 119 | + | ||
| 120 | + int64_t offset = routing_token_idx * gbCopyNum_; | ||
| 121 | + DataCopyPad(local_in[bkCopyNumAlign_], gmIn_[offset], copy_pas, pad_pas); | ||
| 122 | + inQueIn_.EnQue(local_in); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + __aicore__ inline void Compute(float score) | ||
| 126 | + { | ||
| 127 | + LocalTensor<bfloat16_t> local_in = inQueIn_.DeQue<bfloat16_t>(); | ||
| 128 | + LocalTensor<float> local_in_float = local_in.ReinterpretCast<float>(); | ||
| 129 | + | ||
| 130 | + Cast(local_in_float, local_in[bkCopyNumAlign_], RoundMode::CAST_NONE, bkCopyNum_); | ||
| 131 | + | ||
| 132 | + Muls(local_in_float, local_in_float, score, bkCopyNum_); | ||
| 133 | + Add(lmOutFloat_, lmOutFloat_, local_in_float, bkCopyNum_); | ||
| 134 | + | ||
| 135 | + inQueIn_.FreeTensor(local_in); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + __aicore__ inline void CopyOut(int64_t token_idx) | ||
| 139 | + { | ||
| 140 | + DataCopyExtParams copy_pas{1, (uint32_t)(bkCopyByte_), 0, 0, 0}; | ||
| 141 | + DataCopyPad(gmOut_[token_idx * bkCopyNum_], lmOut_, copy_pas); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | +private: | ||
| 145 | + TPipe pipe_; | ||
| 146 | + TQue<QuePosition::VECIN, BUFFER_NUM> inQueIn_, inQueTokenTable_, inQueScoreTable_; | ||
| 147 | + TQue<QuePosition::VECOUT, BUFFER_NUM> outQueOut_; | ||
| 148 | + | ||
| 149 | + GlobalTensor<bfloat16_t> gmIn_, gmOut_; | ||
| 150 | + GlobalTensor<int32_t> gmTokenTable_; | ||
| 151 | + GlobalTensor<bfloat16_t> gmScoreTable_; | ||
| 152 | + | ||
| 153 | + LocalTensor<int32_t> lmTokenTable_; | ||
| 154 | + LocalTensor<bfloat16_t> lmScoreTable_; | ||
| 155 | + LocalTensor<bfloat16_t> lmOut_; | ||
| 156 | + LocalTensor<float> lmOutFloat_; | ||
| 157 | + | ||
| 158 | + int64_t blockNum_, blockIdx_; | ||
| 159 | + | ||
| 160 | + int64_t gbExpertNum_, gbTokenNum_, gbCopyByte_, gbCopyNum_; | ||
| 161 | + int64_t gbExpertNumAlign_, gbTokenNumAlign_; | ||
| 162 | + | ||
| 163 | + int64_t bkExpertNum_, bkTokenNum_, bkCopyByte_, bkCopyNum_; | ||
| 164 | + int64_t bkCopyByteAlign_, bkCopyNumAlign_; | ||
| 165 | +}; | ||
| 166 | + | ||
| 167 | +extern "C" __global__ __aicore__ void compute_final_routing( | ||
| 168 | + GM_ADDR in, GM_ADDR token_table, GM_ADDR score_table, GM_ADDR out, | ||
| 169 | + const int64_t expert_num, const int64_t token_num, const int64_t copy_byte) | ||
| 170 | +{ | ||
| 171 | + final_routing op; | ||
| 172 | + op.Init(in, token_table, score_table, out, expert_num, token_num, copy_byte); | ||
| 173 | + op.Process(); | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +void final_routing_kernel_lanuch(int64_t block_dim, void* stream, | ||
| 177 | + uint8_t* in, uint8_t* token_table, uint8_t* score_table, uint8_t* out, | ||
| 178 | + const int64_t expert_num, const int64_t token_num, const int64_t copy_byte) | ||
| 179 | +{ | ||
| 180 | + compute_final_routing<<<block_dim, nullptr, stream>>>(in, token_table, score_table, out, | ||
| 181 | + expert_num, token_num, copy_byte); | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +inline int64_t align_up(const int64_t number, const int64_t alignSize) | ||
| 185 | +{ | ||
| 186 | + if (number % alignSize == 0) { | ||
| 187 | + return number; | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + return ((number / alignSize + 1) * alignSize); | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | + | ||
| 194 | +int judge_final_routing_lanuch(const int64_t expert_num, const int64_t token_num, const int64_t copy_byte, | ||
| 195 | + const int64_t ubSize, const int64_t vCores) | ||
| 196 | +{ | ||
| 197 | + (void)(vCores); | ||
| 198 | + | ||
| 199 | + constexpr int64_t BUFFER_NUM = 1; | ||
| 200 | + | ||
| 201 | + int64_t gbExpertNum_; | ||
| 202 | + int64_t gbTokenNum_; | ||
| 203 | + int64_t gbCopyByte_; | ||
| 204 | + int64_t gbExpertNumAlign_; | ||
| 205 | + int64_t gbTokenNumAlign_; | ||
| 206 | + | ||
| 207 | + int64_t bkCopyByte_; | ||
| 208 | + int64_t bkCopyByteAlign_; | ||
| 209 | + | ||
| 210 | + gbExpertNum_ = expert_num; | ||
| 211 | + gbTokenNum_ = token_num; | ||
| 212 | + gbCopyByte_ = copy_byte; | ||
| 213 | + | ||
| 214 | + gbTokenNumAlign_ = align_up(gbTokenNum_, 64 / sizeof(int32_t)); | ||
| 215 | + gbExpertNumAlign_ = align_up(gbExpertNum_, 64 / sizeof(int32_t)); | ||
| 216 | + | ||
| 217 | + bkCopyByte_ = gbCopyByte_; | ||
| 218 | + bkCopyByteAlign_ = align_up(bkCopyByte_, 64); | ||
| 219 | + | ||
| 220 | + float use_byte = BUFFER_NUM * bkCopyByteAlign_ * 2; | ||
| 221 | + use_byte += gbTokenNumAlign_ * sizeof(int32_t); | ||
| 222 | + use_byte += gbExpertNumAlign_ * sizeof(int32_t); | ||
| 223 | + use_byte += gbExpertNumAlign_ * sizeof(int16_t); | ||
| 224 | + | ||
| 225 | + if (use_byte > ubSize) { | ||
| 226 | + std::cout << __FUNCTION__ << ": " << use_byte/1024 << " KB" << std::endl; | ||
| 227 | + return 1; | ||
| 228 | + } | ||
| 229 | + return 0; | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +int final_routing_lanuch(int64_t block_dim, void* stream, | ||
| 233 | + uint8_t* in, uint8_t* token_table, uint8_t* score_table, uint8_t* out, | ||
| 234 | + const int64_t expert_num, const int64_t token_num, const int64_t copy_byte) | ||
| 235 | +{ | ||
| 236 | + int64_t ubSize = 184 * 1024; | ||
| 237 | + int64_t vCores = 40; | ||
| 238 | + | ||
| 239 | + int ret = judge_final_routing_lanuch(expert_num, token_num, copy_byte, ubSize, vCores); | ||
| 240 | + if (ret == 0) { | ||
| 241 | + final_routing_kernel_lanuch(block_dim, stream, in, token_table, score_table, | ||
| 242 | + out, expert_num, token_num, copy_byte); | ||
| 243 | + return 0; | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + std::cout << __FUNCTION__ << ": " << "UB size is limited, please check!" << std::endl; | ||
| 247 | + return 1; | ||
| 248 | +} | ||
| 249 | + | ||
| 250 | +int64_t final_routing_npu(int64_t block_dim, torch::Tensor &in, torch::Tensor &token_table, | ||
| 251 | + torch::Tensor &score_table, torch::Tensor &out) | ||
| 252 | +{ | ||
| 253 | + TORCH_CHECK(torch_npu::utils::is_npu(in), "input tensor must be on NPU device"); | ||
| 254 | + TORCH_CHECK(torch_npu::utils::is_npu(token_table), "token table tensor must be on NPU device"); | ||
| 255 | + TORCH_CHECK(torch_npu::utils::is_npu(score_table), "score table tensor must be on NPU device"); | ||
| 256 | + TORCH_CHECK(torch_npu::utils::is_npu(out), "output tensor must be on NPU device"); | ||
| 257 | + | ||
| 258 | + const int64_t token_num = token_table.size(0); | ||
| 259 | + const int64_t expert_num = token_table.size(1); | ||
| 260 | + const int64_t copy_byte = in.element_size() * in.size(1); | ||
| 261 | + | ||
| 262 | + auto stream = c10_npu::getCurrentNPUStream().stream(false); | ||
| 263 | + int launchStatus = 0; | ||
| 264 | + auto acl_call = [=, &launchStatus]() -> int { | ||
| 265 | + launchStatus = final_routing_lanuch(block_dim, stream, (uint8_t *)in.data_ptr(), | ||
| 266 | + (uint8_t *)token_table.data_ptr(), (uint8_t *)score_table.data_ptr(), (uint8_t *)out.data_ptr(), | ||
| 267 | + expert_num, token_num, copy_byte); | ||
| 268 | + return 0; | ||
| 269 | + }; | ||
| 270 | + at_npu::native::OpCommand::RunOpApi("FinalRouting", acl_call); | ||
| 271 | + return launchStatus; | ||
| 272 | +} | ||
| 273 | + | ||
| 274 | +TORCH_LIBRARY_IMPL(npu_ops_transformer_ext, PrivateUse1, m) | ||
| 275 | +{ | ||
| 276 | + m.impl("final_routing", final_routing_npu); | ||
| 277 | +} | ||
| 278 | +} | ||
| 279 | +} | ||
| @@ -0,0 +1,46 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: UTF-8 -*- | ||
| 3 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 4 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 5 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 6 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 7 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 8 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 9 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 10 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 11 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 12 | + | ||
| 13 | +import logging | ||
| 14 | +import torch | ||
| 15 | +import torch_npu | ||
| 16 | +import npu_ops_transformer_ext | ||
| 17 | + | ||
| 18 | +logging.basicConfig(level=logging.INFO) | ||
| 19 | + | ||
| 20 | +TOKEN_NUM = 2 | ||
| 21 | +EXPERT_NUM = 4 | ||
| 22 | +HIDDEN_SIZE = 8 | ||
| 23 | +BLOCK_DIM = 40 | ||
| 24 | + | ||
| 25 | +input_tensor = torch.randint(1, 10, (TOKEN_NUM * EXPERT_NUM, HIDDEN_SIZE)).to(torch.bfloat16) | ||
| 26 | +token_table = torch.arange(TOKEN_NUM * EXPERT_NUM, dtype=torch.int32).reshape(TOKEN_NUM, EXPERT_NUM) | ||
| 27 | +score_table = torch.randint(0, 10, (TOKEN_NUM, EXPERT_NUM)).to(torch.bfloat16) | ||
| 28 | +output = torch.empty(TOKEN_NUM, HIDDEN_SIZE, dtype=torch.bfloat16) | ||
| 29 | +output_cpu = torch.empty_like(output).float() | ||
| 30 | + | ||
| 31 | +input_32 = input_tensor.float() | ||
| 32 | +for t in range(TOKEN_NUM): | ||
| 33 | + for e in range(EXPERT_NUM): | ||
| 34 | + input_idx = token_table[t, e].item() | ||
| 35 | + if input_idx < 0: | ||
| 36 | + continue | ||
| 37 | + score = score_table[t, e].item() | ||
| 38 | + feat = input_32[input_idx] | ||
| 39 | + output_cpu[t] += feat * score | ||
| 40 | + | ||
| 41 | +output_cpu = output_cpu.to(torch.bfloat16) | ||
| 42 | +output_npu = output.npu() | ||
| 43 | +torch.ops.npu_ops_transformer_ext.final_routing(BLOCK_DIM, input_tensor.npu(), token_table.npu(), | ||
| 44 | + score_table.npu(), output_npu) | ||
| 45 | + | ||
| 46 | +logging.info(f"cpu result vs npu result: {torch.equal(output_cpu, output_npu.cpu())}") | ||