已合并
megamoe优化,改成主E模式 #1466
zy_tt创建于 25 天前
megamoe优化,改成主E模式 #1466
已合并
共 30 个文件变更+2726-1759
| @@ -11,13 +11,17 @@ | |||
| 11 | 11 | ||
| 12 | cmake_minimum_required(VERSION 3.16) | 12 | cmake_minimum_required(VERSION 3.16) |
| 13 | 13 | ||
| 14 | -set(CMAKE_COMPILER "bisheng" CACHE STRING "Compiler used for host and CCE sources") | 14 | +if(NOT DEFINED ENV{ASCEND_HOME_PATH}) |
| 15 | -if(NOT DEFINED CMAKE_C_COMPILER) | 15 | + message(FATAL_ERROR "Cannot find ASCEND_HOME_PATH, please run set_env.sh.") |
| 16 | - set(CMAKE_C_COMPILER "${CMAKE_COMPILER}") | ||
| 17 | endif() | 16 | endif() |
| 18 | -if(NOT DEFINED CMAKE_CXX_COMPILER) | 17 | +set(ASCEND_HOME_PATH $ENV{ASCEND_HOME_PATH}) |
| 19 | - set(CMAKE_CXX_COMPILER "${CMAKE_COMPILER}") | 18 | + |
| 19 | +get_filename_component(ASCEND_BISHENG_COMPILER "${ASCEND_HOME_PATH}/bin/bisheng" REALPATH) | ||
| 20 | +if(NOT EXISTS "${ASCEND_BISHENG_COMPILER}") | ||
| 21 | + message(FATAL_ERROR "Cannot find ${ASCEND_HOME_PATH}/bin/bisheng, please run the CANN set_env.sh.") | ||
| 20 | endif() | 22 | endif() |
| 23 | +set(CMAKE_C_COMPILER "${ASCEND_BISHENG_COMPILER}" CACHE FILEPATH "C compiler" FORCE) | ||
| 24 | +set(CMAKE_CXX_COMPILER "${ASCEND_BISHENG_COMPILER}" CACHE FILEPATH "CXX compiler" FORCE) | ||
| 21 | 25 | ||
| 22 | project(pto_example LANGUAGES C CXX) | 26 | project(pto_example LANGUAGES C CXX) |
| 23 | 27 | ||
| @@ -31,14 +35,63 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |||
| 31 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) | 35 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) |
| 32 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) | 36 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) |
| 33 | 37 | ||
| 34 | -if(NOT DEFINED ENV{ASCEND_HOME_PATH}) | ||
| 35 | - message(FATAL_ERROR "Cannot find ASCEND_HOME_PATH, please run set_env.sh.") | ||
| 36 | -else() | ||
| 37 | - set(ASCEND_HOME_PATH $ENV{ASCEND_HOME_PATH}) | ||
| 38 | -endif() | ||
| 39 | - | ||
| 40 | set(ASCEND_DRIVER_PATH /usr/local/Ascend/driver) | 38 | set(ASCEND_DRIVER_PATH /usr/local/Ascend/driver) |
| 41 | 39 | ||
| 40 | +# The CANN environment exposes include/bin links for its active ARM or x86 package. | ||
| 41 | +# Resolve that link instead of duplicating CANN's platform selection logic here. | ||
| 42 | +get_filename_component(ASCEND_KERNEL_PLATFORM_INCLUDE "${ASCEND_HOME_PATH}/include" REALPATH) | ||
| 43 | +get_filename_component(ASCEND_KERNEL_PLATFORM_ROOT "${ASCEND_KERNEL_PLATFORM_INCLUDE}" DIRECTORY) | ||
| 44 | +if(NOT IS_DIRECTORY "${ASCEND_KERNEL_PLATFORM_INCLUDE}") | ||
| 45 | + message(FATAL_ERROR "Cannot resolve the active CANN platform directory from ${ASCEND_HOME_PATH}/include.") | ||
| 46 | +endif() | ||
| 47 | + | ||
| 48 | +# CANN 9.x uses asc/, while older compatibility packages use include/ascendc/. | ||
| 49 | +set(KERNEL_INCLUDE_CANDIDATES | ||
| 50 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/asc | ||
| 51 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/asc/include | ||
| 52 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/asc/include/basic_api | ||
| 53 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/asc/include/interface | ||
| 54 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/asc/include/utils | ||
| 55 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/asc/impl/basic_api | ||
| 56 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/include/ascendc/basic_api | ||
| 57 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/include/ascendc/basic_api/interface | ||
| 58 | + ${ASCEND_KERNEL_PLATFORM_ROOT}/include/ascendc/basic_api/impl | ||
| 59 | +) | ||
| 60 | + | ||
| 61 | +set(KERNEL_INCLUDE_DIRS) | ||
| 62 | +set(KERNEL_OPERATOR_HEADER_FOUND FALSE) | ||
| 63 | +set(KERNEL_TPIPE_HEADER_FOUND FALSE) | ||
| 64 | +set(KERNEL_TPIPE_IMPL_HEADER_FOUND FALSE) | ||
| 65 | +foreach(KERNEL_INCLUDE_CANDIDATE IN LISTS KERNEL_INCLUDE_CANDIDATES) | ||
| 66 | + if(IS_DIRECTORY "${KERNEL_INCLUDE_CANDIDATE}") | ||
| 67 | + list(APPEND KERNEL_INCLUDE_DIRS "${KERNEL_INCLUDE_CANDIDATE}") | ||
| 68 | + if(EXISTS "${KERNEL_INCLUDE_CANDIDATE}/kernel_operator.h") | ||
| 69 | + set(KERNEL_OPERATOR_HEADER_FOUND TRUE) | ||
| 70 | + endif() | ||
| 71 | + if(EXISTS "${KERNEL_INCLUDE_CANDIDATE}/kernel_tpipe.h") | ||
| 72 | + set(KERNEL_TPIPE_HEADER_FOUND TRUE) | ||
| 73 | + endif() | ||
| 74 | + if(EXISTS "${KERNEL_INCLUDE_CANDIDATE}/kernel_tpipe_impl.h") | ||
| 75 | + set(KERNEL_TPIPE_IMPL_HEADER_FOUND TRUE) | ||
| 76 | + endif() | ||
| 77 | + endif() | ||
| 78 | +endforeach() | ||
| 79 | + | ||
| 80 | +if(NOT KERNEL_OPERATOR_HEADER_FOUND | ||
| 81 | + OR NOT KERNEL_TPIPE_HEADER_FOUND | ||
| 82 | + OR NOT KERNEL_TPIPE_IMPL_HEADER_FOUND) | ||
| 83 | + message(FATAL_ERROR | ||
| 84 | + "Incomplete AscendC headers under ASCEND_HOME_PATH=${ASCEND_HOME_PATH}: expected kernel_operator.h, " | ||
| 85 | + "kernel_tpipe.h, and kernel_tpipe_impl.h. Check the active CANN environment.") | ||
| 86 | +endif() | ||
| 87 | + | ||
| 88 | +if(EXISTS ${ASCEND_KERNEL_PLATFORM_ROOT}/include/ascendc/highlevel_api/lib/std/tuple.h) | ||
| 89 | + list(APPEND KERNEL_INCLUDE_DIRS ${ASCEND_KERNEL_PLATFORM_ROOT}/include/ascendc/highlevel_api) | ||
| 90 | +endif() | ||
| 91 | +if(EXISTS ${ASCEND_KERNEL_PLATFORM_ROOT}/pkg_inc/runtime/runtime) | ||
| 92 | + list(APPEND KERNEL_INCLUDE_DIRS ${ASCEND_KERNEL_PLATFORM_ROOT}/pkg_inc/runtime/runtime) | ||
| 93 | +endif() | ||
| 94 | +list(REMOVE_DUPLICATES KERNEL_INCLUDE_DIRS) | ||
| 42 | add_compile_options( | 95 | add_compile_options( |
| 43 | -D_FORTIFY_SOURCE=2 | 96 | -D_FORTIFY_SOURCE=2 |
| 44 | -O2 -std=c++17 | 97 | -O2 -std=c++17 |
| @@ -62,39 +115,7 @@ set(CMAKE_CCE_COMPILE_OPTIONS | |||
| 62 | "SHELL:-mllvm -cce-aicore-dcci-insert-for-scalar=false" | 115 | "SHELL:-mllvm -cce-aicore-dcci-insert-for-scalar=false" |
| 63 | ) | 116 | ) |
| 64 | 117 | ||
| 65 | -if(DEBUG_MODE) | 118 | +set(CCE_AICORE_ARCH "dav-c220") |
| 66 | - message(STATUS "Debug Mode Enabled, Add Debug Options") | ||
| 67 | - add_compile_definitions(_DEBUG) | ||
| 68 | - set(CMAKE_CCE_COMPILE_OPTIONS "${CMAKE_CCE_COMPILE_OPTIONS} --cce-enable-print") | ||
| 69 | -endif() | ||
| 70 | - | ||
| 71 | -set(A2A3_SOC_VERSIONS | ||
| 72 | - Ascend910B1 | ||
| 73 | - Ascend910B | ||
| 74 | - Ascend910C | ||
| 75 | - Ascend910_93 | ||
| 76 | - Ascend910_9391 | ||
| 77 | - Ascend910_9381 | ||
| 78 | - Ascend910_9372 | ||
| 79 | - Ascend910_9392 | ||
| 80 | - Ascend910_9382 | ||
| 81 | - Ascend910_9362 | ||
| 82 | - ascend910_93 | ||
| 83 | - ascend910_9391 | ||
| 84 | - ascend910_9381 | ||
| 85 | - ascend910_9372 | ||
| 86 | - ascend910_9392 | ||
| 87 | - ascend910_9382 | ||
| 88 | - ascend910_9362 | ||
| 89 | -) | ||
| 90 | - | ||
| 91 | -if("${SOC_VERSION}" IN_LIST A2A3_SOC_VERSIONS) | ||
| 92 | - set(CCE_AICORE_ARCH "dav-c220") | ||
| 93 | -elseif("${SOC_VERSION}" STREQUAL "Ascend910_9599") | ||
| 94 | - set(CCE_AICORE_ARCH "dav-c310") | ||
| 95 | -else() | ||
| 96 | - message(FATAL_ERROR "Unsupported SOC_VERSION: ${SOC_VERSION}") | ||
| 97 | -endif() | ||
| 98 | 119 | ||
| 99 | set(CMAKE_CPP_COMPILE_OPTIONS | 120 | set(CMAKE_CPP_COMPILE_OPTIONS |
| 100 | -xc++ | 121 | -xc++ |
| @@ -124,12 +145,7 @@ function(pto_example_mixed NAME) | |||
| 124 | ${PROJECT_SOURCE_DIR}/op_kernel | 145 | ${PROJECT_SOURCE_DIR}/op_kernel |
| 125 | ${PROJECT_SOURCE_DIR}/op_kernel/utils | 146 | ${PROJECT_SOURCE_DIR}/op_kernel/utils |
| 126 | ${PROJECT_BINARY_DIR} | 147 | ${PROJECT_BINARY_DIR} |
| 127 | - ${ASCEND_HOME_PATH}/aarch64-linux/pkg_inc/runtime/runtime | 148 | + ${KERNEL_INCLUDE_DIRS} |
| 128 | - ${ASCEND_HOME_PATH}/aarch64-linux/include/ascendc/basic_api | ||
| 129 | - ${ASCEND_HOME_PATH}/aarch64-linux/asc | ||
| 130 | - ${ASCEND_HOME_PATH}/aarch64-linux/asc/include/basic_api | ||
| 131 | - ${ASCEND_HOME_PATH}/aarch64-linux/asc/include/interface | ||
| 132 | - ${ASCEND_HOME_PATH}/aarch64-linux/ascendc/include/basic_api/impl | ||
| 133 | ) | 149 | ) |
| 134 | target_link_options(${NAME}_kernel PRIVATE --cce-fatobj-link) | 150 | target_link_options(${NAME}_kernel PRIVATE --cce-fatobj-link) |
| 135 | 151 | ||
| @@ -48,14 +48,10 @@ kernels/manual/a2a3/dispatch_mega_combine/ | |||
| 48 | │ ├── combine.h # Remote writeback of GMM2 output to offsetD | 48 | │ ├── combine.h # Remote writeback of GMM2 output to offsetD |
| 49 | │ ├── unpermute.h # TopK weighted reduction and original token order restoration | 49 | │ ├── unpermute.h # TopK weighted reduction and original token order restoration |
| 50 | │ └── utils/ # PTO vector, sync, HCCL window, and GMM pipeline helpers | 50 | │ └── utils/ # PTO vector, sync, HCCL window, and GMM pipeline helpers |
| 51 | -├── overview.md # Design overview, performance comparison, and stage pseudocode | 51 | +├── overview.md # Design overview and performance comparison |
| 52 | -├── front_reorder.md # Front reorder / sort / count-as-flag details | 52 | +├── overview_v1.md # Fixed-group scheduling and overlap design |
| 53 | -├── dispatch.md # Dispatch contract and data movement strategy | 53 | +├── pseudocode.md # Current seven-stage data flow and pseudocode |
| 54 | -├── gmm1.md / gmm2.md # GMM tile scheduling, swizzle, sync, and pipeline details | 54 | +└── README_zh.md # Chinese README |
| 55 | -├── swiglu.md # SwiGLU segmentation and quantization strategy | ||
| 56 | -├── combine.md # Combine large/small paths and remote writeback protocol | ||
| 57 | -├── unpermute.md # Unpermute restoration and accumulation strategy | ||
| 58 | -└── glden.md # Python batch golden rewrite design | ||
| 59 | ``` | 55 | ``` |
| 60 | 56 | ||
| 61 | ## Operator Description | 57 | ## Operator Description |
| @@ -97,12 +93,12 @@ for each rank, token: | |||
| 97 | 93 | ||
| 98 | ## Optimization Notes | 94 | ## Optimization Notes |
| 99 | 95 | ||
| 100 | -- **Expert-level overlap**: AIC-side GMM1/GMM2 and AIV-side Dispatch/SwiGLU/Combine progress by local expert group, connected by hard flags. | 96 | +- **Expert-level overlap**: AIC-side GMM1/GMM2 and AIV-side Dispatch/SwiGLU/Combine progress by local expert, connected by per-expert arrival/ready notifications. |
| 101 | - **Three front reorder paths**: FullLoad, OneCore, and MultiCore are selected by UB working-set size. Small-route cases keep sort/count/inverse/quant work in UB as much as possible. | 97 | - **Three front reorder paths**: FullLoad, OneCore, and MultiCore are selected by UB working-set size. Small-route cases keep sort/count/inverse/quant work in UB as much as possible. |
| 102 | - **Count-as-flag**: FrontReorder publishes count rows with a marker value so peer ranks can wait on data arrival directly instead of adding a full count-exchange barrier. | 98 | - **Count-as-flag**: FrontReorder publishes count rows with a marker value so peer ranks can wait on data arrival directly instead of adding a full count-exchange barrier. |
| 103 | - **PTO tile GMM optimization**: GMM1/GMM2 use output-tile swizzle, L1-to-L0 multi-level reuse, double buffering, and fixpipe quant/cast. | 99 | - **PTO tile GMM optimization**: GMM1/GMM2 use output-tile swizzle, L1-to-L0 multi-level reuse, double buffering, and fixpipe quant/cast. |
| 104 | -- **Segmented SwiGLU overlap**: SwiGLU is split into segments so the first segment can finish before GMM2 starts. | 100 | +- **Dynamic AIC grouping**: GMM1 starts with all 24 AICs and then releases 8; GMM2 starts with 8 AICs and expands to 24 after GMM1 completes. |
| 105 | -- **Dual combine paths**: large-token cases use full-row writeback, while small-token cases split GMM2 tiles into subtiles to improve AIV occupancy. | 101 | +- **Two-stage Unpermute**: 32 AIVs first process tokens whose routes are ready, then all 48 AIVs process the remaining tokens after Combine completes. |
| 106 | 102 | ||
| 107 | ## Tiling Parameters | 103 | ## Tiling Parameters |
| 108 | 104 | ||
| @@ -115,11 +111,11 @@ for each rank, token: | |||
| 115 | | `expertPerRank` | Number of local experts per rank | | 111 | | `expertPerRank` | Number of local experts per rank | |
| 116 | | `worldSize` | MPI/HCCL rank count | | 112 | | `worldSize` | MPI/HCCL rank count | |
| 117 | | `maxOutputSize` | Per-rank routed-row workspace limit; typical performance cases pass a fixed workspace limit explicitly | | 113 | | `maxOutputSize` | Per-rank routed-row workspace limit; typical performance cases pass a fixed workspace limit explicitly | |
| 118 | -| `aicNum` | Logical AIC count; default script value is 24 | | 114 | +| `aicNum` | Fixed physical AIC count for the production path: 24 | |
| 119 | -| `aivNum` | Logical AIV count; default script value is 48 | | 115 | +| `aivNum` | Fixed physical AIV count for the production path: 48 | |
| 120 | | `GMM baseM/baseN` | Main output tile shape is `128 x 256` | | 116 | | `GMM baseM/baseN` | Main output tile shape is `128 x 256` | |
| 121 | | `Front FullLoad` | Selected by `routeElems`, `K`, `expertNum`, and the 192 KiB UB budget | | 117 | | `Front FullLoad` | Selected by `routeElems`, `K`, `expertNum`, and the 192 KiB UB budget | |
| 122 | -| `Combine small path` | Preferred when `problemM * topK <= 4096` | | 118 | +| Fixed groups | GMM1/GMM2 use `16 + 8` AICs; Dispatch/SwiGLU/Combine have `16/16/8` AIV slots, with SwiGLU activating 8 or 16 by M | |
| 123 | 119 | ||
| 124 | ## Supported Cases | 120 | ## Supported Cases |
| 125 | 121 | ||
| @@ -152,19 +148,19 @@ aivNum=48 | |||
| 152 | │ FrontReorder (AIV) │ | 148 | │ FrontReorder (AIV) │ |
| 153 | │ sort expertId routes -> offsetA + count/prefix metadata │ | 149 | │ sort expertId routes -> offsetA + count/prefix metadata │ |
| 154 | └──────────────────────────────┬───────────────────────────────────────────────┘ | 150 | └──────────────────────────────┬───────────────────────────────────────────────┘ |
| 155 | - │ D2C ready / count-as-flag | 151 | + │ count metadata ready / count-as-flag |
| 156 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ | 152 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ |
| 157 | │ Expert-level overlapped pipeline │ | 153 | │ Expert-level overlapped pipeline │ |
| 158 | │ │ | 154 | │ │ |
| 159 | -│ AIV: Dispatch(group i) -> SwiGLU(segment/group i) -> Combine(group i) │ | 155 | +│ AIV: Dispatch(expert i) -> SwiGLU(expert i) -> Combine(expert i) │ |
| 160 | -│ AIC: GMM1(group i) -> GMM2(group i) │ | 156 | +│ AIC: GMM1 starts 24 -> 16; released AICs join GMM2, which grows 8 -> 24 │ |
| 161 | │ │ | 157 | │ │ |
| 162 | -│ Stages communicate through hard flags: D2C, C2V, V2C, G2C/Combine ready │ | 158 | +│ Stages communicate through per-expert GM arrival/ready progress │ |
| 163 | └──────────────────────────────┬───────────────────────────────────────────────┘ | 159 | └──────────────────────────────┬───────────────────────────────────────────────┘ |
| 164 | - │ final boundary | 160 | + │ per-rank expert progress / DataReady |
| 165 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ | 161 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ |
| 166 | -│ Unpermute (AIV) │ | 162 | +│ Two-stage Unpermute (AIV) │ |
| 167 | -│ offsetD + probs + expandedRowIdx -> TopK weighted reduce -> out[M, K] │ | 163 | +│ 32 AIVs process ready tokens -> 48 AIVs process remaining tokens -> out │ |
| 168 | └──────────────────────────────────────────────────────────────────────────────┘ | 164 | └──────────────────────────────────────────────────────────────────────────────┘ |
| 169 | ``` | 165 | ``` |
| 170 | 166 | ||
| @@ -211,7 +207,7 @@ srcRowBase = preSumBeforeRank[srcRank, localExpert] | |||
| 211 | dstRowBase = groupBase + (srcRank == 0 ? 0 : cumsumMM[srcRank - 1, localExpert]) | 207 | dstRowBase = groupBase + (srcRank == 0 ? 0 : cumsumMM[srcRank - 1, localExpert]) |
| 212 | ``` | 208 | ``` |
| 213 | 209 | ||
| 214 | -After each local expert group is gathered, Dispatch sets the GMM1-ready flag for that group. | 210 | +After each local expert is gathered, Dispatch coordinates its active workers and publishes GMM1 ready progress. |
| 215 | 211 | ||
| 216 | ## GMM1 / SwiGLU / GMM2 Stages | 212 | ## GMM1 / SwiGLU / GMM2 Stages |
| 217 | 213 | ||
| @@ -226,7 +222,7 @@ gmA[int8] x weight1[int8] | |||
| 226 | -> gmC[half] | 222 | -> gmC[half] |
| 227 | ``` | 223 | ``` |
| 228 | 224 | ||
| 229 | -Each local expert is split into `128 x 256` output tiles. Linear tile ids are mapped to `(blockM, blockN)` with swizzle to improve L1 reuse of the B-side weights. | 225 | +Each local expert is split into `128 x 256` output tiles. Linear tile ids are mapped to `(blockM, blockN)` with swizzle to improve L1 reuse of the B-side weights. The first two experts use all 24 AICs; subsequent experts use the 16-AIC GMM1 group. |
| 230 | 226 | ||
| 231 | ### SwiGLU | 227 | ### SwiGLU |
| 232 | 228 | ||
| @@ -239,7 +235,7 @@ gmC * perTokenScale1 | |||
| 239 | -> gmPermutedToken[int8] + perTokenScale2[float] | 235 | -> gmPermutedToken[int8] + perTokenScale2[float] |
| 240 | ``` | 236 | ``` |
| 241 | 237 | ||
| 242 | -SwiGLU is split into segments. Core0 writes segment metadata and the other AIVs split rows from that metadata. | 238 | +SwiGLU advances one expert at a time and splits its rows across the active fixed group. After all active AIVs finish an expert, its coordinator publishes GMM2 ready progress. M=16 uses 8 AIVs; the other listed cases use 16. |
| 243 | 239 | ||
| 244 | ### GMM2 | 240 | ### GMM2 |
| 245 | 241 | ||
| @@ -252,7 +248,7 @@ gmPermutedToken[int8] x weight2[int8] | |||
| 252 | -> gmm2Output[half] | 248 | -> gmm2Output[half] |
| 253 | ``` | 249 | ``` |
| 254 | 250 | ||
| 255 | -After GMM2 finishes a local expert group, it sets the combine-ready flag for that group. | 251 | +GMM2 starts on the 8-AIC group. Once GMM1 is done, its released 16 AICs join GMM2 at an expert boundary; each completed expert publishes arrival progress for Combine. |
| 256 | 252 | ||
| 257 | ## Combine / Unpermute Stages | 253 | ## Combine / Unpermute Stages |
| 258 | 254 | ||
| @@ -266,16 +262,14 @@ gmm2Output[srcRow, 0:K] half | |||
| 266 | -> srcRank.remoteWindow.offsetD[dstRow, 0:K] | 262 | -> srcRank.remoteWindow.offsetD[dstRow, 0:K] |
| 267 | ``` | 263 | ``` |
| 268 | 264 | ||
| 269 | -Path selection: | 265 | +The 8-AIV Combine group assigns full-row writeback by source rank. It publishes per-rank expert progress while running and publishes final DataReady after all experts are visible. |
| 270 | - | ||
| 271 | -- **DirectLarge**: full-row writeback for large-token cases. | ||
| 272 | -- **DirectSmall**: subtile writeback for small-token cases to improve AIV occupancy. | ||
| 273 | 266 | ||
| 274 | Unpermute restores the source-rank token order: | 267 | Unpermute restores the source-rank token order: |
| 275 | 268 | ||
| 276 | ```text | 269 | ```text |
| 277 | offsetD + probs + expandedRowIdx | 270 | offsetD + probs + expandedRowIdx |
| 278 | - -> TopK weighted accumulation | 271 | + -> phase 1: 32 AIVs reduce tokens whose routes are ready |
| 272 | + -> phase 2: 48 AIVs reduce the remaining tokens | ||
| 279 | -> out[M, K] | 273 | -> out[M, K] |
| 280 | ``` | 274 | ``` |
| 281 | 275 | ||
| @@ -288,6 +282,7 @@ The HCCL remote window carries cross-rank visible data: | |||
| 288 | | `offsetA` | HCCL window | FrontReorder writes packed int8 token rows; Dispatch pulls from peer ranks | | 282 | | `offsetA` | HCCL window | FrontReorder writes packed int8 token rows; Dispatch pulls from peer ranks | |
| 289 | | `offsetD` | HCCL window | Combine writes back to source ranks; Unpermute consumes locally | | 283 | | `offsetD` | HCCL window | Combine writes back to source ranks; Unpermute consumes locally | |
| 290 | | `tokenPerExpert` | HCCL window | count-as-flag cross-rank count rows | | 284 | | `tokenPerExpert` | HCCL window | count-as-flag cross-rank count rows | |
| 285 | +| `ExpertProgress / DataReady` | HCCL window | Per-rank Combine progress and launch-scoped completion notification | | ||
| 291 | | `gmA` | workspace GM | GMM1 input generated by Dispatch | | 286 | | `gmA` | workspace GM | GMM1 input generated by Dispatch | |
| 292 | | `gmC` | workspace GM | GMM1 output and SwiGLU input | | 287 | | `gmC` | workspace GM | GMM1 output and SwiGLU input | |
| 293 | | `gmPermutedToken` | workspace GM | SwiGLU dynamic-quant output and GMM2 input | | 288 | | `gmPermutedToken` | workspace GM | SwiGLU dynamic-quant output and GMM2 input | |
| @@ -321,7 +316,7 @@ Small route counts should prefer FullLoad, which reduces GM intermediate traffic | |||
| 321 | 316 | ||
| 322 | ### 2. Keep Expert-Level Stage Boundaries Clear | 317 | ### 2. Keep Expert-Level Stage Boundaries Clear |
| 323 | 318 | ||
| 324 | -Dispatch, GMM1, SwiGLU, GMM2, and Combine depend on hard flags. Before removing any synchronization, verify the exact per-group set/wait relationship. | 319 | +Dispatch, GMM1, SwiGLU, GMM2, and Combine use producer arrival and consumer ready slots. Before changing synchronization, verify the producer count, consumer range, and expected expert progress at each boundary. |
| 325 | 320 | ||
| 326 | ### 3. Prioritize GMM Tile Efficiency | 321 | ### 3. Prioritize GMM Tile Efficiency |
| 327 | 322 | ||
| @@ -332,9 +327,9 @@ GMM1/GMM2 dominate runtime. Check: | |||
| 332 | - whether small `currentM` causes AIC imbalance; | 327 | - whether small `currentM` causes AIC imbalance; |
| 333 | - whether AIV communication/writeback competes with GMM HBM traffic. | 328 | - whether AIV communication/writeback competes with GMM HBM traffic. |
| 334 | 329 | ||
| 335 | -### 4. Use Subtile Combine for Small Token Counts | 330 | +### 4. Balance Combine Against GMM2 |
| 336 | 331 | ||
| 337 | -For small M, the direct row path may underuse AIVs. DirectSmall splits GMM2 tiles into subtiles, but requires the GMM2 tiling column width to align with the small-path subtile width. | 332 | +Combine uses 8 AIVs and a shape-specific delayed start to avoid competing with GMM2 for HBM bandwidth. When tuning small M, check the GMM2 join point, Combine start expert, and SwiGLU active-worker count together. |
| 338 | 333 | ||
| 339 | ### 5. Use Batch Golden Generation | 334 | ### 5. Use Batch Golden Generation |
| 340 | 335 | ||
| @@ -345,9 +340,7 @@ Large synthetic cases use the `python-batch` golden backend by default. Use `pyt | |||
| 345 | Configure the Ascend CANN environment: | 340 | Configure the Ascend CANN environment: |
| 346 | 341 | ||
| 347 | ```bash | 342 | ```bash |
| 348 | -export ASCEND_CANN_PATH=/usr/local/Ascend/cann/set_env.sh | 343 | +source <cann-install>/set_env.sh |
| 349 | -export ASCEND_HOME_PATH=/usr/local/Ascend/cann/cann | ||
| 350 | -source /usr/local/Ascend/cann/cann/set_env.sh | ||
| 351 | ``` | 344 | ``` |
| 352 | 345 | ||
| 353 | Run the default 2048 case: | 346 | Run the default 2048 case: |
| @@ -369,13 +362,13 @@ bash run.sh --world-size 8 --m 512 --k 7168 --n 4096 --topk 8 --experts 16 --max | |||
| 369 | 362 | ||
| 370 | | Environment Variable | Purpose | Default Behavior | | 363 | | Environment Variable | Purpose | Default Behavior | |
| 371 | | --- | --- | --- | | 364 | | --- | --- | --- | |
| 372 | -| `ASCEND_HOME_PATH` | CANN installation path | Must be set before running | | 365 | +| `ASCEND_HOME_PATH` | Active CANN installation path | Set by the CANN `set_env.sh` | |
| 373 | -| `CMAKE_COMPILER` | Compiler used by CMake | `bisheng` | | ||
| 374 | | `MPI_ENV_BIN` | MPI/conda `bin` path | `/home/ntlab/miniconda3/envs/ltr_pto/bin` | | 366 | | `MPI_ENV_BIN` | MPI/conda `bin` path | `/home/ntlab/miniconda3/envs/ltr_pto/bin` | |
| 375 | | `MPI_ENV_LIB` | MPI/conda `lib` path | `/home/ntlab/miniconda3/envs/ltr_pto/lib` | | 367 | | `MPI_ENV_LIB` | MPI/conda `lib` path | `/home/ntlab/miniconda3/envs/ltr_pto/lib` | |
| 376 | | `MPI_LIB_PATH` | Absolute path to `libmpi.so` | `${MPI_ENV_LIB}/libmpi.so` | | 368 | | `MPI_LIB_PATH` | Absolute path to `libmpi.so` | `${MPI_ENV_LIB}/libmpi.so` | |
| 377 | | `MPI_RUNNER` | MPI launch command | `mpirun` | | 369 | | `MPI_RUNNER` | MPI launch command | `mpirun` | |
| 378 | | `HCCL_BUFFSIZE` | HCCL RDMA window size | Raised automatically by `run.sh` when needed | | 370 | | `HCCL_BUFFSIZE` | HCCL RDMA window size | Raised automatically by `run.sh` when needed | |
| 371 | +| `DISPATCH_MEGA_COMBINE_START_SYNC_DEBUG` | Synchronize kernel entry for cross-core timing comparison | Disabled | | ||
| 379 | 372 | ||
| 380 | ## Changing Case Parameters | 373 | ## Changing Case Parameters |
| 381 | 374 | ||
| @@ -400,7 +393,7 @@ Common constraints: | |||
| 400 | | HCCL window too small | The manually set `HCCL_BUFFSIZE` is below the case requirement; unset it or increase it | | 393 | | HCCL window too small | The manually set `HCCL_BUFFSIZE` is below the case requirement; unset it or increase it | |
| 401 | | MPI launch fails | Check that `MPI_ENV_BIN`, `MPI_ENV_LIB`, and `MPI_LIB_PATH` point to the same conda/MPI environment | | 394 | | MPI launch fails | Check that `MPI_ENV_BIN`, `MPI_ENV_LIB`, and `MPI_LIB_PATH` point to the same conda/MPI environment | |
| 402 | | Golden generation is slow | Use the default `python-batch` backend; use `python-naive` only for debug comparison | | 395 | | Golden generation is slow | Use the default `python-batch` backend; use `python-naive` only for debug comparison | |
| 403 | -| Small-M performance is unstable | Check whether FullLoad is selected, whether Combine uses DirectSmall, and whether AIV concurrency is hurting GMM | | 396 | +| Small-M performance is unstable | Check whether FullLoad is selected and whether the configured SwiGLU workers, GMM2 join point, and Combine start point increase HBM contention | |
| 404 | | Result diff is abnormal | Check whether old generated data was reused; do not reuse stale `out/` after changing expert distribution or key case parameters | | 397 | | Result diff is abnormal | Check whether old generated data was reused; do not reuse stale `out/` after changing expert distribution or key case parameters | |
| 405 | 398 | ||
| 406 | ## Build System | 399 | ## Build System |
| @@ -10,7 +10,7 @@ | |||
| 10 | - Ascend910B / Ascend910C | 10 | - Ascend910B / Ascend910C |
| 11 | - Ascend910_93 / Ascend910_9391 / Ascend910_9381 / Ascend910_9372 / Ascend910_9392 / Ascend910_9382 / Ascend910_9362 | 11 | - Ascend910_93 / Ascend910_9391 / Ascend910_9381 / Ascend910_9372 / Ascend910_9392 / Ascend910_9382 / Ascend910_9362 |
| 12 | 12 | ||
| 13 | -> 当前目录位于 `a2a3` 手写 kernel 下,性能和运行验证主要面向 A2/A3 形态。`CMakeLists.txt` 中也保留了 `Ascend910_9599` 的 `dav-c310` 编译分支,但使用前需要按目标环境重新验证。 | 13 | +当前目录位于 `a2a3` 手写 kernel 下。典型性能 case 使用脚本默认的 A2/A3 配置,命令中无需显式指定 SoC。 |
| 14 | 14 | ||
| 15 | ## 目录结构 | 15 | ## 目录结构 |
| 16 | 16 | ||
| @@ -40,14 +40,10 @@ kernels/manual/a2a3/dispatch_mega_combine/ | |||
| 40 | │ ├── combine.h # GMM2 输出远端写回 offsetD | 40 | │ ├── combine.h # GMM2 输出远端写回 offsetD |
| 41 | │ ├── unpermute.h # topK weighted reduce 和原 token 顺序还原 | 41 | │ ├── unpermute.h # topK weighted reduce 和原 token 顺序还原 |
| 42 | │ └── utils/ # PTO vector、sync、HCCL window、GMM pipeline helper | 42 | │ └── utils/ # PTO vector、sync、HCCL window、GMM pipeline helper |
| 43 | -├── overview.md # 总体设计、性能对比和阶段伪码 | 43 | +├── overview.md # 总体设计和性能对比 |
| 44 | -├── front_reorder.md # front reorder / sort / count-as-flag 细节 | 44 | +├── overview_v1.md # 固定分组调度和 overlap 设计 |
| 45 | -├── dispatch.md # dispatch 阶段契约和搬运策略 | 45 | +├── pseudocode.md # 当前七阶段数据流和伪码 |
| 46 | -├── gmm1.md / gmm2.md # GMM tile 调度、swizzle、同步和 pipeline | 46 | +└── README.md # 英文 README |
| 47 | -├── swiglu.md # SwiGLU 分段和量化策略 | ||
| 48 | -├── combine.md # combine large/small path 和远端写回协议 | ||
| 49 | -├── unpermute.md # unpermute 还原与累加策略 | ||
| 50 | -└── glden.md # Python golden batch rewrite 设计 | ||
| 51 | ``` | 47 | ``` |
| 52 | 48 | ||
| 53 | ## 算子说明 | 49 | ## 算子说明 |
| @@ -89,12 +85,12 @@ for each rank, token: | |||
| 89 | 85 | ||
| 90 | ## 优化说明 | 86 | ## 优化说明 |
| 91 | 87 | ||
| 92 | -- **expert 级流水重叠**:AIC 侧 GMM1/GMM2 和 AIV 侧 dispatch/SwiGLU/combine 按 local expert group 轮转推进,通过 hard flag 串接阶段边界。 | 88 | +- **expert 级流水重叠**:AIC 侧 GMM1/GMM2 和 AIV 侧 Dispatch/SwiGLU/Combine 按 local expert 推进,通过逐 expert 的 arrival/ready 通知串接阶段边界。 |
| 93 | - **front reorder 三路径**:按 UB 工作集大小选择 FullLoad、OneCore、MultiCore。小 route 尽量留在 UB 内完成排序、count、反排和 quant,避免不必要的 GM 中间态。 | 89 | - **front reorder 三路径**:按 UB 工作集大小选择 FullLoad、OneCore、MultiCore。小 route 尽量留在 UB 内完成排序、count、反排和 quant,避免不必要的 GM 中间态。 |
| 94 | - **count-as-flag**:front 发布 `tokenPerExpert` 时给 count row 加 marker,peer 通过数据值等待到达,减少 AlltoAll count 后的整机同步。 | 90 | - **count-as-flag**:front 发布 `tokenPerExpert` 时给 count row 加 marker,peer 通过数据值等待到达,减少 AlltoAll count 后的整机同步。 |
| 95 | - **GMM PTO tile 优化**:GMM1/GMM2 使用 PTO tile 编程,包含 output tile swizzle、L1 -> L0 多级复用、双缓冲和 fixpipe quant/cast。 | 91 | - **GMM PTO tile 优化**:GMM1/GMM2 使用 PTO tile 编程,包含 output tile swizzle、L1 -> L0 多级复用、双缓冲和 fixpipe quant/cast。 |
| 96 | -- **SwiGLU 分段 overlap**:SwiGLU 按 segment 切分,第一段尽量压到 GMM2 启动前完成,降低 GMM1 -> GMM2 中间等待。 | 92 | +- **AIC 动态分组**:GMM1 先使用全部 24 个 AIC,随后释放 8 个;GMM2 从 8 个 AIC 起步,在 GMM1 完成后扩展到 24 个。 |
| 97 | -- **combine 双路径**:large token path 按完整 row 写回,small token path 按 GMM2 tile 拆 subtile,提高小 M 场景 AIV 利用率。 | 93 | +- **两阶段 Unpermute**:前 32 个 AIV 先处理 route 已就绪的 token,Combine 全部完成后由 48 个 AIV 处理剩余 token。 |
| 98 | 94 | ||
| 99 | ## Tiling 参数 | 95 | ## Tiling 参数 |
| 100 | 96 | ||
| @@ -107,11 +103,11 @@ for each rank, token: | |||
| 107 | | `expertPerRank` | 每 rank 本地 expert 数 | | 103 | | `expertPerRank` | 每 rank 本地 expert 数 | |
| 108 | | `worldSize` | MPI/HCCL rank 数 | | 104 | | `worldSize` | MPI/HCCL rank 数 | |
| 109 | | `maxOutputSize` | 每 rank routed row workspace 上限;典型性能 case 显式使用固定 workspace 上限 | | 105 | | `maxOutputSize` | 每 rank routed row workspace 上限;典型性能 case 显式使用固定 workspace 上限 | |
| 110 | -| `aicNum` | AIC 逻辑核数,默认脚本为 24 | | 106 | +| `aicNum` | 生产路径固定物理 AIC 数:24 | |
| 111 | -| `aivNum` | AIV 逻辑核数,默认脚本为 48 | | 107 | +| `aivNum` | 生产路径固定物理 AIV 数:48 | |
| 112 | | `GMM baseM/baseN` | 主要 tile 口径为 `128 x 256` output tile | | 108 | | `GMM baseM/baseN` | 主要 tile 口径为 `128 x 256` output tile | |
| 113 | | `Front FullLoad` | 由 `routeElems`、`K`、`expertNum` 和 UB 192 KiB 预算共同决定 | | 109 | | `Front FullLoad` | 由 `routeElems`、`K`、`expertNum` 和 UB 192 KiB 预算共同决定 | |
| 114 | -| `Combine small path` | `problemM * topK <= 4096` 时倾向使用 subtile path | | 110 | +| 固定分组 | GMM1/GMM2 使用 `16 + 8` 个 AIC;Dispatch/SwiGLU/Combine 预留 `16/16/8` 个 AIV slot,SwiGLU 按 M 激活 8 或 16 个 | |
| 115 | 111 | ||
| 116 | ## 支持 Case | 112 | ## 支持 Case |
| 117 | 113 | ||
| @@ -146,23 +142,23 @@ aivNum=48 | |||
| 146 | │ FrontReorder (AIV) │ | 142 | │ FrontReorder (AIV) │ |
| 147 | │ sort expertId route -> offsetA + count/prefix metadata │ | 143 | │ sort expertId route -> offsetA + count/prefix metadata │ |
| 148 | └──────────────────────────────┬───────────────────────────────────────────────┘ | 144 | └──────────────────────────────┬───────────────────────────────────────────────┘ |
| 149 | - │ D2C ready / count-as-flag | 145 | + │ count metadata ready / count-as-flag |
| 150 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ | 146 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ |
| 151 | -│ Expert-level overlapped pipeline │ | 147 | +│ Expert 级重叠流水 │ |
| 152 | │ │ | 148 | │ │ |
| 153 | -│ AIV: Dispatch(group i) -> SwiGLU(segment/group i) -> Combine(group i) │ | 149 | +│ AIV: Dispatch(expert i) -> SwiGLU(expert i) -> Combine(expert i) │ |
| 154 | -│ AIC: GMM1(group i) -> GMM2(group i) │ | 150 | +│ AIC: GMM1 从 24 缩为 16;释放的 AIC 加入 GMM2,使其从 8 扩为 24 │ |
| 155 | │ │ | 151 | │ │ |
| 156 | -│ Stages communicate with hard flags: D2C, C2V, V2C, G2C/Combine ready │ | 152 | +│ 各阶段通过逐 expert 的 GM arrival/ready 进度衔接 │ |
| 157 | └──────────────────────────────┬───────────────────────────────────────────────┘ | 153 | └──────────────────────────────┬───────────────────────────────────────────────┘ |
| 158 | - │ final boundary | 154 | + │ 各 rank expert 进度 / DataReady |
| 159 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ | 155 | ┌──────────────────────────────▼───────────────────────────────────────────────┐ |
| 160 | -│ Unpermute (AIV) │ | 156 | +│ 两阶段 Unpermute (AIV) │ |
| 161 | -│ offsetD + probs + expandedRowIdx -> topK weighted reduce -> out[M, K] │ | 157 | +│ 32 个 AIV 处理已就绪 token -> 48 个 AIV 处理剩余 token -> out │ |
| 162 | └──────────────────────────────────────────────────────────────────────────────┘ | 158 | └──────────────────────────────────────────────────────────────────────────────┘ |
| 163 | ``` | 159 | ``` |
| 164 | 160 | ||
| 165 | -完整执行顺序在 `MegaMoe::Process()` 中串联,阶段顺序为: | 161 | +`MegaMoe::Process()` 先完成 FrontReorder,再由固定分组按以下依赖关系并发推进: |
| 166 | 162 | ||
| 167 | ```text | 163 | ```text |
| 168 | FrontReorder -> Dispatch -> GMM1 -> SwiGLU -> GMM2 -> Combine -> Unpermute | 164 | FrontReorder -> Dispatch -> GMM1 -> SwiGLU -> GMM2 -> Combine -> Unpermute |
| @@ -211,7 +207,7 @@ srcRowBase = preSumBeforeRank[srcRank, localExpert] | |||
| 211 | dstRowBase = groupBase + (srcRank == 0 ? 0 : cumsumMM[srcRank - 1, localExpert]) | 207 | dstRowBase = groupBase + (srcRank == 0 ? 0 : cumsumMM[srcRank - 1, localExpert]) |
| 212 | ``` | 208 | ``` |
| 213 | 209 | ||
| 214 | -每个 local expert group 搬运完成后,dispatch 设置 GMM1 ready flag,允许 AIC 开始消费该 group。 | 210 | +每个 local expert 搬运完成后,Dispatch 汇聚活跃 worker 的到达状态并发布 GMM1 ready 进度。 |
| 215 | 211 | ||
| 216 | ## GMM1 / SwiGLU / GMM2 阶段 | 212 | ## GMM1 / SwiGLU / GMM2 阶段 |
| 217 | 213 | ||
| @@ -226,7 +222,7 @@ gmA[int8] x weight1[int8] | |||
| 226 | -> gmC[half] | 222 | -> gmC[half] |
| 227 | ``` | 223 | ``` |
| 228 | 224 | ||
| 229 | -每个 local expert 的输出 tile 网格按 `128 x 256` output tile 切分。线性 tile id 会通过 swizzle 映射到 `(blockM, blockN)`,让相邻 tile 更容易复用 L1 中的 B 侧权重。 | 225 | +每个 local expert 的输出 tile 网格按 `128 x 256` output tile 切分。线性 tile id 会通过 swizzle 映射到 `(blockM, blockN)`,让相邻 tile 更容易复用 L1 中的 B 侧权重。前两个 expert 使用全部 24 个 AIC,后续 expert 使用 16-AIC GMM1 组。 |
| 230 | 226 | ||
| 231 | ### SwiGLU | 227 | ### SwiGLU |
| 232 | 228 | ||
| @@ -239,7 +235,7 @@ gmC * perTokenScale1 | |||
| 239 | -> gmPermutedToken[int8] + perTokenScale2[float] | 235 | -> gmPermutedToken[int8] + perTokenScale2[float] |
| 240 | ``` | 236 | ``` |
| 241 | 237 | ||
| 242 | -SwiGLU 按 segment 切分;core0 写 segment metadata,其它 AIV 读取后按 row 分担计算。 | 238 | +SwiGLU 按 expert 推进,并在固定活跃组内按 row 分工。全部活跃 AIV 完成当前 expert 后,由 coordinator 发布 GMM2 ready 进度;M=16 使用 8 个 AIV,其余表中 case 使用 16 个。 |
| 243 | 239 | ||
| 244 | ### GMM2 | 240 | ### GMM2 |
| 245 | 241 | ||
| @@ -252,7 +248,7 @@ gmPermutedToken[int8] x weight2[int8] | |||
| 252 | -> gmm2Output[half] | 248 | -> gmm2Output[half] |
| 253 | ``` | 249 | ``` |
| 254 | 250 | ||
| 255 | -GMM2 完成每个 local expert group 后设置 combine ready flag,AIV combine 才能写回该 group。 | 251 | +GMM2 从 8-AIC 组开始执行。GMM1 完成后,其释放的 16 个 AIC 在 expert 边界加入 GMM2;每个 expert 完成后发布 arrival 进度供 Combine 消费。 |
| 256 | 252 | ||
| 257 | ## Combine / Unpermute 阶段 | 253 | ## Combine / Unpermute 阶段 |
| 258 | 254 | ||
| @@ -266,17 +262,14 @@ gmm2Output[srcRow, 0:K] half | |||
| 266 | -> srcRank.remoteWindow.offsetD[dstRow, 0:K] | 262 | -> srcRank.remoteWindow.offsetD[dstRow, 0:K] |
| 267 | ``` | 263 | ``` |
| 268 | 264 | ||
| 269 | -路径选择: | 265 | +8-AIV Combine 组按 source rank 分配完整 row 写回。执行过程中发布各 rank 的 expert 进度,全部 expert 可见后发布最终 DataReady。 |
| 270 | - | ||
| 271 | -- **DirectLarge**:大 token 量场景,按完整 row 写回。 | ||
| 272 | -- **DirectSmall**:小 token 量场景,按 subtile 拆分,提升 AIV 并行度。 | ||
| 273 | - | ||
| 274 | 266 | ||
| 275 | Unpermute 是最后的源 rank 还原阶段: | 267 | Unpermute 是最后的源 rank 还原阶段: |
| 276 | 268 | ||
| 277 | ```text | 269 | ```text |
| 278 | offsetD + probs + expandedRowIdx | 270 | offsetD + probs + expandedRowIdx |
| 279 | - -> 按原 token/topK 加权累加 | 271 | + -> 第一阶段:32 个 AIV 处理 route 已就绪的 token |
| 272 | + -> 第二阶段:48 个 AIV 处理剩余 token | ||
| 280 | -> out[M, K] | 273 | -> out[M, K] |
| 281 | ``` | 274 | ``` |
| 282 | 275 | ||
| @@ -289,6 +282,7 @@ HCCL remote window 主要承载跨 rank 可见的数据: | |||
| 289 | | `offsetA` | HCCL window | FrontReorder 写入 packed int8 token row,Dispatch 从 peer 拉取 | | 282 | | `offsetA` | HCCL window | FrontReorder 写入 packed int8 token row,Dispatch 从 peer 拉取 | |
| 290 | | `offsetD` | HCCL window | Combine 写回源 rank,Unpermute 在源 rank 消费 | | 283 | | `offsetD` | HCCL window | Combine 写回源 rank,Unpermute 在源 rank 消费 | |
| 291 | | `tokenPerExpert` | HCCL window | count-as-flag 的跨 rank count row | | 284 | | `tokenPerExpert` | HCCL window | count-as-flag 的跨 rank count row | |
| 285 | +| `ExpertProgress / DataReady` | HCCL window | 各 rank 的 Combine 进度和当前 launch 完成通知 | | ||
| 292 | | `gmA` | workspace GM | Dispatch 生成的 GMM1 输入 | | 286 | | `gmA` | workspace GM | Dispatch 生成的 GMM1 输入 | |
| 293 | | `gmC` | workspace GM | GMM1 输出,SwiGLU 输入 | | 287 | | `gmC` | workspace GM | GMM1 输出,SwiGLU 输入 | |
| 294 | | `gmPermutedToken` | workspace GM | SwiGLU dynamic quant 后的 GMM2 输入 | | 288 | | `gmPermutedToken` | workspace GM | SwiGLU dynamic quant 后的 GMM2 输入 | |
| @@ -322,7 +316,7 @@ overview.md | |||
| 322 | 316 | ||
| 323 | ### 2. 保持 expert 级阶段边界清晰 | 317 | ### 2. 保持 expert 级阶段边界清晰 |
| 324 | 318 | ||
| 325 | -Dispatch、GMM1、SwiGLU、GMM2、Combine 之间依赖 hard flag。优化时优先确认每个 group 的 set/wait 是否一一匹配,避免为了减少 `SYNCALL` 破坏跨 AIC/AIV 的真实数据依赖。 | 319 | +Dispatch、GMM1、SwiGLU、GMM2、Combine 之间使用 producer arrival 和 consumer ready slot。修改同步前需要确认每个边界的 producer 数、consumer 范围和期望 expert 进度。 |
| 326 | 320 | ||
| 327 | ### 3. 优先优化 GMM tile 效率 | 321 | ### 3. 优先优化 GMM tile 效率 |
| 328 | 322 | ||
| @@ -333,9 +327,9 @@ GMM1/GMM2 是主耗时阶段。重点关注: | |||
| 333 | - `currentM` 较小时 AIC 是否负载不均; | 327 | - `currentM` 较小时 AIC 是否负载不均; |
| 334 | - AIV 通信/写回是否与 GMM HBM 访问冲突。 | 328 | - AIV 通信/写回是否与 GMM HBM 访问冲突。 |
| 335 | 329 | ||
| 336 | -### 4. 小 token 场景使用 subtile combine | 330 | +### 4. 平衡 Combine 与 GMM2 |
| 337 | 331 | ||
| 338 | -小 M 下 direct row path 容易让 AIV 并行度不足。DirectSmall 通过 subtile 切分提高核利用率,但需要保持 `gmm2Tiling.l1TileN` 与 small path subtile 列宽对齐。 | 332 | +Combine 固定使用 8 个 AIV,并按 shape 延后启动,避免与 GMM2 竞争 HBM 带宽。调优小 M 时需要一起检查 GMM2 扩组点、Combine 启动 expert 和 SwiGLU 活跃 worker 数。 |
| 339 | 333 | ||
| 340 | ### 5. Golden 生成使用 batch backend | 334 | ### 5. Golden 生成使用 batch backend |
| 341 | 335 | ||
| @@ -346,9 +340,7 @@ large synthetic case 默认使用 `python-batch` golden backend,避免逐 toke | |||
| 346 | 配置 Ascend CANN 环境: | 340 | 配置 Ascend CANN 环境: |
| 347 | 341 | ||
| 348 | ```bash | 342 | ```bash |
| 349 | -export ASCEND_CANN_PATH=/usr/local/Ascend/cann/set_env.sh | 343 | +source <cann-install>/set_env.sh |
| 350 | -export ASCEND_HOME_PATH=/usr/local/Ascend/cann/cann | ||
| 351 | -source /usr/local/Ascend/cann/cann/set_env.sh | ||
| 352 | ``` | 344 | ``` |
| 353 | 345 | ||
| 354 | 运行默认 2048 case: | 346 | 运行默认 2048 case: |
| @@ -370,13 +362,13 @@ bash run.sh --world-size 8 --m 512 --k 7168 --n 4096 --topk 8 --experts 16 --max | |||
| 370 | 362 | ||
| 371 | | 环境变量 | 用途 | 默认行为 | | 363 | | 环境变量 | 用途 | 默认行为 | |
| 372 | | --- | --- | --- | | 364 | | --- | --- | --- | |
| 373 | -| `ASCEND_HOME_PATH` | CANN 安装目录 | 必须提前设置 | | 365 | +| `ASCEND_HOME_PATH` | 当前 CANN 安装目录 | 由 CANN `set_env.sh` 设置 | |
| 374 | -| `CMAKE_COMPILER` | CMake 使用的编译器 | `bisheng` | | ||
| 375 | | `MPI_ENV_BIN` | MPI/conda bin 路径 | `/home/ntlab/miniconda3/envs/ltr_pto/bin` | | 366 | | `MPI_ENV_BIN` | MPI/conda bin 路径 | `/home/ntlab/miniconda3/envs/ltr_pto/bin` | |
| 376 | | `MPI_ENV_LIB` | MPI/conda lib 路径 | `/home/ntlab/miniconda3/envs/ltr_pto/lib` | | 367 | | `MPI_ENV_LIB` | MPI/conda lib 路径 | `/home/ntlab/miniconda3/envs/ltr_pto/lib` | |
| 377 | | `MPI_LIB_PATH` | `libmpi.so` 绝对路径 | `${MPI_ENV_LIB}/libmpi.so` | | 368 | | `MPI_LIB_PATH` | `libmpi.so` 绝对路径 | `${MPI_ENV_LIB}/libmpi.so` | |
| 378 | | `MPI_RUNNER` | MPI 启动命令 | `mpirun` | | 369 | | `MPI_RUNNER` | MPI 启动命令 | `mpirun` | |
| 379 | | `HCCL_BUFFSIZE` | HCCL RDMA window 大小 | `run.sh` 按 case 自动抬高到安全值 | | 370 | | `HCCL_BUFFSIZE` | HCCL RDMA window 大小 | `run.sh` 按 case 自动抬高到安全值 | |
| 371 | +| `DISPATCH_MEGA_COMBINE_START_SYNC_DEBUG` | 同步 kernel 入口,用于比较跨核计时 | 默认关闭 | | ||
| 380 | 372 | ||
| 381 | ## 修改 Case 参数 | 373 | ## 修改 Case 参数 |
| 382 | 374 | ||
| @@ -401,7 +393,7 @@ bash run.sh --world-size 8 --m 512 --k 7168 --n 4096 --topk 8 --experts 16 --max | |||
| 401 | | HCCL window too small | 手动设置的 `HCCL_BUFFSIZE` 低于 case 需求;取消覆盖或调大该变量 | | 393 | | HCCL window too small | 手动设置的 `HCCL_BUFFSIZE` 低于 case 需求;取消覆盖或调大该变量 | |
| 402 | | MPI 启动失败 | 检查 `MPI_ENV_BIN`、`MPI_ENV_LIB`、`MPI_LIB_PATH` 是否指向同一个 conda/MPI 环境 | | 394 | | MPI 启动失败 | 检查 `MPI_ENV_BIN`、`MPI_ENV_LIB`、`MPI_LIB_PATH` 是否指向同一个 conda/MPI 环境 | |
| 403 | | golden 生成很慢 | 使用默认 `python-batch`,必要时调大 `--golden-chunk-rows`;只有调试对照才使用 `python-naive` | | 395 | | golden 生成很慢 | 使用默认 `python-batch`,必要时调大 `--golden-chunk-rows`;只有调试对照才使用 `python-naive` | |
| 404 | -| 小 M 性能不稳定 | 优先检查 FullLoad case 是否命中、combine 是否走 DirectSmall、AIV 并发是否过高影响 GMM | | 396 | +| 小 M 性能不稳定 | 检查 FullLoad 是否命中,以及 SwiGLU worker 数、GMM2 扩组点和 Combine 启动点是否加重 HBM 竞争 | |
| 405 | | 结果 diff 异常 | 先检查 data cache 是否复用旧分布;改变 expert 分布或 case 关键参数后不要使用旧 `out/` | | 397 | | 结果 diff 异常 | 先检查 data cache 是否复用旧分布;改变 expert 分布或 case 关键参数后不要使用旧 `out/` | |
| 406 | 398 | ||
| 407 | ## 构建系统 | 399 | ## 构建系统 |
| @@ -131,14 +131,20 @@ CaseConfig LoadCaseConfig(const std::string& case_json_path) | |||
| 131 | cfg.aiv_num = ParseJsonUInt(text, "aiv_num"); | 131 | cfg.aiv_num = ParseJsonUInt(text, "aiv_num"); |
| 132 | cfg.compare_atol = ParseJsonDouble(text, "compare_atol", 1e-3); | 132 | cfg.compare_atol = ParseJsonDouble(text, "compare_atol", 1e-3); |
| 133 | cfg.compare_rtol = ParseJsonDouble(text, "compare_rtol", 1e-3); | 133 | cfg.compare_rtol = ParseJsonDouble(text, "compare_rtol", 1e-3); |
| 134 | - cfg.input_tokens_all_ranks = | 134 | + const double defaultInputTokens = static_cast<double>(cfg.m) * cfg.world_size; |
| 135 | - ParseJsonDouble(text, "input_tokens_all_ranks", static_cast<double>(cfg.m) * cfg.world_size); | 135 | + cfg.input_tokens_all_ranks = ParseJsonDouble(text, "input_tokens_all_ranks", defaultInputTokens); |
| 136 | - cfg.routed_tokens_all_ranks = | 136 | + const double defaultRoutedTokens = cfg.input_tokens_all_ranks * cfg.topk; |
| 137 | - ParseJsonDouble(text, "routed_tokens_all_ranks", static_cast<double>(cfg.m) * cfg.topk * cfg.world_size); | 137 | + cfg.routed_tokens_all_ranks = ParseJsonDouble(text, "routed_tokens_all_ranks", defaultRoutedTokens); |
| 138 | - cfg.remote_routed_tokens_all_ranks = ParseJsonDouble(text, "remote_routed_tokens_all_ranks", 0.0); | 138 | + const double defaultRemoteRoutedTokens = |
| 139 | - cfg.compute_flops_all_ranks = | 139 | + cfg.world_size == 0U ? 0.0 : |
| 140 | - ParseJsonDouble(text, "compute_flops_all_ranks", cfg.routed_tokens_all_ranks * 3.0 * cfg.k * cfg.n); | 140 | + cfg.routed_tokens_all_ranks * static_cast<double>(cfg.world_size - 1U) / cfg.world_size; |
| 141 | - cfg.comm_bytes_all_ranks = ParseJsonDouble(text, "comm_bytes_all_ranks", 0.0); | 141 | + cfg.remote_routed_tokens_all_ranks = |
| 142 | + ParseJsonDouble(text, "remote_routed_tokens_all_ranks", defaultRemoteRoutedTokens); | ||
| 143 | + const double defaultComputeFlops = cfg.routed_tokens_all_ranks * 3.0 * cfg.k * cfg.n; | ||
| 144 | + cfg.compute_flops_all_ranks = ParseJsonDouble(text, "compute_flops_all_ranks", defaultComputeFlops); | ||
| 145 | + const double bytesPerRemoteRoute = static_cast<double>(cfg.k) * 3.0 + sizeof(float); | ||
| 146 | + cfg.comm_bytes_all_ranks = | ||
| 147 | + ParseJsonDouble(text, "comm_bytes_all_ranks", cfg.remote_routed_tokens_all_ranks * bytesPerRemoteRoute); | ||
| 142 | return cfg; | 148 | return cfg; |
| 143 | } | 149 | } |
| 144 | 150 | ||
| @@ -146,9 +152,8 @@ RankFileSet BuildRankFileSet(const std::string& case_dir, int rank) | |||
| 146 | { | 152 | { |
| 147 | const std::string prefix = case_dir + "/rank" + std::to_string(rank) + "_"; | 153 | const std::string prefix = case_dir + "/rank" + std::to_string(rank) + "_"; |
| 148 | return RankFileSet{ | 154 | return RankFileSet{ |
| 149 | - prefix + "x.bin", prefix + "weight1.bin", prefix + "weight2.bin", | 155 | + prefix + "x.bin", prefix + "weight1.bin", prefix + "weight2.bin", prefix + "expert_idx.bin", |
| 150 | - prefix + "expert_idx.bin", prefix + "scale1.bin", prefix + "scale2.bin", | 156 | + prefix + "scale1.bin", prefix + "scale2.bin", prefix + "probs.bin", prefix + "expected_out.bin", |
| 151 | - prefix + "probs.bin", prefix + "x_active_mask.bin", prefix + "expected_out.bin", | ||
| 152 | }; | 157 | }; |
| 153 | } | 158 | } |
| 154 | 159 | ||
| @@ -25,7 +25,6 @@ struct RankFileSet { | |||
| 25 | std::string scale1; | 25 | std::string scale1; |
| 26 | std::string scale2; | 26 | std::string scale2; |
| 27 | std::string probs; | 27 | std::string probs; |
| 28 | - std::string x_active_mask; | ||
| 29 | std::string expected_out; | 28 | std::string expected_out; |
| 30 | }; | 29 | }; |
| 31 | 30 | ||
| @@ -49,11 +49,6 @@ extern "C" __global__ __aicore__ void dispatch_mega_combine_kernel( | |||
| 49 | GM_ADDR probs, GM_ADDR c, GM_ADDR expertTokenNums, GM_ADDR workspaceGM, GM_ADDR tilingGM, GM_ADDR profileGM, | 49 | GM_ADDR probs, GM_ADDR c, GM_ADDR expertTokenNums, GM_ADDR workspaceGM, GM_ADDR tilingGM, GM_ADDR profileGM, |
| 50 | uint32_t startSyncDebug) | 50 | uint32_t startSyncDebug) |
| 51 | { | 51 | { |
| 52 | - | ||
| 53 | - cce::printf( | ||
| 54 | - "MegaMoe kernel enter block=%d sub=%d DAV_VEC=%d workspace=%d tiling=%d\n", int(get_block_idx()), | ||
| 55 | - int(get_subblockid()), int(DAV_VEC), int(workspaceGM != nullptr), int(tilingGM != nullptr)); | ||
| 56 | - | ||
| 57 | __gm__ uint64_t* profileEntry = nullptr; | 52 | __gm__ uint64_t* profileEntry = nullptr; |
| 58 | if (profileGM != nullptr) { | 53 | if (profileGM != nullptr) { |
| 59 | std::size_t profileOffset = static_cast<std::size_t>(get_block_idx()) * kMegaMoeProfileBytesPerBlock; | 54 | std::size_t profileOffset = static_cast<std::size_t>(get_block_idx()) * kMegaMoeProfileBytesPerBlock; |
| @@ -66,12 +61,18 @@ extern "C" __global__ __aicore__ void dispatch_mega_combine_kernel( | |||
| 66 | set_ffts_base_addr(reinterpret_cast<uint64_t>(fftsAddr)); | 61 | set_ffts_base_addr(reinterpret_cast<uint64_t>(fftsAddr)); |
| 67 | if (workspaceGM != nullptr && tilingGM != nullptr) { | 62 | if (workspaceGM != nullptr && tilingGM != nullptr) { |
| 68 | const __gm__ MegaMoeTilingData* tilingData = reinterpret_cast<__gm__ MegaMoeTilingData*>(tilingGM); | 63 | const __gm__ MegaMoeTilingData* tilingData = reinterpret_cast<__gm__ MegaMoeTilingData*>(tilingGM); |
| 69 | -#ifdef _DEBUG | 64 | +#if defined(__DAV_VEC__) |
| 70 | - cce::printf( | 65 | + if (get_block_idx() == 0U && get_subblockid() == 0U) { |
| 71 | - "MegaMoe kernel tiling block=%d sub=%d stage=%d M=%d topK=%d expertNum=%d\n", int(get_block_idx()), | 66 | + ResetFixedSyncWorkspace(workspaceGM, tilingData); |
| 72 | - int(get_subblockid()), int(tilingData->frontReorderTiling.stageNum), int(tilingData->megaMoeInfo.M), | 67 | + if (tilingData->frontReorderTiling.stageNum >= 13U && |
| 73 | - int(tilingData->megaMoeInfo.topK), int(tilingData->frontReorderTiling.expertNum)); | 68 | + tilingData->unpermuteTiling.unpermuteImplMode == kMegaMoeUnpermuteImplRankStreaming) { |
| 69 | + PtoRemoteWindow remoteWindow; | ||
| 70 | + remoteWindow.Init(reinterpret_cast<GM_ADDR>(tilingData->runtimeInfo.remoteWindowContext)); | ||
| 71 | + remoteWindow.PrepareDataReadyEpoch(); | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | 74 | ||
| 75 | + pto::SYNCALL<pto::SyncCoreType::Mix>(); | ||
| 75 | if (startSyncDebug != 0U) { | 76 | if (startSyncDebug != 0U) { |
| 76 | PtoRemoteWindow remoteWindow; | 77 | PtoRemoteWindow remoteWindow; |
| 77 | remoteWindow.Init(reinterpret_cast<GM_ADDR>(tilingData->runtimeInfo.remoteWindowContext)); | 78 | remoteWindow.Init(reinterpret_cast<GM_ADDR>(tilingData->runtimeInfo.remoteWindowContext)); |
| @@ -86,9 +87,12 @@ extern "C" __global__ __aicore__ void dispatch_mega_combine_kernel( | |||
| 86 | if (tilingData->megaMoeInfo.expertPerRank == 8U) { | 87 | if (tilingData->megaMoeInfo.expertPerRank == 8U) { |
| 87 | RunMegaMoeSpecialized<8U>( | 88 | RunMegaMoeSpecialized<8U>( |
| 88 | x, w1, w2, expertId, scale1, scale2, probs, c, expertTokenNums, workspaceGM, tilingData); | 89 | x, w1, w2, expertId, scale1, scale2, probs, c, expertTokenNums, workspaceGM, tilingData); |
| 89 | - } else { | 90 | + } else if (tilingData->megaMoeInfo.expertPerRank == 16U) { |
| 90 | RunMegaMoeSpecialized<16U>( | 91 | RunMegaMoeSpecialized<16U>( |
| 91 | x, w1, w2, expertId, scale1, scale2, probs, c, expertTokenNums, workspaceGM, tilingData); | 92 | x, w1, w2, expertId, scale1, scale2, probs, c, expertTokenNums, workspaceGM, tilingData); |
| 93 | + } else if (tilingData->megaMoeInfo.expertPerRank == 32U) { | ||
| 94 | + RunMegaMoeSpecialized<32U>( | ||
| 95 | + x, w1, w2, expertId, scale1, scale2, probs, c, expertTokenNums, workspaceGM, tilingData); | ||
| 92 | } | 96 | } |
| 93 | } | 97 | } |
| 94 | 98 | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /** | 1 | /** |
| 2 | -Copyright (c) 2025 Huawei Technologies Co., Ltd. | 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 | 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"). | 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. | 5 | Please refer to the License for details. You may not use this file except in compliance with the License. |
| @@ -11,10 +11,10 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | - | ||
| 15 | 14 | ||
| 16 | 15 | ||
| 17 | 16 | ||
| 17 | + | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| @@ -41,76 +41,33 @@ constexpr int kDefaultMeasureIters = 5; | |||
| 41 | constexpr double kMicrosecondsPerSecond = 1000.0 * 1000.0; | 41 | constexpr double kMicrosecondsPerSecond = 1000.0 * 1000.0; |
| 42 | constexpr double kBytesPerGiB = 1024.0 * 1024.0 * 1024.0; | 42 | constexpr double kBytesPerGiB = 1024.0 * 1024.0 * 1024.0; |
| 43 | static double g_sys_cnt_multiple = 20.0; // Default A2/A3, in ns per SYS_CNT tick. | 43 | static double g_sys_cnt_multiple = 20.0; // Default A2/A3, in ns per SYS_CNT tick. |
| 44 | -constexpr uint32_t kHostCombineImplDirectAuto = 3U; | ||
| 45 | 44 | ||
| 46 | -struct DeviceBuffer { | 45 | +struct DeviceMemoryReleaser { |
| 47 | - void* ptr = nullptr; | 46 | + void operator()(void* allocation) const noexcept { (void)aclrtFree(allocation); } |
| 48 | - size_t bytes = 0; | ||
| 49 | - | ||
| 50 | - DeviceBuffer() = default; | ||
| 51 | - DeviceBuffer(const DeviceBuffer&) = delete; | ||
| 52 | - DeviceBuffer& operator=(const DeviceBuffer&) = delete; | ||
| 53 | - DeviceBuffer(DeviceBuffer&& other) noexcept : ptr(other.ptr), bytes(other.bytes) | ||
| 54 | - { | ||
| 55 | - other.ptr = nullptr; | ||
| 56 | - other.bytes = 0; | ||
| 57 | - } | ||
| 58 | - DeviceBuffer& operator=(DeviceBuffer&& other) noexcept | ||
| 59 | - { | ||
| 60 | - if (this != &other) { | ||
| 61 | - if (ptr != nullptr) { | ||
| 62 | - aclrtFree(ptr); | ||
| 63 | - } | ||
| 64 | - ptr = other.ptr; | ||
| 65 | - bytes = other.bytes; | ||
| 66 | - other.ptr = nullptr; | ||
| 67 | - other.bytes = 0; | ||
| 68 | - } | ||
| 69 | - return *this; | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - ~DeviceBuffer() | ||
| 73 | - { | ||
| 74 | - if (ptr != nullptr) { | ||
| 75 | - aclrtFree(ptr); | ||
| 76 | - } | ||
| 77 | - } | ||
| 78 | }; | 47 | }; |
| 79 | 48 | ||
| 80 | -struct HostBuffer { | 49 | +struct HostMemoryReleaser { |
| 81 | - void* ptr = nullptr; | 50 | + void operator()(void* allocation) const noexcept { (void)aclrtFreeHost(allocation); } |
| 82 | - size_t bytes = 0; | ||
| 83 | - | ||
| 84 | - HostBuffer() = default; | ||
| 85 | - HostBuffer(const HostBuffer&) = delete; | ||
| 86 | - HostBuffer& operator=(const HostBuffer&) = delete; | ||
| 87 | - HostBuffer(HostBuffer&& other) noexcept : ptr(other.ptr), bytes(other.bytes) | ||
| 88 | - { | ||
| 89 | - other.ptr = nullptr; | ||
| 90 | - other.bytes = 0; | ||
| 91 | - } | ||
| 92 | - HostBuffer& operator=(HostBuffer&& other) noexcept | ||
| 93 | - { | ||
| 94 | - if (this != &other) { | ||
| 95 | - if (ptr != nullptr) { | ||
| 96 | - aclrtFreeHost(ptr); | ||
| 97 | - } | ||
| 98 | - ptr = other.ptr; | ||
| 99 | - bytes = other.bytes; | ||
| 100 | - other.ptr = nullptr; | ||
| 101 | - other.bytes = 0; | ||
| 102 | - } | ||
| 103 | - return *this; | ||
| 104 | - } | ||
| 105 | - | ||
| 106 | - ~HostBuffer() | ||
| 107 | - { | ||
| 108 | - if (ptr != nullptr) { | ||
| 109 | - aclrtFreeHost(ptr); | ||
| 110 | - } | ||
| 111 | - } | ||
| 112 | }; | 51 | }; |
| 113 | 52 | ||
| 53 | +template <typename Releaser> | ||
| 54 | +class AclOwnedBuffer { | ||
| 55 | +public: | ||
| 56 | + AclOwnedBuffer() = default; | ||
| 57 | + AclOwnedBuffer(void* allocation, size_t allocationBytes) : storage_(allocation), byteCount_(allocationBytes) {} | ||
| 58 | + | ||
| 59 | + void* data() const noexcept { return storage_.get(); } | ||
| 60 | + | ||
| 61 | + size_t size() const noexcept { return byteCount_; } | ||
| 62 | + | ||
| 63 | +private: | ||
| 64 | + std::unique_ptr<void, Releaser> storage_; | ||
| 65 | + size_t byteCount_ = 0; | ||
| 66 | +}; | ||
| 67 | + | ||
| 68 | +using DeviceBuffer = AclOwnedBuffer<DeviceMemoryReleaser>; | ||
| 69 | +using HostBuffer = AclOwnedBuffer<HostMemoryReleaser>; | ||
| 70 | + | ||
| 114 | struct PerfStats { | 71 | struct PerfStats { |
| 115 | double avg = 0.0; | 72 | double avg = 0.0; |
| 116 | double min = 0.0; | 73 | double min = 0.0; |
| @@ -121,9 +78,7 @@ struct PerfStats { | |||
| 121 | struct RunOptions { | 78 | struct RunOptions { |
| 122 | int warmup_iters = kDefaultWarmupIters; | 79 | int warmup_iters = kDefaultWarmupIters; |
| 123 | int measure_iters = kDefaultMeasureIters; | 80 | int measure_iters = kDefaultMeasureIters; |
| 124 | - bool skip_accuracy = false; | ||
| 125 | bool start_sync_debug = false; | 81 | bool start_sync_debug = false; |
| 126 | - bool workload_audit = false; | ||
| 127 | }; | 82 | }; |
| 128 | 83 | ||
| 129 | struct RankHostInputs { | 84 | struct RankHostInputs { |
| @@ -134,7 +89,6 @@ struct RankHostInputs { | |||
| 134 | std::vector<uint8_t> scale1; | 89 | std::vector<uint8_t> scale1; |
| 135 | std::vector<uint8_t> scale2; | 90 | std::vector<uint8_t> scale2; |
| 136 | std::vector<uint8_t> probs; | 91 | std::vector<uint8_t> probs; |
| 137 | - std::vector<uint8_t> x_active_mask; | ||
| 138 | std::vector<uint16_t> expected_out; | 92 | std::vector<uint16_t> expected_out; |
| 139 | }; | 93 | }; |
| 140 | 94 | ||
| @@ -156,16 +110,16 @@ struct RankDeviceBuffers { | |||
| 156 | 110 | ||
| 157 | DeviceBuffer MakeDeviceBuffer(size_t bytes, const void* host_src = nullptr) | 111 | DeviceBuffer MakeDeviceBuffer(size_t bytes, const void* host_src = nullptr) |
| 158 | { | 112 | { |
| 159 | - DeviceBuffer buffer; | ||
| 160 | - buffer.bytes = bytes; | ||
| 161 | if (bytes == 0) { | 113 | if (bytes == 0) { |
| 162 | - return buffer; | 114 | + return {}; |
| 163 | } | 115 | } |
| 164 | - if (aclrtMalloc(&buffer.ptr, bytes, ACL_MEM_MALLOC_HUGE_FIRST) != ACL_SUCCESS) { | 116 | + void* allocation = nullptr; |
| 117 | + if (aclrtMalloc(&allocation, bytes, ACL_MEM_MALLOC_HUGE_FIRST) != ACL_SUCCESS) { | ||
| 165 | throw std::runtime_error("aclrtMalloc failed"); | 118 | throw std::runtime_error("aclrtMalloc failed"); |
| 166 | } | 119 | } |
| 120 | + DeviceBuffer buffer(allocation, bytes); | ||
| 167 | if (host_src != nullptr && | 121 | if (host_src != nullptr && |
| 168 | - aclrtMemcpy(buffer.ptr, bytes, host_src, bytes, ACL_MEMCPY_HOST_TO_DEVICE) != ACL_SUCCESS) { | 122 | + aclrtMemcpy(buffer.data(), bytes, host_src, bytes, ACL_MEMCPY_HOST_TO_DEVICE) != ACL_SUCCESS) { |
| 169 | throw std::runtime_error("aclrtMemcpy host->device failed"); | 123 | throw std::runtime_error("aclrtMemcpy host->device failed"); |
| 170 | } | 124 | } |
| 171 | return buffer; | 125 | return buffer; |
| @@ -173,15 +127,14 @@ DeviceBuffer MakeDeviceBuffer(size_t bytes, const void* host_src = nullptr) | |||
| 173 | 127 | ||
| 174 | HostBuffer MakeHostBuffer(size_t bytes) | 128 | HostBuffer MakeHostBuffer(size_t bytes) |
| 175 | { | 129 | { |
| 176 | - HostBuffer buffer; | ||
| 177 | - buffer.bytes = bytes; | ||
| 178 | if (bytes == 0) { | 130 | if (bytes == 0) { |
| 179 | - return buffer; | 131 | + return {}; |
| 180 | } | 132 | } |
| 181 | - if (aclrtMallocHost(&buffer.ptr, bytes) != ACL_SUCCESS) { | 133 | + void* allocation = nullptr; |
| 134 | + if (aclrtMallocHost(&allocation, bytes) != ACL_SUCCESS) { | ||
| 182 | throw std::runtime_error("aclrtMallocHost failed"); | 135 | throw std::runtime_error("aclrtMallocHost failed"); |
| 183 | } | 136 | } |
| 184 | - return buffer; | 137 | + return HostBuffer(allocation, bytes); |
| 185 | } | 138 | } |
| 186 | 139 | ||
| 187 | std::vector<uint16_t> BytesToU16(const std::vector<uint8_t>& bytes) | 140 | std::vector<uint16_t> BytesToU16(const std::vector<uint8_t>& bytes) |
| @@ -211,8 +164,6 @@ int ParseEnvInt(const char* name, int default_value) | |||
| 211 | } | 164 | } |
| 212 | } | 165 | } |
| 213 | 166 | ||
| 214 | -bool TraceEnabled() { return ParseEnvInt("DISPATCH_MEGA_COMBINE_TRACE", 0) != 0; } | ||
| 215 | - | ||
| 216 | uint64_t AlignUpU64(uint64_t value, uint64_t align) | 167 | uint64_t AlignUpU64(uint64_t value, uint64_t align) |
| 217 | { | 168 | { |
| 218 | if (align == 0U) { | 169 | if (align == 0U) { |
| @@ -234,14 +185,6 @@ uint64_t SwigluFullRowUbBytes(uint32_t n) | |||
| 234 | return ub_offset; | 185 | return ub_offset; |
| 235 | } | 186 | } |
| 236 | 187 | ||
| 237 | -void Trace(int rank_id, const std::string& message) | ||
| 238 | -{ | ||
| 239 | - if (!TraceEnabled()) { | ||
| 240 | - return; | ||
| 241 | - } | ||
| 242 | - std::cerr << "[trace] rank=" << rank_id << " " << message << std::endl; | ||
| 243 | -} | ||
| 244 | - | ||
| 245 | bool ZeroWindowMemory(const StandaloneRankRuntime& runtime) | 188 | bool ZeroWindowMemory(const StandaloneRankRuntime& runtime) |
| 246 | { | 189 | { |
| 247 | const uint64_t window_bytes = runtime.hccl.WindowBytes(); | 190 | const uint64_t window_bytes = runtime.hccl.WindowBytes(); |
| @@ -254,10 +197,10 @@ bool ZeroWindowMemory(const StandaloneRankRuntime& runtime) | |||
| 254 | 197 | ||
| 255 | void ZeroDeviceBuffer(const DeviceBuffer& buffer, const char* name) | 198 | void ZeroDeviceBuffer(const DeviceBuffer& buffer, const char* name) |
| 256 | { | 199 | { |
| 257 | - if (buffer.bytes == 0) { | 200 | + if (buffer.size() == 0) { |
| 258 | return; | 201 | return; |
| 259 | } | 202 | } |
| 260 | - if (aclrtMemset(buffer.ptr, buffer.bytes, 0, buffer.bytes) != ACL_SUCCESS) { | 203 | + if (aclrtMemset(buffer.data(), buffer.size(), 0, buffer.size()) != ACL_SUCCESS) { |
| 261 | throw std::runtime_error(std::string("failed to zero ") + name); | 204 | throw std::runtime_error(std::string("failed to zero ") + name); |
| 262 | } | 205 | } |
| 263 | } | 206 | } |
| @@ -332,18 +275,18 @@ std::vector<double> GatherMaxSamplesToRoot(const std::vector<double>& local_samp | |||
| 332 | 275 | ||
| 333 | double ReadKernelProfileUs(const DeviceBuffer& profile_dev, HostBuffer& profile_host, uint32_t block_dim) | 276 | double ReadKernelProfileUs(const DeviceBuffer& profile_dev, HostBuffer& profile_host, uint32_t block_dim) |
| 334 | { | 277 | { |
| 335 | - if (profile_dev.bytes == 0 || profile_host.bytes == 0 || block_dim == 0) { | 278 | + if (profile_dev.size() == 0 || profile_host.size() == 0 || block_dim == 0) { |
| 336 | return 0.0; | 279 | return 0.0; |
| 337 | } | 280 | } |
| 338 | if (aclrtMemcpy( | 281 | if (aclrtMemcpy( |
| 339 | - profile_host.ptr, profile_host.bytes, profile_dev.ptr, profile_dev.bytes, ACL_MEMCPY_DEVICE_TO_HOST) != | 282 | + profile_host.data(), profile_host.size(), profile_dev.data(), profile_dev.size(), |
| 340 | - ACL_SUCCESS) { | 283 | + ACL_MEMCPY_DEVICE_TO_HOST) != ACL_SUCCESS) { |
| 341 | throw std::runtime_error("device->host profile copy failed"); | 284 | throw std::runtime_error("device->host profile copy failed"); |
| 342 | } | 285 | } |
| 343 | 286 | ||
| 344 | uint64_t start_min = std::numeric_limits<uint64_t>::max(); | 287 | uint64_t start_min = std::numeric_limits<uint64_t>::max(); |
| 345 | uint64_t end_max = 0; | 288 | uint64_t end_max = 0; |
| 346 | - const auto* profile = static_cast<const uint8_t*>(profile_host.ptr); | 289 | + const auto* profile = static_cast<const uint8_t*>(profile_host.data()); |
| 347 | for (uint32_t block = 0; block < block_dim; ++block) { | 290 | for (uint32_t block = 0; block < block_dim; ++block) { |
| 348 | for (size_t profile_idx = 0; profile_idx < kMegaMoeProfileEntriesPerBlock; ++profile_idx) { | 291 | for (size_t profile_idx = 0; profile_idx < kMegaMoeProfileEntriesPerBlock; ++profile_idx) { |
| 349 | const uint64_t* entry = reinterpret_cast<const uint64_t*>( | 292 | const uint64_t* entry = reinterpret_cast<const uint64_t*>( |
| @@ -414,168 +357,14 @@ void PrintPerfSummary( | |||
| 414 | << " routed_tokens/s=" << ToTokensPerSecond(cfg.routed_tokens_all_ranks, kernel_stats.avg) | 357 | << " routed_tokens/s=" << ToTokensPerSecond(cfg.routed_tokens_all_ranks, kernel_stats.avg) |
| 415 | << " eq_compute=" << ToTflops(cfg.compute_flops_all_ranks, kernel_stats.avg) << " TFLOPS" | 358 | << " eq_compute=" << ToTflops(cfg.compute_flops_all_ranks, kernel_stats.avg) << " TFLOPS" |
| 416 | << " eq_comm=" << ToGbs(cfg.comm_bytes_all_ranks, kernel_stats.avg) << " GB/s\n"; | 359 | << " eq_comm=" << ToGbs(cfg.comm_bytes_all_ranks, kernel_stats.avg) << " GB/s\n"; |
| 417 | - std::cout | 360 | + std::cout << " note: equivalent compute/comm are logical workload estimates, not hardware counters.\n"; |
| 418 | - << " note: equivalent compute/comm are derived from case.json logical workload, not hardware counters.\n"; | ||
| 419 | std::cout << "===============================================================\n" << std::endl; | 361 | std::cout << "===============================================================\n" << std::endl; |
| 420 | } | 362 | } |
| 421 | 363 | ||
| 422 | -int32_t LoadI32(const std::vector<uint8_t>& bytes, size_t index) | ||
| 423 | -{ | ||
| 424 | - const size_t byteOffset = index * sizeof(int32_t); | ||
| 425 | - if (byteOffset + sizeof(int32_t) > bytes.size()) { | ||
| 426 | - return 0; | ||
| 427 | - } | ||
| 428 | - uint32_t value = 0U; | ||
| 429 | - for (size_t byteIdx = 0; byteIdx < sizeof(int32_t); ++byteIdx) { | ||
| 430 | - value |= static_cast<uint32_t>(bytes[byteOffset + byteIdx]) << (byteIdx * 8U); | ||
| 431 | - } | ||
| 432 | - return static_cast<int32_t>(value); | ||
| 433 | -} | ||
| 434 | - | ||
| 435 | -struct WorkloadAuditLayout { | ||
| 436 | - size_t global_expert_num = 0; | ||
| 437 | - size_t fixed_fields = 6; | ||
| 438 | - size_t per_rank_offset = 6; | ||
| 439 | - size_t per_expert_offset = 0; | ||
| 440 | - size_t fields = 0; | ||
| 441 | -}; | ||
| 442 | - | ||
| 443 | -WorkloadAuditLayout BuildWorkloadAuditLayout(const CaseConfig& cfg) | ||
| 444 | -{ | ||
| 445 | - WorkloadAuditLayout layout; | ||
| 446 | - layout.global_expert_num = static_cast<size_t>(cfg.world_size) * cfg.expert_per_rank; | ||
| 447 | - layout.per_rank_offset = layout.fixed_fields; | ||
| 448 | - layout.per_expert_offset = layout.per_rank_offset + cfg.world_size; | ||
| 449 | - layout.fields = layout.per_expert_offset + layout.global_expert_num; | ||
| 450 | - return layout; | ||
| 451 | -} | ||
| 452 | - | ||
| 453 | -std::vector<uint64_t> BuildLocalWorkloadAudit( | ||
| 454 | - const CaseConfig& cfg, const WorkloadAuditLayout& layout, int rank_id, const std::vector<uint8_t>& expert_idx, | ||
| 455 | - const std::vector<uint8_t>& x_active_mask, size_t actual_output_elems) | ||
| 456 | -{ | ||
| 457 | - std::vector<uint64_t> local(layout.fields, 0); | ||
| 458 | - uint64_t active_tokens = 0; | ||
| 459 | - uint64_t valid_routes = 0; | ||
| 460 | - uint64_t remote_routes = 0; | ||
| 461 | - uint64_t invalid_expert_routes = 0; | ||
| 462 | - const size_t expert_idx_count = expert_idx.size() / sizeof(int32_t); | ||
| 463 | - for (uint32_t token = 0; token < cfg.m; ++token) { | ||
| 464 | - const bool active = token < x_active_mask.size() && x_active_mask[token] != 0; | ||
| 465 | - if (!active) { | ||
| 466 | - continue; | ||
| 467 | - } | ||
| 468 | - ++active_tokens; | ||
| 469 | - for (uint32_t topk = 0; topk < cfg.topk; ++topk) { | ||
| 470 | - const size_t slot = static_cast<size_t>(token) * cfg.topk + topk; | ||
| 471 | - if (slot >= expert_idx_count) { | ||
| 472 | - ++invalid_expert_routes; | ||
| 473 | - continue; | ||
| 474 | - } | ||
| 475 | - const int32_t expert = LoadI32(expert_idx, slot); | ||
| 476 | - if (expert < 0 || static_cast<size_t>(expert) >= layout.global_expert_num || cfg.expert_per_rank == 0U) { | ||
| 477 | - ++invalid_expert_routes; | ||
| 478 | - continue; | ||
| 479 | - } | ||
| 480 | - const uint32_t dst_rank = static_cast<uint32_t>(expert) / cfg.expert_per_rank; | ||
| 481 | - ++valid_routes; | ||
| 482 | - if (static_cast<int>(dst_rank) != rank_id) { | ||
| 483 | - ++remote_routes; | ||
| 484 | - } | ||
| 485 | - ++local[layout.per_rank_offset + dst_rank]; | ||
| 486 | - ++local[layout.per_expert_offset + static_cast<size_t>(expert)]; | ||
| 487 | - } | ||
| 488 | - } | ||
| 489 | - local[0] = active_tokens; | ||
| 490 | - local[1] = active_tokens * cfg.topk; | ||
| 491 | - local[2] = valid_routes; | ||
| 492 | - local[3] = remote_routes; | ||
| 493 | - local[4] = invalid_expert_routes; | ||
| 494 | - local[5] = actual_output_elems; | ||
| 495 | - return local; | ||
| 496 | -} | ||
| 497 | - | ||
| 498 | -std::vector<uint64_t> GatherWorkloadAuditTotals( | ||
| 499 | - const std::vector<uint64_t>& local, size_t fields, int rank_id, int world_size) | ||
| 500 | -{ | ||
| 501 | - const int local_bytes = static_cast<int>(local.size() * sizeof(uint64_t)); | ||
| 502 | - std::vector<uint64_t> gathered(rank_id == 0 ? fields * static_cast<size_t>(world_size) : 0, 0); | ||
| 503 | - CommMpiGather( | ||
| 504 | - local.data(), local_bytes, COMM_MPI_CHAR, rank_id == 0 ? gathered.data() : nullptr, local_bytes, COMM_MPI_CHAR, | ||
| 505 | - 0); | ||
| 506 | - | ||
| 507 | - std::vector<uint64_t> total(rank_id == 0 ? fields : 0, 0); | ||
| 508 | - if (rank_id != 0) { | ||
| 509 | - return total; | ||
| 510 | - } | ||
| 511 | - for (int rank = 0; rank < world_size; ++rank) { | ||
| 512 | - const uint64_t* rank_fields = gathered.data() + static_cast<size_t>(rank) * fields; | ||
| 513 | - for (size_t idx = 0; idx < fields; ++idx) { | ||
| 514 | - total[idx] += rank_fields[idx]; | ||
| 515 | - } | ||
| 516 | - } | ||
| 517 | - return total; | ||
| 518 | -} | ||
| 519 | - | ||
| 520 | -void PrintWorkloadAuditTotals( | ||
| 521 | - const CaseConfig& cfg, const WorkloadAuditLayout& layout, const std::vector<uint64_t>& total, bool skip_accuracy) | ||
| 522 | -{ | ||
| 523 | - const auto dest_begin = total.begin() + static_cast<std::ptrdiff_t>(layout.per_rank_offset); | ||
| 524 | - const auto dest_end = dest_begin + cfg.world_size; | ||
| 525 | - const auto expert_begin = total.begin() + static_cast<std::ptrdiff_t>(layout.per_expert_offset); | ||
| 526 | - const auto expert_end = expert_begin + static_cast<std::ptrdiff_t>(layout.global_expert_num); | ||
| 527 | - const uint64_t min_dest_rows = dest_begin == dest_end ? 0 : *std::min_element(dest_begin, dest_end); | ||
| 528 | - const uint64_t max_dest_rows = dest_begin == dest_end ? 0 : *std::max_element(dest_begin, dest_end); | ||
| 529 | - const uint64_t min_expert_rows = expert_begin == expert_end ? 0 : *std::min_element(expert_begin, expert_end); | ||
| 530 | - const uint64_t max_expert_rows = expert_begin == expert_end ? 0 : *std::max_element(expert_begin, expert_end); | ||
| 531 | - const size_t nonzero_experts = | ||
| 532 | - static_cast<size_t>(std::count_if(expert_begin, expert_end, [](uint64_t rows) { return rows != 0; })); | ||
| 533 | - const uint64_t expected_output_elems = | ||
| 534 | - static_cast<uint64_t>(cfg.world_size) * static_cast<uint64_t>(cfg.m) * static_cast<uint64_t>(cfg.k); | ||
| 535 | - const bool routes_match_case = static_cast<double>(total[0]) == cfg.input_tokens_all_ranks && | ||
| 536 | - static_cast<double>(total[2]) == cfg.routed_tokens_all_ranks && | ||
| 537 | - static_cast<double>(total[3]) == cfg.remote_routed_tokens_all_ranks; | ||
| 538 | - const bool output_shape_match = total[5] == expected_output_elems; | ||
| 539 | - | ||
| 540 | - std::cout << "[WORKLOAD_AUDIT] dispatch_mega_combine " | ||
| 541 | - << (routes_match_case && output_shape_match && total[4] == 0 ? "PASS" : "CHECK") | ||
| 542 | - << " accuracy=" << (skip_accuracy ? "SKIP" : "FULL") << '\n'; | ||
| 543 | - std::cout << " source active_tokens=" << total[0] << " topk_slots=" << total[1] << " valid_routes=" << total[2] | ||
| 544 | - << " remote_routes=" << total[3] << " invalid_expert_routes=" << total[4] << '\n'; | ||
| 545 | - std::cout << " case_json input_tokens=" << static_cast<uint64_t>(cfg.input_tokens_all_ranks) | ||
| 546 | - << " routed_tokens=" << static_cast<uint64_t>(cfg.routed_tokens_all_ranks) | ||
| 547 | - << " remote_routed_tokens=" << static_cast<uint64_t>(cfg.remote_routed_tokens_all_ranks) << '\n'; | ||
| 548 | - std::cout << " dest_rows total=" << total[2] << " per_rank_min=" << min_dest_rows | ||
| 549 | - << " per_rank_max=" << max_dest_rows << " max_output_size=" << cfg.max_output_size << '\n'; | ||
| 550 | - std::cout << " expert_rows nonzero=" << nonzero_experts << "/" << layout.global_expert_num | ||
| 551 | - << " min=" << min_expert_rows << " max=" << max_expert_rows << '\n'; | ||
| 552 | - std::cout << " output_elements total=" << total[5] << " expected=" << expected_output_elems << std::endl; | ||
| 553 | -} | ||
| 554 | - | ||
| 555 | -void PrintWorkloadAuditIfEnabled( | ||
| 556 | - const CaseConfig& cfg, int rank_id, int world_size, const std::vector<uint8_t>& expert_idx, | ||
| 557 | - const std::vector<uint8_t>& x_active_mask, size_t actual_output_elems, bool skip_accuracy) | ||
| 558 | -{ | ||
| 559 | - const WorkloadAuditLayout layout = BuildWorkloadAuditLayout(cfg); | ||
| 560 | - const std::vector<uint64_t> local = | ||
| 561 | - BuildLocalWorkloadAudit(cfg, layout, rank_id, expert_idx, x_active_mask, actual_output_elems); | ||
| 562 | - const std::vector<uint64_t> total = GatherWorkloadAuditTotals(local, layout.fields, rank_id, world_size); | ||
| 563 | - if (rank_id == 0) { | ||
| 564 | - PrintWorkloadAuditTotals(cfg, layout, total, skip_accuracy); | ||
| 565 | - } | ||
| 566 | -} | ||
| 567 | - | ||
| 568 | void ValidateFullPathConstraints(const CaseConfig& cfg) | 364 | void ValidateFullPathConstraints(const CaseConfig& cfg) |
| 569 | { | 365 | { |
| 570 | - if (cfg.expert_per_rank + 1U > MEGA_MOE_D2C_MAX_LOGICAL_GROUP_EVENTS) { | 366 | + if (cfg.expert_per_rank != 8U && cfg.expert_per_rank != 16U && cfg.expert_per_rank != 32U) { |
| 571 | - throw std::runtime_error( | 367 | + throw std::runtime_error("expert_per_rank must be one of 8, 16 or 32"); |
| 572 | - "D2C hard flag budget exceeded: expert_per_rank=" + std::to_string(cfg.expert_per_rank) + | ||
| 573 | - " max=" + std::to_string(MEGA_MOE_D2C_MAX_LOGICAL_GROUP_EVENTS)); | ||
| 574 | - } | ||
| 575 | - if (cfg.expert_per_rank > MEGA_MOE_GMM2_TO_COMBINE_MAX_LOGICAL_GROUP_EVENTS) { | ||
| 576 | - throw std::runtime_error( | ||
| 577 | - "GMM2->combine hard flag budget exceeded: expert_per_rank=" + std::to_string(cfg.expert_per_rank) + | ||
| 578 | - " max=" + std::to_string(MEGA_MOE_GMM2_TO_COMBINE_MAX_LOGICAL_GROUP_EVENTS)); | ||
| 579 | } | 368 | } |
| 580 | if (cfg.k % 128U != 0U) { | 369 | if (cfg.k % 128U != 0U) { |
| 581 | throw std::runtime_error("GMM1 requires K % 128 == 0"); | 370 | throw std::runtime_error("GMM1 requires K % 128 == 0"); |
| @@ -604,31 +393,25 @@ RunOptions LoadRunOptions() | |||
| 604 | RunOptions options; | 393 | RunOptions options; |
| 605 | options.warmup_iters = ParseEnvInt("DISPATCH_MEGA_COMBINE_WARMUP_ITERS", kDefaultWarmupIters); | 394 | options.warmup_iters = ParseEnvInt("DISPATCH_MEGA_COMBINE_WARMUP_ITERS", kDefaultWarmupIters); |
| 606 | options.measure_iters = ParseEnvInt("DISPATCH_MEGA_COMBINE_MEASURE_ITERS", kDefaultMeasureIters); | 395 | options.measure_iters = ParseEnvInt("DISPATCH_MEGA_COMBINE_MEASURE_ITERS", kDefaultMeasureIters); |
| 607 | - options.skip_accuracy = ParseEnvInt("DISPATCH_MEGA_COMBINE_SKIP_ACCURACY", 0) != 0; | ||
| 608 | options.start_sync_debug = ParseEnvInt("DISPATCH_MEGA_COMBINE_START_SYNC_DEBUG", 0) != 0; | 396 | options.start_sync_debug = ParseEnvInt("DISPATCH_MEGA_COMBINE_START_SYNC_DEBUG", 0) != 0; |
| 609 | - options.workload_audit = ParseEnvInt("DISPATCH_MEGA_COMBINE_WORKLOAD_AUDIT", 0) != 0; | ||
| 610 | if (options.warmup_iters < 0 || options.measure_iters < 0) { | 397 | if (options.warmup_iters < 0 || options.measure_iters < 0) { |
| 611 | throw std::runtime_error("warmup/measure iters must be non-negative"); | 398 | throw std::runtime_error("warmup/measure iters must be non-negative"); |
| 612 | } | 399 | } |
| 613 | return options; | 400 | return options; |
| 614 | } | 401 | } |
| 615 | 402 | ||
| 616 | -MegaMoeBuildResult BuildAndValidateTiling(const CaseConfig& cfg, const StandaloneRankRuntime& runtime, int rank_id) | 403 | +MegaMoeBuildResult BuildAndValidateTiling(const CaseConfig& cfg, const StandaloneRankRuntime& runtime) |
| 617 | { | 404 | { |
| 618 | MegaMoeBuildResult build = BuildMegaMoeTiling(cfg, runtime); | 405 | MegaMoeBuildResult build = BuildMegaMoeTiling(cfg, runtime); |
| 619 | const auto& front = build.tiling.frontReorderTiling; | 406 | const auto& front = build.tiling.frontReorderTiling; |
| 620 | if (!FrontCaseIsSupported(front.frontCase)) { | 407 | if (!FrontCaseIsSupported(front.frontCase)) { |
| 621 | throw std::runtime_error("front unsupported case has no legacy fallback"); | 408 | throw std::runtime_error("front unsupported case has no legacy fallback"); |
| 622 | } | 409 | } |
| 623 | - build.tiling.combineTiling.combineImplMode = kHostCombineImplDirectAuto; | ||
| 624 | ValidateFullPathConstraints(cfg); | 410 | ValidateFullPathConstraints(cfg); |
| 625 | - Trace( | ||
| 626 | - rank_id, | ||
| 627 | - "tiling built frontCase=" + std::to_string(front.frontCase) + " block_dim=" + std::to_string(build.block_dim)); | ||
| 628 | return build; | 411 | return build; |
| 629 | } | 412 | } |
| 630 | 413 | ||
| 631 | -RankHostInputs LoadRankHostInputs(const RankFileSet& files, bool skip_accuracy) | 414 | +RankHostInputs LoadRankHostInputs(const RankFileSet& files) |
| 632 | { | 415 | { |
| 633 | RankHostInputs inputs; | 416 | RankHostInputs inputs; |
| 634 | inputs.x = ReadBinaryFile(files.x); | 417 | inputs.x = ReadBinaryFile(files.x); |
| @@ -638,10 +421,7 @@ RankHostInputs LoadRankHostInputs(const RankFileSet& files, bool skip_accuracy) | |||
| 638 | inputs.scale1 = ReadBinaryFile(files.scale1); | 421 | inputs.scale1 = ReadBinaryFile(files.scale1); |
| 639 | inputs.scale2 = ReadBinaryFile(files.scale2); | 422 | inputs.scale2 = ReadBinaryFile(files.scale2); |
| 640 | inputs.probs = ReadBinaryFile(files.probs); | 423 | inputs.probs = ReadBinaryFile(files.probs); |
| 641 | - inputs.x_active_mask = ReadBinaryFile(files.x_active_mask); | 424 | + inputs.expected_out = BytesToU16(ReadBinaryFile(files.expected_out)); |
| 642 | - if (!skip_accuracy) { | ||
| 643 | - inputs.expected_out = BytesToU16(ReadBinaryFile(files.expected_out)); | ||
| 644 | - } | ||
| 645 | return inputs; | 425 | return inputs; |
| 646 | } | 426 | } |
| 647 | 427 | ||
| @@ -677,56 +457,53 @@ MegaMoeLaunchArgs BuildLaunchArgs( | |||
| 677 | MegaMoeLaunchArgs args; | 457 | MegaMoeLaunchArgs args; |
| 678 | args.ffts = reinterpret_cast<void*>(ffts_addr); | 458 | args.ffts = reinterpret_cast<void*>(ffts_addr); |
| 679 | args.block_dim = build.block_dim; | 459 | args.block_dim = build.block_dim; |
| 680 | - args.tiling = buffers.tiling.ptr; | 460 | + args.tiling = buffers.tiling.data(); |
| 681 | - args.workspace = buffers.workspace.ptr; | 461 | + args.workspace = buffers.workspace.data(); |
| 682 | - args.x = buffers.x.ptr; | 462 | + args.x = buffers.x.data(); |
| 683 | - args.weight1 = buffers.weight1.ptr; | 463 | + args.weight1 = buffers.weight1.data(); |
| 684 | - args.weight2 = buffers.weight2.ptr; | 464 | + args.weight2 = buffers.weight2.data(); |
| 685 | - args.expert_idx = buffers.expert_idx.ptr; | 465 | + args.expert_idx = buffers.expert_idx.data(); |
| 686 | - args.scale1 = buffers.scale1.ptr; | 466 | + args.scale1 = buffers.scale1.data(); |
| 687 | - args.scale2 = buffers.scale2.ptr; | 467 | + args.scale2 = buffers.scale2.data(); |
| 688 | - args.probs = buffers.probs.ptr; | 468 | + args.probs = buffers.probs.data(); |
| 689 | - args.out = buffers.out.ptr; | 469 | + args.out = buffers.out.data(); |
| 690 | - args.expert_token_nums = buffers.expert_token_nums.ptr; | 470 | + args.expert_token_nums = buffers.expert_token_nums.data(); |
| 691 | - args.profile_data = buffers.profile.ptr; | 471 | + args.profile_data = buffers.profile.data(); |
| 692 | args.start_sync_debug = start_sync_debug ? 1U : 0U; | 472 | args.start_sync_debug = start_sync_debug ? 1U : 0U; |
| 693 | return args; | 473 | return args; |
| 694 | } | 474 | } |
| 695 | 475 | ||
| 696 | -void LaunchAndSync(int rank_id, const MegaMoeLaunchArgs& args, aclrtStream stream, const char* trace_tag) | 476 | +void LaunchAndSync(const MegaMoeLaunchArgs& args, aclrtStream stream) |
| 697 | { | 477 | { |
| 698 | - Trace(rank_id, std::string(trace_tag) + " launch begin"); | ||
| 699 | launchMegaMoe(args, stream); | 478 | launchMegaMoe(args, stream); |
| 700 | - Trace(rank_id, std::string(trace_tag) + " launch submitted"); | ||
| 701 | if (aclrtSynchronizeStream(stream) != ACL_SUCCESS) { | 479 | if (aclrtSynchronizeStream(stream) != ACL_SUCCESS) { |
| 702 | throw std::runtime_error("stream sync failed"); | 480 | throw std::runtime_error("stream sync failed"); |
| 703 | } | 481 | } |
| 704 | - Trace(rank_id, std::string(trace_tag) + " launch synced"); | ||
| 705 | } | 482 | } |
| 706 | 483 | ||
| 707 | void RunWarmupIterations( | 484 | void RunWarmupIterations( |
| 708 | const StandaloneRankRuntime& runtime, const RankDeviceBuffers& buffers, const MegaMoeLaunchArgs& args, | 485 | const StandaloneRankRuntime& runtime, const RankDeviceBuffers& buffers, const MegaMoeLaunchArgs& args, |
| 709 | - int warmup_iters, int rank_id) | 486 | + int warmup_iters) |
| 710 | { | 487 | { |
| 711 | CommMpiBarrier(); | 488 | CommMpiBarrier(); |
| 712 | for (int iter = 0; iter < warmup_iters; ++iter) { | 489 | for (int iter = 0; iter < warmup_iters; ++iter) { |
| 713 | PrepareIterationState(runtime, buffers.out, buffers.expert_token_nums, buffers.workspace, buffers.profile); | 490 | PrepareIterationState(runtime, buffers.out, buffers.expert_token_nums, buffers.workspace, buffers.profile); |
| 714 | CommMpiBarrier(); | 491 | CommMpiBarrier(); |
| 715 | - LaunchAndSync(rank_id, args, runtime.compute_stream, "warmup"); | 492 | + LaunchAndSync(args, runtime.compute_stream); |
| 716 | CommMpiBarrier(); | 493 | CommMpiBarrier(); |
| 717 | } | 494 | } |
| 718 | } | 495 | } |
| 719 | 496 | ||
| 720 | std::vector<double> RunMeasureIterations( | 497 | std::vector<double> RunMeasureIterations( |
| 721 | const StandaloneRankRuntime& runtime, RankDeviceBuffers& buffers, const MegaMoeLaunchArgs& args, | 498 | const StandaloneRankRuntime& runtime, RankDeviceBuffers& buffers, const MegaMoeLaunchArgs& args, |
| 722 | - const MegaMoeBuildResult& build, int measure_iters, int rank_id) | 499 | + const MegaMoeBuildResult& build, int measure_iters) |
| 723 | { | 500 | { |
| 724 | std::vector<double> kernel_times_us; | 501 | std::vector<double> kernel_times_us; |
| 725 | kernel_times_us.reserve(static_cast<size_t>(measure_iters)); | 502 | kernel_times_us.reserve(static_cast<size_t>(measure_iters)); |
| 726 | for (int iter = 0; iter < measure_iters; ++iter) { | 503 | for (int iter = 0; iter < measure_iters; ++iter) { |
| 727 | PrepareIterationState(runtime, buffers.out, buffers.expert_token_nums, buffers.workspace, buffers.profile); | 504 | PrepareIterationState(runtime, buffers.out, buffers.expert_token_nums, buffers.workspace, buffers.profile); |
| 728 | CommMpiBarrier(); | 505 | CommMpiBarrier(); |
| 729 | - LaunchAndSync(rank_id, args, runtime.compute_stream, "measure"); | 506 | + LaunchAndSync(args, runtime.compute_stream); |
| 730 | CommMpiBarrier(); | 507 | CommMpiBarrier(); |
| 731 | kernel_times_us.push_back(ReadKernelProfileUs(buffers.profile, buffers.profile_host, build.block_dim)); | 508 | kernel_times_us.push_back(ReadKernelProfileUs(buffers.profile, buffers.profile_host, build.block_dim)); |
| 732 | } | 509 | } |
| @@ -737,8 +514,8 @@ std::vector<uint16_t> CopyActualOutputToHost(const CaseConfig& cfg, const Device | |||
| 737 | { | 514 | { |
| 738 | std::vector<uint16_t> actual_out(static_cast<size_t>(cfg.m) * cfg.k); | 515 | std::vector<uint16_t> actual_out(static_cast<size_t>(cfg.m) * cfg.k); |
| 739 | if (aclrtMemcpy( | 516 | if (aclrtMemcpy( |
| 740 | - actual_out.data(), actual_out.size() * sizeof(uint16_t), out_dev.ptr, actual_out.size() * sizeof(uint16_t), | 517 | + actual_out.data(), actual_out.size() * sizeof(uint16_t), out_dev.data(), |
| 741 | - ACL_MEMCPY_DEVICE_TO_HOST) != ACL_SUCCESS) { | 518 | + actual_out.size() * sizeof(uint16_t), ACL_MEMCPY_DEVICE_TO_HOST) != ACL_SUCCESS) { |
| 742 | throw std::runtime_error("device->host output copy failed"); | 519 | throw std::runtime_error("device->host output copy failed"); |
| 743 | } | 520 | } |
| 744 | return actual_out; | 521 | return actual_out; |
| @@ -746,14 +523,8 @@ std::vector<uint16_t> CopyActualOutputToHost(const CaseConfig& cfg, const Device | |||
| 746 | 523 | ||
| 747 | bool ReportRankAccuracy( | 524 | bool ReportRankAccuracy( |
| 748 | int rank_id, int world_size, const CaseConfig& cfg, const RankHostInputs& inputs, | 525 | int rank_id, int world_size, const CaseConfig& cfg, const RankHostInputs& inputs, |
| 749 | - const std::vector<uint16_t>& actual_out, bool skip_accuracy) | 526 | + const std::vector<uint16_t>& actual_out) |
| 750 | { | 527 | { |
| 751 | - if (skip_accuracy) { | ||
| 752 | - PrintOrderedByRank( | ||
| 753 | - rank_id, world_size, | ||
| 754 | - "rank=" + std::to_string(rank_id) + " accuracy=SKIP\nPASS rank=" + std::to_string(rank_id)); | ||
| 755 | - return true; | ||
| 756 | - } | ||
| 757 | const AccuracyReport report = CompareFp16File(inputs.expected_out, actual_out, cfg.compare_atol, cfg.compare_rtol); | 528 | const AccuracyReport report = CompareFp16File(inputs.expected_out, actual_out, cfg.compare_atol, cfg.compare_rtol); |
| 758 | PrintOrderedByRank( | 529 | PrintOrderedByRank( |
| 759 | rank_id, world_size, | 530 | rank_id, world_size, |
| @@ -768,22 +539,20 @@ bool RunOneRank(int rank_id, int world_size, const std::string& case_dir, const | |||
| 768 | if (!InitStandaloneRankRuntime(runtime, rank_id, world_size, root_info)) { | 539 | if (!InitStandaloneRankRuntime(runtime, rank_id, world_size, root_info)) { |
| 769 | return false; | 540 | return false; |
| 770 | } | 541 | } |
| 771 | - Trace(rank_id, "runtime initialized"); | ||
| 772 | 542 | ||
| 773 | bool ok = false; | 543 | bool ok = false; |
| 774 | try { | 544 | try { |
| 775 | const RunOptions options = LoadRunOptions(); | 545 | const RunOptions options = LoadRunOptions(); |
| 776 | const CaseConfig cfg = LoadCaseConfig(case_dir + "/case.json"); | 546 | const CaseConfig cfg = LoadCaseConfig(case_dir + "/case.json"); |
| 777 | const RankFileSet files = BuildRankFileSet(case_dir, rank_id); | 547 | const RankFileSet files = BuildRankFileSet(case_dir, rank_id); |
| 778 | - const MegaMoeBuildResult build = BuildAndValidateTiling(cfg, runtime, rank_id); | 548 | + const MegaMoeBuildResult build = BuildAndValidateTiling(cfg, runtime); |
| 779 | - const RankHostInputs inputs = LoadRankHostInputs(files, options.skip_accuracy); | 549 | + const RankHostInputs inputs = LoadRankHostInputs(files); |
| 780 | RankDeviceBuffers buffers = AllocateRankDeviceBuffers(cfg, build, inputs); | 550 | RankDeviceBuffers buffers = AllocateRankDeviceBuffers(cfg, build, inputs); |
| 781 | - Trace(rank_id, "buffers allocated"); | ||
| 782 | 551 | ||
| 783 | const MegaMoeLaunchArgs args = BuildLaunchArgs(build, buffers, options.start_sync_debug); | 552 | const MegaMoeLaunchArgs args = BuildLaunchArgs(build, buffers, options.start_sync_debug); |
| 784 | - RunWarmupIterations(runtime, buffers, args, options.warmup_iters, rank_id); | 553 | + RunWarmupIterations(runtime, buffers, args, options.warmup_iters); |
| 785 | const std::vector<double> kernel_times_us = | 554 | const std::vector<double> kernel_times_us = |
| 786 | - RunMeasureIterations(runtime, buffers, args, build, options.measure_iters, rank_id); | 555 | + RunMeasureIterations(runtime, buffers, args, build, options.measure_iters); |
| 787 | 556 | ||
| 788 | const std::vector<double> kernel_max_samples = GatherMaxSamplesToRoot(kernel_times_us, rank_id, world_size); | 557 | const std::vector<double> kernel_max_samples = GatherMaxSamplesToRoot(kernel_times_us, rank_id, world_size); |
| 789 | if (rank_id == 0) { | 558 | if (rank_id == 0) { |
| @@ -792,19 +561,14 @@ bool RunOneRank(int rank_id, int world_size, const std::string& case_dir, const | |||
| 792 | 561 | ||
| 793 | PrepareIterationState(runtime, buffers.out, buffers.expert_token_nums, buffers.workspace, buffers.profile); | 562 | PrepareIterationState(runtime, buffers.out, buffers.expert_token_nums, buffers.workspace, buffers.profile); |
| 794 | CommMpiBarrier(); | 563 | CommMpiBarrier(); |
| 795 | - LaunchAndSync(rank_id, args, runtime.compute_stream, "final"); | 564 | + LaunchAndSync(args, runtime.compute_stream); |
| 796 | CommMpiBarrier(); | 565 | CommMpiBarrier(); |
| 797 | 566 | ||
| 798 | const std::vector<uint16_t> actual_out = CopyActualOutputToHost(cfg, buffers.out); | 567 | const std::vector<uint16_t> actual_out = CopyActualOutputToHost(cfg, buffers.out); |
| 799 | - if (options.workload_audit) { | ||
| 800 | - PrintWorkloadAuditIfEnabled( | ||
| 801 | - cfg, rank_id, world_size, inputs.expert_idx, inputs.x_active_mask, actual_out.size(), | ||
| 802 | - options.skip_accuracy); | ||
| 803 | - } | ||
| 804 | WriteBinaryFile( | 568 | WriteBinaryFile( |
| 805 | case_dir + "/output_rank" + std::to_string(rank_id) + ".bin", actual_out.data(), | 569 | case_dir + "/output_rank" + std::to_string(rank_id) + ".bin", actual_out.data(), |
| 806 | actual_out.size() * sizeof(uint16_t)); | 570 | actual_out.size() * sizeof(uint16_t)); |
| 807 | - ok = ReportRankAccuracy(rank_id, world_size, cfg, inputs, actual_out, options.skip_accuracy); | 571 | + ok = ReportRankAccuracy(rank_id, world_size, cfg, inputs, actual_out); |
| 808 | } catch (const std::exception& ex) { | 572 | } catch (const std::exception& ex) { |
| 809 | std::cerr << "rank=" << rank_id << " error: " << ex.what() << std::endl; | 573 | std::cerr << "rank=" << rank_id << " error: " << ex.what() << std::endl; |
| 810 | ok = false; | 574 | ok = false; |
| @@ -21,38 +21,24 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | -#include "utils/pto_sync_substrate.hpp" | 24 | +#include "utils/mega_expert_sync.hpp" |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | constexpr uint32_t kCombineVecTileElems = 8192U; | 27 | constexpr uint32_t kCombineVecTileElems = 8192U; |
| 28 | constexpr uint32_t kCombineBufferNum = 2U; | 28 | constexpr uint32_t kCombineBufferNum = 2U; |
| 29 | -constexpr uint32_t kCombineInvalidTask = 0xFFFFFFFFU; | ||
| 30 | -constexpr uint32_t kCombineSmallTokenThreshold = 4096U; | ||
| 31 | -constexpr uint32_t kCombineSmallTokenSubtileRows = 16U; | ||
| 32 | -constexpr uint32_t kCombineSmallTokenSubtileCols = 256U; | ||
| 33 | -constexpr uint32_t kCombineSmallMaxElems = 8192U; | ||
| 34 | -constexpr uint32_t kCombineSmallScaleElems = kCombineSmallMaxElems / kCombineSmallTokenSubtileCols; | ||
| 35 | -constexpr uint32_t kCombineImplDirectLarge = 1U; | ||
| 36 | -constexpr uint32_t kCombineImplDirectSmall = 2U; | ||
| 37 | -constexpr uint32_t kCombineImplDirectAuto = 3U; | ||
| 38 | -constexpr uint32_t kCombineDirectLargeUbStages = 2U; | ||
| 39 | -constexpr uint32_t kCombineDirectSmallUbStages = 2U; | ||
| 40 | -constexpr uint32_t kCombineLargeLanesPerRank = 2U; | ||
| 41 | 29 | ||
| 42 | template <typename OutputElement> | 30 | template <typename OutputElement> |
| 43 | class Combine { | 31 | class Combine { |
| 44 | public: | 32 | public: |
| 45 | AICORE inline void Init(GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData); | 33 | AICORE inline void Init(GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData); |
| 46 | - AICORE inline void Process(); | 34 | + AICORE inline void ProcessFixed(uint32_t groupLocalId, uint32_t groupSize); |
| 35 | + AICORE inline void ProcessFixedFinalBoundary(uint32_t role, uint32_t flatAivId, bool combineActive); | ||
| 47 | 36 | ||
| 48 | private: | 37 | private: |
| 49 | static_assert( | 38 | static_assert( |
| 50 | std::is_same_v<OutputElement, half> || std::is_same_v<OutputElement, bfloat16_t>, | 39 | std::is_same_v<OutputElement, half> || std::is_same_v<OutputElement, bfloat16_t>, |
| 51 | "combine output must be half or bfloat16"); | 40 | "combine output must be half or bfloat16"); |
| 52 | 41 | ||
| 53 | - using VectorShape = pto::Shape<1, 1, 1, 1, pto::DYNAMIC>; | ||
| 54 | - using VectorStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | ||
| 55 | - using ScaleGlobal = pto::GlobalTensor<float, VectorShape, VectorStride, pto::Layout::ND>; | ||
| 56 | using BlockShape = pto::Shape<1, 1, 1, pto::DYNAMIC, pto::DYNAMIC>; | 42 | using BlockShape = pto::Shape<1, 1, 1, pto::DYNAMIC, pto::DYNAMIC>; |
| 57 | using BlockStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | 43 | using BlockStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; |
| 58 | using CBlockGlobal = pto::GlobalTensor<half, BlockShape, BlockStride, pto::Layout::ND>; | 44 | using CBlockGlobal = pto::GlobalTensor<half, BlockShape, BlockStride, pto::Layout::ND>; |
| @@ -60,32 +46,9 @@ private: | |||
| 60 | using TileC = pto::Tile<pto::TileType::Vec, half, 1, kCombineVecTileElems, pto::BLayout::RowMajor, -1, -1>; | 46 | using TileC = pto::Tile<pto::TileType::Vec, half, 1, kCombineVecTileElems, pto::BLayout::RowMajor, -1, -1>; |
| 61 | using TileFp32 = pto::Tile<pto::TileType::Vec, float, 1, kCombineVecTileElems, pto::BLayout::RowMajor, -1, -1>; | 47 | using TileFp32 = pto::Tile<pto::TileType::Vec, float, 1, kCombineVecTileElems, pto::BLayout::RowMajor, -1, -1>; |
| 62 | using TileD = pto::Tile<pto::TileType::Vec, OutputElement, 1, kCombineVecTileElems, pto::BLayout::RowMajor, -1, -1>; | 48 | using TileD = pto::Tile<pto::TileType::Vec, OutputElement, 1, kCombineVecTileElems, pto::BLayout::RowMajor, -1, -1>; |
| 63 | - using SmallTileC = pto::Tile< | 49 | + AICORE inline bool RankStreamingEnabled() const |
| 64 | - pto::TileType::Vec, half, kCombineSmallTokenSubtileRows, kCombineSmallTokenSubtileCols, pto::BLayout::RowMajor, | ||
| 65 | - -1, -1>; | ||
| 66 | - using SmallTileFp32 = pto::Tile< | ||
| 67 | - pto::TileType::Vec, float, kCombineSmallTokenSubtileRows, kCombineSmallTokenSubtileCols, pto::BLayout::RowMajor, | ||
| 68 | - -1, -1>; | ||
| 69 | - using SmallTileD = pto::Tile< | ||
| 70 | - pto::TileType::Vec, OutputElement, kCombineSmallTokenSubtileRows, kCombineSmallTokenSubtileCols, | ||
| 71 | - pto::BLayout::RowMajor, -1, -1>; | ||
| 72 | - | ||
| 73 | - AICORE inline uint64_t TokenVolume() const { return static_cast<uint64_t>(problemM_) * topK_; } | ||
| 74 | - AICORE inline bool IsSmallTokenPath() const { return TokenVolume() <= kCombineSmallTokenThreshold; } | ||
| 75 | - AICORE inline uint32_t CombineImplMode() const { return tilingData_->combineTiling.combineImplMode; } | ||
| 76 | - AICORE inline bool DirectLargeEnabled() const | ||
| 77 | { | 50 | { |
| 78 | - const uint32_t mode = CombineImplMode(); | 51 | + return tilingData_->unpermuteTiling.unpermuteImplMode == kMegaMoeUnpermuteImplRankStreaming; |
| 79 | - return (mode == kCombineImplDirectLarge || mode == kCombineImplDirectAuto) && !IsSmallTokenPath(); | ||
| 80 | - } | ||
| 81 | - AICORE inline bool DirectSmallEnabled() const | ||
| 82 | - { | ||
| 83 | - const uint32_t mode = CombineImplMode(); | ||
| 84 | - return (mode == kCombineImplDirectSmall || mode == kCombineImplDirectAuto) && IsSmallTokenPath(); | ||
| 85 | - } | ||
| 86 | - AICORE inline uint16_t Gmm2ToCombineFlagId(uint32_t groupIdx) const | ||
| 87 | - { | ||
| 88 | - return MEGA_MOE_GMM2_TO_COMBINE_HARD_FLAG_BASE + groupIdx / CROSS_CORE_FLAG_MAX_SET_COUNT; | ||
| 89 | } | 52 | } |
| 90 | AICORE inline uint32_t CurrentM(uint32_t groupIdx) const | 53 | AICORE inline uint32_t CurrentM(uint32_t groupIdx) const |
| 91 | { | 54 | { |
| @@ -125,7 +88,23 @@ private: | |||
| 125 | if (lanes == 0U) { | 88 | if (lanes == 0U) { |
| 126 | lanes = 1U; | 89 | lanes = 1U; |
| 127 | } | 90 | } |
| 128 | - return lanes > kCombineLargeLanesPerRank ? kCombineLargeLanesPerRank : lanes; | 91 | + const uint32_t configuredLanes = tilingData_->fixedGroupTiling.combineLargeLanesPerRank; |
| 92 | + return lanes > configuredLanes ? configuredLanes : lanes; | ||
| 93 | + } | ||
| 94 | + AICORE inline uint32_t DirectLargeTaskCount() const { return rankSize_ * LargeLanesPerRank(); } | ||
| 95 | + AICORE inline uint32_t DirectLargeWorkerCount() const | ||
| 96 | + { | ||
| 97 | + const uint32_t taskCount = DirectLargeTaskCount(); | ||
| 98 | + return coreNum_ < taskCount ? coreNum_ : taskCount; | ||
| 99 | + } | ||
| 100 | + AICORE inline uint32_t ReadyCoordinatorCore() const | ||
| 101 | + { | ||
| 102 | + const uint32_t workerCount = DirectLargeWorkerCount(); | ||
| 103 | + if (workerCount == 0U) { | ||
| 104 | + return 0U; | ||
| 105 | + } | ||
| 106 | + const uint64_t firstLocalTask = static_cast<uint64_t>(rank_) * LargeLanesPerRank(); | ||
| 107 | + return static_cast<uint32_t>(firstLocalTask % workerCount); | ||
| 129 | } | 108 | } |
| 130 | AICORE inline uint32_t LargeLaneRowBegin(uint32_t rows, uint32_t laneIdx, uint32_t lanesPerRank) const | 109 | AICORE inline uint32_t LargeLaneRowBegin(uint32_t rows, uint32_t laneIdx, uint32_t lanesPerRank) const |
| 131 | { | 110 | { |
| @@ -145,26 +124,18 @@ private: | |||
| 145 | AICORE inline void InitUbLayout(); | 124 | AICORE inline void InitUbLayout(); |
| 146 | AICORE inline void SetInitialFlags() const; | 125 | AICORE inline void SetInitialFlags() const; |
| 147 | AICORE inline void FinalizeLocalPipe() const; | 126 | AICORE inline void FinalizeLocalPipe() const; |
| 127 | + AICORE inline void FinalizeExpertStores() const; | ||
| 148 | AICORE inline uint32_t TokenPerExpertResetElems() const; | 128 | AICORE inline uint32_t TokenPerExpertResetElems() const; |
| 149 | - AICORE inline bool ResetTokenPerExpert(uint32_t elems) const; | 129 | + AICORE inline bool ResetTokenPerExpertByOwner(uint32_t elems, bool resetOwner) const; |
| 150 | - AICORE inline void ProcessFinalBoundary(); | 130 | + AICORE inline void PublishAssignedExpertProgress(uint32_t readyExpertCount) const; |
| 151 | - AICORE inline void WaitGmm2Ready(uint32_t groupIdx, bool aivSyncAfterWait) const; | 131 | + AICORE inline void FinalizeRankStreamingLane(); |
| 132 | + AICORE inline uint32_t Gmm2ProducerCount(uint32_t groupIdx) const; | ||
| 133 | + AICORE inline void WaitGmm2Ready(uint32_t groupIdx) const; | ||
| 152 | AICORE inline void ProcessDirectLargeSegmentRows( | 134 | AICORE inline void ProcessDirectLargeSegmentRows( |
| 153 | uint32_t srcRank, uint32_t srcRowOffset, uint32_t rows, uint32_t dstRowOffset); | 135 | uint32_t srcRank, uint32_t srcRowOffset, uint32_t rows, uint32_t dstRowOffset); |
| 154 | AICORE inline void ProcessDirectLargeTokenPath(); | 136 | AICORE inline void ProcessDirectLargeTokenPath(); |
| 155 | - AICORE inline void LoadSmallSubtile( | ||
| 156 | - uint32_t bufferId, uint32_t srcRowOffset, uint32_t rowNum, uint32_t colBegin, uint32_t colNum) const; | ||
| 157 | - AICORE inline void DequantDirectSmallSubtile(uint32_t bufferId, uint32_t rowNum, uint32_t colNum); | ||
| 158 | - AICORE inline void StoreSmallSubtileIntersection( | ||
| 159 | - uint32_t bufferId, __gm__ OutputElement* dstBase, uint32_t dstRowOffset, uint32_t ubRowOffset, uint32_t rowNum, | ||
| 160 | - uint32_t colBegin, uint32_t colNum); | ||
| 161 | - AICORE inline void StoreSmallSubtileToRanks( | ||
| 162 | - uint32_t groupIdx, const GmmCommonTileInfo& tileInfo, uint32_t tileRowBegin, uint32_t rows, uint32_t bufferId); | ||
| 163 | - AICORE inline void ProcessDirectSmallTile( | ||
| 164 | - uint32_t groupIdx, uint32_t groupBase, const GmmCommonTileInfo& tileInfo, uint32_t subtileBegin, | ||
| 165 | - uint32_t subtileCount); | ||
| 166 | - AICORE inline void ProcessDirectSmallTokenPath(); | ||
| 167 | 137 | ||
| 138 | + GM_ADDR workspaceGM_ = nullptr; | ||
| 168 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; | 139 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; |
| 169 | 140 | ||
| 170 | PtoRemoteWindow remoteWindow_; | 141 | PtoRemoteWindow remoteWindow_; |
| @@ -175,9 +146,7 @@ private: | |||
| 175 | __gm__ int32_t* preSumBeforeRankPtr_ = nullptr; | 146 | __gm__ int32_t* preSumBeforeRankPtr_ = nullptr; |
| 176 | __gm__ int32_t* tokenPerExpertPtr_ = nullptr; | 147 | __gm__ int32_t* tokenPerExpertPtr_ = nullptr; |
| 177 | 148 | ||
| 178 | - uint32_t problemM_ = 0; | ||
| 179 | uint32_t problemK_ = 0; | 149 | uint32_t problemK_ = 0; |
| 180 | - uint32_t topK_ = 0; | ||
| 181 | uint32_t maxOutputSize_ = 0; | 150 | uint32_t maxOutputSize_ = 0; |
| 182 | uint32_t expertPerRank_ = 0; | 151 | uint32_t expertPerRank_ = 0; |
| 183 | uint32_t expertNumAligned_ = 0; | 152 | uint32_t expertNumAligned_ = 0; |
| @@ -186,22 +155,20 @@ private: | |||
| 186 | uint32_t coreIdx_ = 0; | 155 | uint32_t coreIdx_ = 0; |
| 187 | uint32_t coreNum_ = 1; | 156 | uint32_t coreNum_ = 1; |
| 188 | uint32_t pingpongId_ = 0; | 157 | uint32_t pingpongId_ = 0; |
| 158 | + int32_t dataReadyEpoch_ = 0; | ||
| 189 | uint64_t ubCOffset_[kCombineBufferNum] = {0, 0}; | 159 | uint64_t ubCOffset_[kCombineBufferNum] = {0, 0}; |
| 190 | uint64_t ubFp32Offset_[kCombineBufferNum] = {0, 0}; | 160 | uint64_t ubFp32Offset_[kCombineBufferNum] = {0, 0}; |
| 191 | uint64_t ubDOffset_[kCombineBufferNum] = {0, 0}; | 161 | uint64_t ubDOffset_[kCombineBufferNum] = {0, 0}; |
| 192 | - uint64_t ubScaleOffset_[kCombineBufferNum] = {0, 0}; | ||
| 193 | - mutable uint32_t smallScaleSourceOffset_[kCombineBufferNum] = {kCombineInvalidTask, kCombineInvalidTask}; | ||
| 194 | }; | 162 | }; |
| 195 | 163 | ||
| 196 | template <typename OutputElement> | 164 | template <typename OutputElement> |
| 197 | AICORE inline void Combine<OutputElement>::Init(GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) | 165 | AICORE inline void Combine<OutputElement>::Init(GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) |
| 198 | { | 166 | { |
| 167 | + workspaceGM_ = workspaceGM; | ||
| 199 | tilingData_ = tilingData; | 168 | tilingData_ = tilingData; |
| 200 | pingpongId_ = 0; | 169 | pingpongId_ = 0; |
| 201 | 170 | ||
| 202 | - problemM_ = tilingData_->megaMoeInfo.M; | ||
| 203 | problemK_ = tilingData_->megaMoeInfo.K; | 171 | problemK_ = tilingData_->megaMoeInfo.K; |
| 204 | - topK_ = tilingData_->megaMoeInfo.topK; | ||
| 205 | maxOutputSize_ = tilingData_->megaMoeInfo.maxOutputSize; | 172 | maxOutputSize_ = tilingData_->megaMoeInfo.maxOutputSize; |
| 206 | expertPerRank_ = tilingData_->megaMoeInfo.expertPerRank; | 173 | expertPerRank_ = tilingData_->megaMoeInfo.expertPerRank; |
| 207 | expertNumAligned_ = tilingData_->frontReorderTiling.expertNumAligned; | 174 | expertNumAligned_ = tilingData_->frontReorderTiling.expertNumAligned; |
| @@ -224,6 +191,7 @@ AICORE inline void Combine<OutputElement>::Init(GM_ADDR workspaceGM, const __gm_ | |||
| 224 | reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.preSumBeforeRankOffset); | 191 | reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.preSumBeforeRankOffset); |
| 225 | tokenPerExpertPtr_ = | 192 | tokenPerExpertPtr_ = |
| 226 | reinterpret_cast<__gm__ int32_t*>(remoteWindow_.LocalBase() + peerMemoryLayout_.offsetPeerTokenPerExpert); | 193 | reinterpret_cast<__gm__ int32_t*>(remoteWindow_.LocalBase() + peerMemoryLayout_.offsetPeerTokenPerExpert); |
| 194 | + dataReadyEpoch_ = RankStreamingEnabled() ? remoteWindow_.DataReadyEpoch() : 0; | ||
| 227 | 195 | ||
| 228 | InitUbLayout(); | 196 | InitUbLayout(); |
| 229 | } | 197 | } |
| @@ -232,30 +200,13 @@ template <typename OutputElement> | |||
| 232 | AICORE inline void Combine<OutputElement>::InitUbLayout() | 200 | AICORE inline void Combine<OutputElement>::InitUbLayout() |
| 233 | { | 201 | { |
| 234 | uint64_t ubOffset = 0; | 202 | uint64_t ubOffset = 0; |
| 235 | - if (DirectLargeEnabled()) { | ||
| 236 | - for (uint32_t i = 0; i < kCombineBufferNum; ++i) { | ||
| 237 | - ubCOffset_[i] = ubOffset; | ||
| 238 | - ubOffset += alignUp(static_cast<uint64_t>(problemK_) * sizeof(half), UB_ALIGN); | ||
| 239 | - ubDOffset_[i] = ubOffset; | ||
| 240 | - ubOffset += alignUp(static_cast<uint64_t>(problemK_) * sizeof(OutputElement), UB_ALIGN); | ||
| 241 | - ubFp32Offset_[i] = ubOffset; | ||
| 242 | - ubOffset += alignUp(static_cast<uint64_t>(problemK_) * sizeof(float), UB_ALIGN); | ||
| 243 | - ubScaleOffset_[i] = 0U; | ||
| 244 | - smallScaleSourceOffset_[i] = kCombineInvalidTask; | ||
| 245 | - } | ||
| 246 | - return; | ||
| 247 | - } | ||
| 248 | - | ||
| 249 | for (uint32_t i = 0; i < kCombineBufferNum; ++i) { | 203 | for (uint32_t i = 0; i < kCombineBufferNum; ++i) { |
| 250 | ubCOffset_[i] = ubOffset; | 204 | ubCOffset_[i] = ubOffset; |
| 251 | - ubOffset += alignUp(static_cast<uint64_t>(kCombineSmallMaxElems) * sizeof(half), UB_ALIGN); | 205 | + ubOffset += alignUp(static_cast<uint64_t>(problemK_) * sizeof(half), UB_ALIGN); |
| 252 | ubDOffset_[i] = ubOffset; | 206 | ubDOffset_[i] = ubOffset; |
| 253 | - ubOffset += alignUp(static_cast<uint64_t>(kCombineSmallMaxElems) * sizeof(OutputElement), UB_ALIGN); | 207 | + ubOffset += alignUp(static_cast<uint64_t>(problemK_) * sizeof(OutputElement), UB_ALIGN); |
| 254 | ubFp32Offset_[i] = ubOffset; | 208 | ubFp32Offset_[i] = ubOffset; |
| 255 | - ubOffset += alignUp(static_cast<uint64_t>(kCombineSmallMaxElems) * sizeof(float), UB_ALIGN); | 209 | + ubOffset += alignUp(static_cast<uint64_t>(problemK_) * sizeof(float), UB_ALIGN); |
| 256 | - ubScaleOffset_[i] = ubOffset; | ||
| 257 | - ubOffset += alignUp(static_cast<uint64_t>(kCombineSmallScaleElems) * sizeof(float), UB_ALIGN); | ||
| 258 | - smallScaleSourceOffset_[i] = kCombineInvalidTask; | ||
| 259 | } | 210 | } |
| 260 | } | 211 | } |
| 261 | 212 | ||
| @@ -277,6 +228,19 @@ AICORE inline void Combine<OutputElement>::FinalizeLocalPipe() const | |||
| 277 | } | 228 | } |
| 278 | } | 229 | } |
| 279 | 230 | ||
| 231 | +template <typename OutputElement> | ||
| 232 | +AICORE inline void Combine<OutputElement>::FinalizeExpertStores() const | ||
| 233 | +{ | ||
| 234 | + for (uint32_t i = 0; i < kCombineBufferNum; ++i) { | ||
| 235 | + wait_flag(PIPE_MTE3, PIPE_V, StoreFreeEvent(i)); | ||
| 236 | + } | ||
| 237 | + for (uint32_t i = 0; i < kCombineBufferNum; ++i) { | ||
| 238 | + set_flag(PIPE_MTE3, PIPE_V, StoreFreeEvent(i)); | ||
| 239 | + } | ||
| 240 | + pipe_barrier(PIPE_ALL); | ||
| 241 | + dsb(DSB_DDR); | ||
| 242 | +} | ||
| 243 | + | ||
| 280 | template <typename OutputElement> | 244 | template <typename OutputElement> |
| 281 | AICORE inline uint32_t Combine<OutputElement>::TokenPerExpertResetElems() const | 245 | AICORE inline uint32_t Combine<OutputElement>::TokenPerExpertResetElems() const |
| 282 | { | 246 | { |
| @@ -284,9 +248,9 @@ AICORE inline uint32_t Combine<OutputElement>::TokenPerExpertResetElems() const | |||
| 284 | } | 248 | } |
| 285 | 249 | ||
| 286 | template <typename OutputElement> | 250 | template <typename OutputElement> |
| 287 | -AICORE inline bool Combine<OutputElement>::ResetTokenPerExpert(uint32_t elems) const | 251 | +AICORE inline bool Combine<OutputElement>::ResetTokenPerExpertByOwner(uint32_t elems, bool resetOwner) const |
| 288 | { | 252 | { |
| 289 | - if (coreIdx_ != coreNum_ - 1U) { | 253 | + if (!resetOwner) { |
| 290 | return false; | 254 | return false; |
| 291 | } | 255 | } |
| 292 | PtoFillUb<int32_t>(0U, 0, elems); | 256 | PtoFillUb<int32_t>(0U, 0, elems); |
| @@ -298,20 +262,91 @@ AICORE inline bool Combine<OutputElement>::ResetTokenPerExpert(uint32_t elems) c | |||
| 298 | } | 262 | } |
| 299 | 263 | ||
| 300 | template <typename OutputElement> | 264 | template <typename OutputElement> |
| 301 | -AICORE inline void Combine<OutputElement>::ProcessFinalBoundary() | 265 | +AICORE inline void Combine<OutputElement>::ProcessFixedFinalBoundary( |
| 266 | + uint32_t role, uint32_t flatAivId, bool combineActive) | ||
| 302 | { | 267 | { |
| 303 | - FinalizeLocalPipe(); | 268 | + if (role == kMegaMoeFixedRoleCombine && combineActive) { |
| 269 | + FinalizeLocalPipe(); | ||
| 270 | + } | ||
| 304 | pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); | 271 | pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); |
| 305 | - (void)ResetTokenPerExpert(TokenPerExpertResetElems()); | 272 | + ResetTokenPerExpertByOwner(TokenPerExpertResetElems(), flatAivId + 1U == kMegaMoeFixedPhysicalAivNum); |
| 306 | remoteWindow_.CrossRankSync(); | 273 | remoteWindow_.CrossRankSync(); |
| 307 | } | 274 | } |
| 308 | 275 | ||
| 309 | template <typename OutputElement> | 276 | template <typename OutputElement> |
| 310 | -AICORE inline void Combine<OutputElement>::WaitGmm2Ready(uint32_t groupIdx, bool aivSyncAfterWait) const | 277 | +AICORE inline void Combine<OutputElement>::PublishAssignedExpertProgress(uint32_t readyExpertCount) const |
| 311 | { | 278 | { |
| 312 | - CrossCoreWaitFlag<0x2>(Gmm2ToCombineFlagId(groupIdx)); | 279 | + const uint32_t lanesPerRank = LargeLanesPerRank(); |
| 313 | - if (aivSyncAfterWait) { | 280 | + const uint32_t taskCount = DirectLargeTaskCount(); |
| 314 | - pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); | 281 | + for (uint32_t taskIdx = coreIdx_; taskIdx < taskCount; taskIdx += coreNum_) { |
| 282 | + const uint32_t ownerRank = taskIdx / lanesPerRank; | ||
| 283 | + remoteWindow_.PublishExpertProgress(static_cast<int32_t>(ownerRank), readyExpertCount, dataReadyEpoch_); | ||
| 284 | + } | ||
| 285 | +} | ||
| 286 | + | ||
| 287 | +template <typename OutputElement> | ||
| 288 | +AICORE inline void Combine<OutputElement>::FinalizeRankStreamingLane() | ||
| 289 | +{ | ||
| 290 | + const uint32_t taskCount = DirectLargeTaskCount(); | ||
| 291 | + const uint32_t combineWorkerCount = DirectLargeWorkerCount(); | ||
| 292 | + if (coreIdx_ >= combineWorkerCount) { | ||
| 293 | + return; | ||
| 294 | + } | ||
| 295 | + | ||
| 296 | + remoteWindow_.AcquireDataReady(); | ||
| 297 | + const uint32_t lanesPerRank = LargeLanesPerRank(); | ||
| 298 | + for (uint32_t taskIdx = coreIdx_; taskIdx < taskCount; taskIdx += coreNum_) { | ||
| 299 | + const uint32_t ownerRank = taskIdx / lanesPerRank; | ||
| 300 | + remoteWindow_.PublishExpertProgress(static_cast<int32_t>(ownerRank), expertPerRank_, dataReadyEpoch_); | ||
| 301 | + remoteWindow_.PublishDataReady(static_cast<int32_t>(ownerRank), dataReadyEpoch_); | ||
| 302 | + } | ||
| 303 | + remoteWindow_.PublishLocalCombineDone(coreIdx_, dataReadyEpoch_); | ||
| 304 | + | ||
| 305 | + if (coreIdx_ != 0U) { | ||
| 306 | + return; | ||
| 307 | + } | ||
| 308 | + remoteWindow_.WaitLocalCombineDoneMte(combineWorkerCount, dataReadyEpoch_); | ||
| 309 | + ResetTokenPerExpertByOwner(TokenPerExpertResetElems(), true); | ||
| 310 | + const uint32_t workerCount = tilingData_->fixedGroupTiling.physicalAivNum; | ||
| 311 | + const uint32_t initialWorkerCount = tilingData_->fixedGroupTiling.gmm1GroupSize * 2U; | ||
| 312 | + const uint32_t helperCount = workerCount > initialWorkerCount ? workerCount - initialWorkerCount : 0U; | ||
| 313 | + remoteWindow_.PublishUnpermuteStartRangeMte(initialWorkerCount, helperCount, dataReadyEpoch_); | ||
| 314 | +} | ||
| 315 | + | ||
| 316 | +template <typename OutputElement> | ||
| 317 | +AICORE inline uint32_t Combine<OutputElement>::Gmm2ProducerCount(uint32_t groupIdx) const | ||
| 318 | +{ | ||
| 319 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData_->fixedGroupTiling; | ||
| 320 | + if (groupIdx < fixed.gmm2JoinCheckStartExpert) { | ||
| 321 | + return fixed.gmm2GroupSize; | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + const int32_t decision = WaitEpochAcquire( | ||
| 325 | + FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm2JoinSlot), | ||
| 326 | + static_cast<int32_t>(groupIdx + 1U)); | ||
| 327 | + const uint32_t encodedExpert = static_cast<uint32_t>(decision & kMegaMoeFixedGmm2JoinDecisionMask); | ||
| 328 | + const bool joined = | ||
| 329 | + (decision & kMegaMoeFixedGmm2JoinDecisionBit) != 0 && encodedExpert != 0U && groupIdx + 1U >= encodedExpert; | ||
| 330 | + return joined ? fixed.physicalAicNum : fixed.gmm2GroupSize; | ||
| 331 | +} | ||
| 332 | + | ||
| 333 | +template <typename OutputElement> | ||
| 334 | +AICORE inline void Combine<OutputElement>::WaitGmm2Ready(uint32_t groupIdx) const | ||
| 335 | +{ | ||
| 336 | + const MegaMoeSyncLayout sync = FixedSyncLayout(tilingData_); | ||
| 337 | + const uint32_t readyLocalId = coreIdx_ % tilingData_->fixedGroupTiling.gmm2GroupSize; | ||
| 338 | + const uint32_t readySlot = sync.combineReadyBase + readyLocalId; | ||
| 339 | + const int32_t expectedEpoch = static_cast<int32_t>(groupIdx * 2U + 2U); | ||
| 340 | + if (coreIdx_ == ReadyCoordinatorCore()) { | ||
| 341 | + const uint32_t producerCount = Gmm2ProducerCount(groupIdx); | ||
| 342 | + const uint32_t producerBaseOffset = producerCount == tilingData_->fixedGroupTiling.physicalAicNum ? | ||
| 343 | + 0U : | ||
| 344 | + tilingData_->fixedGroupTiling.gmm1GroupSize; | ||
| 345 | + CoordinateGroupConsumersMte( | ||
| 346 | + workspaceGM_, tilingData_, sync.gmm2ArrivalBase + producerBaseOffset, sync.combineReadyBase, producerCount, | ||
| 347 | + tilingData_->fixedGroupTiling.gmm2GroupSize, groupIdx); | ||
| 348 | + } else { | ||
| 349 | + WaitEpochAcquire(FixedSyncSlot(workspaceGM_, tilingData_, readySlot), expectedEpoch); | ||
| 315 | } | 350 | } |
| 316 | } | 351 | } |
| 317 | 352 | ||
| @@ -375,14 +410,23 @@ template <typename OutputElement> | |||
| 375 | AICORE inline void Combine<OutputElement>::ProcessDirectLargeTokenPath() | 410 | AICORE inline void Combine<OutputElement>::ProcessDirectLargeTokenPath() |
| 376 | { | 411 | { |
| 377 | uint32_t groupBase = 0; | 412 | uint32_t groupBase = 0; |
| 413 | + const bool delayedStart = expertPerRank_ != 0U; | ||
| 414 | + const uint32_t initialReadyExpert = expertPerRank_ > tilingData_->fixedGroupTiling.combineStartAfterGmm2Expert ? | ||
| 415 | + tilingData_->fixedGroupTiling.combineStartAfterGmm2Expert : | ||
| 416 | + expertPerRank_ - 1U; | ||
| 417 | + if (delayedStart && coreIdx_ < DirectLargeTaskCount()) { | ||
| 418 | + WaitGmm2Ready(initialReadyExpert); | ||
| 419 | + } | ||
| 378 | for (uint32_t groupIdx = 0; groupIdx < expertPerRank_; ++groupIdx) { // 逐个group遍历 | 420 | for (uint32_t groupIdx = 0; groupIdx < expertPerRank_; ++groupIdx) { // 逐个group遍历 |
| 379 | const uint32_t currentM = CurrentM(groupIdx); // expert 总共有多少输出 row | 421 | const uint32_t currentM = CurrentM(groupIdx); // expert 总共有多少输出 row |
| 380 | - WaitGmm2Ready(groupIdx, true); | ||
| 381 | const uint32_t lanesPerRank = LargeLanesPerRank(); | 422 | const uint32_t lanesPerRank = LargeLanesPerRank(); |
| 423 | + const uint32_t taskCount = rankSize_ * lanesPerRank; | ||
| 424 | + if (!delayedStart || groupIdx > initialReadyExpert) { | ||
| 425 | + WaitGmm2Ready(groupIdx); | ||
| 426 | + } | ||
| 382 | if (lanesPerRank == 0U) { | 427 | if (lanesPerRank == 0U) { |
| 383 | continue; | 428 | continue; |
| 384 | } | 429 | } |
| 385 | - const uint32_t taskCount = rankSize_ * lanesPerRank; // 卡按照lane再切分,当前默认每卡 2 lane | ||
| 386 | for (uint32_t taskIdx = coreIdx_; taskIdx < taskCount; taskIdx += coreNum_) { | 430 | for (uint32_t taskIdx = coreIdx_; taskIdx < taskCount; taskIdx += coreNum_) { |
| 387 | const uint32_t safeLanes = lanesPerRank == 0U ? 1U : lanesPerRank; | 431 | const uint32_t safeLanes = lanesPerRank == 0U ? 1U : lanesPerRank; |
| 388 | const uint32_t srcRank = taskIdx / safeLanes; | 432 | const uint32_t srcRank = taskIdx / safeLanes; |
| @@ -395,195 +439,33 @@ AICORE inline void Combine<OutputElement>::ProcessDirectLargeTokenPath() | |||
| 395 | const uint32_t dstRowOffset = DstRowOffset(srcRank, groupIdx); | 439 | const uint32_t dstRowOffset = DstRowOffset(srcRank, groupIdx); |
| 396 | ProcessDirectLargeSegmentRows(srcRank, srcRowOffset + rowBegin, rowNum, dstRowOffset + rowBegin); | 440 | ProcessDirectLargeSegmentRows(srcRank, srcRowOffset + rowBegin, rowNum, dstRowOffset + rowBegin); |
| 397 | } | 441 | } |
| 442 | + const uint32_t readyExpertCount = groupIdx + 1U; | ||
| 443 | + const uint32_t phase1ReadyExpertCount = tilingData_->fixedGroupTiling.unpermutePhase1ReadyExpertCount; | ||
| 444 | + if (RankStreamingEnabled() && lanesPerRank == 1U && coreIdx_ < DirectLargeWorkerCount() && | ||
| 445 | + readyExpertCount == phase1ReadyExpertCount && readyExpertCount < expertPerRank_) { | ||
| 446 | + FinalizeExpertStores(); | ||
| 447 | + PublishAssignedExpertProgress(readyExpertCount); | ||
| 448 | + } | ||
| 398 | groupBase += currentM; | 449 | groupBase += currentM; |
| 399 | } | 450 | } |
| 400 | } | 451 | } |
| 401 | 452 | ||
| 402 | template <typename OutputElement> | 453 | template <typename OutputElement> |
| 403 | -AICORE inline void Combine<OutputElement>::LoadSmallSubtile( | 454 | +AICORE inline void Combine<OutputElement>::ProcessFixed(uint32_t groupLocalId, uint32_t groupSize) |
| 404 | - uint32_t bufferId, uint32_t srcRowOffset, uint32_t rowNum, uint32_t colBegin, uint32_t colNum) const | ||
| 405 | -{ | ||
| 406 | - wait_flag(PIPE_V, PIPE_MTE2, LoadFreeEvent(bufferId)); | ||
| 407 | - SmallTileC cTile(rowNum, colNum); | ||
| 408 | - pto::TASSIGN(cTile, ubCOffset_[bufferId]); | ||
| 409 | - BlockShape cShape(rowNum, colNum); | ||
| 410 | - BlockStride cStride( | ||
| 411 | - static_cast<int64_t>(rowNum) * problemK_, static_cast<int64_t>(rowNum) * problemK_, | ||
| 412 | - static_cast<int64_t>(rowNum) * problemK_, problemK_); | ||
| 413 | - CBlockGlobal cGlobal(gmm2OutputPtr_ + static_cast<uint64_t>(srcRowOffset) * problemK_ + colBegin, cShape, cStride); | ||
| 414 | - pto::TLOAD(cTile, cGlobal); | ||
| 415 | - | ||
| 416 | - if (smallScaleSourceOffset_[bufferId] != srcRowOffset) { | ||
| 417 | - SmallTileFp32 scaleTile(1, rowNum); | ||
| 418 | - pto::TASSIGN(scaleTile, ubScaleOffset_[bufferId]); | ||
| 419 | - VectorShape scaleShape(rowNum); | ||
| 420 | - VectorStride scaleStride(rowNum, rowNum, rowNum, rowNum); | ||
| 421 | - ScaleGlobal scaleGlobal(perTokenScale2Ptr_ + srcRowOffset, scaleShape, scaleStride); | ||
| 422 | - pto::TLOAD(scaleTile, scaleGlobal); | ||
| 423 | - smallScaleSourceOffset_[bufferId] = srcRowOffset; | ||
| 424 | - } | ||
| 425 | - set_flag(PIPE_MTE2, PIPE_V, LoadReadyEvent(bufferId)); | ||
| 426 | - set_flag(PIPE_MTE2, PIPE_S, LoadReadyEvent(bufferId)); | ||
| 427 | -} | ||
| 428 | - | ||
| 429 | -template <typename OutputElement> | ||
| 430 | -AICORE inline void Combine<OutputElement>::DequantDirectSmallSubtile( | ||
| 431 | - uint32_t bufferId, uint32_t rowNum, uint32_t colNum) | ||
| 432 | -{ | ||
| 433 | - wait_flag(PIPE_MTE2, PIPE_V, LoadReadyEvent(bufferId)); | ||
| 434 | - SmallTileFp32 fp32Tile(rowNum, colNum); | ||
| 435 | - SmallTileC cTile(rowNum, colNum); | ||
| 436 | - pto::TASSIGN(fp32Tile, ubFp32Offset_[bufferId]); | ||
| 437 | - pto::TASSIGN(cTile, ubCOffset_[bufferId]); | ||
| 438 | - pto::TCVT(fp32Tile, cTile, pto::RoundMode::CAST_NONE); | ||
| 439 | - pipe_barrier(PIPE_V); | ||
| 440 | - set_flag(PIPE_V, PIPE_MTE2, LoadFreeEvent(bufferId)); | ||
| 441 | - | ||
| 442 | - wait_flag(PIPE_MTE2, PIPE_S, LoadReadyEvent(bufferId)); | ||
| 443 | - SmallTileFp32 scaleTile(1, rowNum); | ||
| 444 | - pto::TASSIGN(scaleTile, ubScaleOffset_[bufferId]); | ||
| 445 | - for (uint32_t row = 0; row < rowNum; ++row) { | ||
| 446 | - const float scale = scaleTile.GetValue(row); | ||
| 447 | - SmallTileFp32 rowTile(1, colNum); | ||
| 448 | - pto::TASSIGN( | ||
| 449 | - rowTile, | ||
| 450 | - ubFp32Offset_[bufferId] + static_cast<uint64_t>(row) * kCombineSmallTokenSubtileCols * sizeof(float)); | ||
| 451 | - pto::TMULS(rowTile, rowTile, scale); | ||
| 452 | - } | ||
| 453 | - pipe_barrier(PIPE_V); | ||
| 454 | - wait_flag(PIPE_MTE3, PIPE_V, StoreFreeEvent(bufferId)); | ||
| 455 | - SmallTileD dTile(rowNum, colNum); | ||
| 456 | - pto::TASSIGN(dTile, ubDOffset_[bufferId]); | ||
| 457 | - pto::TCVT(dTile, fp32Tile, pto::RoundMode::CAST_RINT); | ||
| 458 | - set_flag(PIPE_V, PIPE_MTE3, StoreReadyEvent(bufferId)); | ||
| 459 | -} | ||
| 460 | - | ||
| 461 | -template <typename OutputElement> | ||
| 462 | -AICORE inline void Combine<OutputElement>::StoreSmallSubtileIntersection( | ||
| 463 | - uint32_t bufferId, __gm__ OutputElement* dstBase, uint32_t dstRowOffset, uint32_t ubRowOffset, uint32_t rowNum, | ||
| 464 | - uint32_t colBegin, uint32_t colNum) | ||
| 465 | -{ | ||
| 466 | - SmallTileD dTile(rowNum, colNum); | ||
| 467 | - pto::TASSIGN( | ||
| 468 | - dTile, ubDOffset_[bufferId] + | ||
| 469 | - static_cast<uint64_t>(ubRowOffset) * kCombineSmallTokenSubtileCols * sizeof(OutputElement)); | ||
| 470 | - BlockShape dShape(rowNum, colNum); | ||
| 471 | - BlockStride dStride( | ||
| 472 | - static_cast<int64_t>(rowNum) * problemK_, static_cast<int64_t>(rowNum) * problemK_, | ||
| 473 | - static_cast<int64_t>(rowNum) * problemK_, problemK_); | ||
| 474 | - DBlockGlobal dGlobal(dstBase + static_cast<uint64_t>(dstRowOffset) * problemK_ + colBegin, dShape, dStride); | ||
| 475 | - pto::TSTORE(dGlobal, dTile); | ||
| 476 | -} | ||
| 477 | - | ||
| 478 | -template <typename OutputElement> | ||
| 479 | -AICORE inline void Combine<OutputElement>::StoreSmallSubtileToRanks( | ||
| 480 | - uint32_t groupIdx, const GmmCommonTileInfo& tileInfo, uint32_t tileRowBegin, uint32_t rows, uint32_t bufferId) | ||
| 481 | -{ | ||
| 482 | - const uint32_t stTile = tileRowBegin; | ||
| 483 | - const uint32_t edTile = tileRowBegin + rows; | ||
| 484 | - uint32_t preSumRankInExpert = 0U; | ||
| 485 | - uint32_t tileOffset = 0U; | ||
| 486 | - wait_flag(PIPE_V, PIPE_MTE3, StoreReadyEvent(bufferId)); | ||
| 487 | - for (uint32_t srcRank = 0; srcRank < rankSize_; ++srcRank) { | ||
| 488 | - const uint32_t lenRankInExpert = RowsRaw(srcRank, groupIdx); | ||
| 489 | - const uint32_t dstExpertOffset = DstRowOffset(srcRank, groupIdx); | ||
| 490 | - const uint32_t stRankInExpert = preSumRankInExpert; | ||
| 491 | - const uint32_t edRankInExpert = stRankInExpert + lenRankInExpert; | ||
| 492 | - preSumRankInExpert += lenRankInExpert; | ||
| 493 | - if (stRankInExpert >= edTile) { | ||
| 494 | - break; | ||
| 495 | - } | ||
| 496 | - if (edRankInExpert <= stTile) { | ||
| 497 | - continue; | ||
| 498 | - } | ||
| 499 | - const uint32_t stData = stRankInExpert > stTile ? stRankInExpert : stTile; | ||
| 500 | - const uint32_t edData = edRankInExpert < edTile ? edRankInExpert : edTile; | ||
| 501 | - if (edData <= stData) { | ||
| 502 | - continue; | ||
| 503 | - } | ||
| 504 | - const uint32_t lenData = edData - stData; | ||
| 505 | - const uint32_t dstOffsetInExpert = stTile > stRankInExpert ? stTile - stRankInExpert : 0U; | ||
| 506 | - __gm__ OutputElement* dstBase = reinterpret_cast<__gm__ OutputElement*>( | ||
| 507 | - remoteWindow_.RemoteBase(peerMemoryLayout_.offsetD, static_cast<int32_t>(srcRank))); | ||
| 508 | - if (dstBase != nullptr) { | ||
| 509 | - StoreSmallSubtileIntersection( | ||
| 510 | - bufferId, dstBase, dstExpertOffset + dstOffsetInExpert, tileOffset, lenData, tileInfo.blockColStart, | ||
| 511 | - tileInfo.actualN); | ||
| 512 | - } | ||
| 513 | - tileOffset += lenData; | ||
| 514 | - } | ||
| 515 | - set_flag(PIPE_MTE3, PIPE_V, StoreFreeEvent(bufferId)); | ||
| 516 | -} | ||
| 517 | - | ||
| 518 | -template <typename OutputElement> | ||
| 519 | -AICORE inline void Combine<OutputElement>::ProcessDirectSmallTile( | ||
| 520 | - uint32_t groupIdx, uint32_t groupBase, const GmmCommonTileInfo& tileInfo, uint32_t subtileBegin, | ||
| 521 | - uint32_t subtileCount) | ||
| 522 | -{ | ||
| 523 | - for (uint32_t subtile = 0; subtile < subtileCount; ++subtile) { | ||
| 524 | - const uint32_t rowInTile = (subtileBegin + subtile) * kCombineSmallTokenSubtileRows; | ||
| 525 | - if (rowInTile >= tileInfo.actualM) { | ||
| 526 | - continue; | ||
| 527 | - } | ||
| 528 | - const uint32_t rows = (tileInfo.actualM - rowInTile > kCombineSmallTokenSubtileRows) ? | ||
| 529 | - kCombineSmallTokenSubtileRows : | ||
| 530 | - (tileInfo.actualM - rowInTile); | ||
| 531 | - const uint32_t tileRowBegin = tileInfo.blockRowStart + rowInTile; | ||
| 532 | - const uint32_t srcRow = groupBase + tileRowBegin; | ||
| 533 | - const uint32_t bufferId = pingpongId_; | ||
| 534 | - pingpongId_ = (pingpongId_ + 1U) % kCombineBufferNum; | ||
| 535 | - LoadSmallSubtile(bufferId, srcRow, rows, tileInfo.blockColStart, tileInfo.actualN); | ||
| 536 | - DequantDirectSmallSubtile(bufferId, rows, tileInfo.actualN); | ||
| 537 | - StoreSmallSubtileToRanks(groupIdx, tileInfo, tileRowBegin, rows, bufferId); | ||
| 538 | - } | ||
| 539 | -} | ||
| 540 | - | ||
| 541 | -template <typename OutputElement> | ||
| 542 | -AICORE inline void Combine<OutputElement>::ProcessDirectSmallTokenPath() | ||
| 543 | -{ | ||
| 544 | - uint32_t groupBase = 0; | ||
| 545 | - uint32_t startCoreIdx = 0; | ||
| 546 | - const uint32_t aicCoreIdx = get_block_idx(); | ||
| 547 | - const uint32_t aicCoreNum = get_block_num(); | ||
| 548 | - const uint32_t aivSubCoreIdx = get_subblockid(); | ||
| 549 | - const uint32_t l1TileM = tilingData_->gmm2Tiling.l1TileM; | ||
| 550 | - const uint32_t l1TileN = tilingData_->gmm2Tiling.l1TileN; | ||
| 551 | - for (uint32_t groupIdx = 0; groupIdx < expertPerRank_; ++groupIdx) { | ||
| 552 | - const uint32_t currentM = MoeClipCurrentM(CurrentM(groupIdx), groupBase, maxOutputSize_); | ||
| 553 | - WaitGmm2Ready(groupIdx, false); | ||
| 554 | - const uint32_t coreLoops = GmmCommonCoreLoops(currentM, problemK_, l1TileM, l1TileN); // 按照L1的size做切tile | ||
| 555 | - const uint32_t startLoopIdx = | ||
| 556 | - aicCoreNum == 0U ? 0U : GmmCommonStartLoopIdx(aicCoreIdx, aicCoreNum, startCoreIdx); | ||
| 557 | - for (uint32_t loopIdx = startLoopIdx; aicCoreNum != 0U && loopIdx < coreLoops; loopIdx += aicCoreNum) { | ||
| 558 | - const GmmCommonTileInfo tileInfo = GmmCommonBuildTileInfo(currentM, problemK_, l1TileM, l1TileN, loopIdx); | ||
| 559 | - const uint32_t subtileCount = static_cast<uint32_t>( | ||
| 560 | - ceilDiv(tileInfo.actualM, kCombineSmallTokenSubtileRows)); // 按照m维度做切分成subtile | ||
| 561 | - const uint32_t firstHalfSubtiles = subtileCount / 2U; | ||
| 562 | - const uint32_t firstSubtile = aivSubCoreIdx == 0U ? 0U : firstHalfSubtiles; | ||
| 563 | - uint32_t assignedSubtiles = subtileCount / 2U; | ||
| 564 | - if (aivSubCoreIdx == 1U && assignedSubtiles * 2U < subtileCount) { | ||
| 565 | - ++assignedSubtiles; | ||
| 566 | - } | ||
| 567 | - ProcessDirectSmallTile(groupIdx, groupBase, tileInfo, firstSubtile, assignedSubtiles); | ||
| 568 | - } | ||
| 569 | - startCoreIdx = aicCoreNum == 0U ? 0U : (startCoreIdx + coreLoops) % aicCoreNum; | ||
| 570 | - groupBase += currentM; | ||
| 571 | - } | ||
| 572 | -} | ||
| 573 | - | ||
| 574 | -template <typename OutputElement> | ||
| 575 | -AICORE inline void Combine<OutputElement>::Process() | ||
| 576 | { | 455 | { |
| 577 | if ASCEND_IS_AIC { | 456 | if ASCEND_IS_AIC { |
| 578 | return; | 457 | return; |
| 579 | } | 458 | } |
| 459 | + coreIdx_ = groupLocalId; | ||
| 460 | + coreNum_ = groupSize; | ||
| 580 | SetInitialFlags(); | 461 | SetInitialFlags(); |
| 581 | - if (DirectSmallEnabled()) { // problemM_ * topK_ 小于4096的时候走small case | 462 | + if (coreIdx_ < DirectLargeTaskCount()) { |
| 582 | - ProcessDirectSmallTokenPath(); | ||
| 583 | - } else if (DirectLargeEnabled()) { | ||
| 584 | ProcessDirectLargeTokenPath(); | 463 | ProcessDirectLargeTokenPath(); |
| 585 | } | 464 | } |
| 586 | - ProcessFinalBoundary(); | 465 | + if (RankStreamingEnabled()) { |
| 466 | + FinalizeLocalPipe(); | ||
| 467 | + FinalizeRankStreamingLane(); | ||
| 468 | + } | ||
| 587 | } | 469 | } |
| 588 | 470 | ||
| 589 | 471 | ||
| @@ -19,7 +19,7 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | -#include "utils/pto_sync_substrate.hpp" | 22 | +#include "utils/mega_expert_sync.hpp" |
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | constexpr uint32_t kDispatchBufferNum = 2U; | 25 | constexpr uint32_t kDispatchBufferNum = 2U; |
| @@ -42,6 +42,7 @@ public: | |||
| 42 | AICORE inline void Init(GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) | 42 | AICORE inline void Init(GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) |
| 43 | { | 43 | { |
| 44 | (void)expertTokenNumsGM; | 44 | (void)expertTokenNumsGM; |
| 45 | + workspaceGM_ = workspaceGM; | ||
| 45 | tilingData_ = tilingData; | 46 | tilingData_ = tilingData; |
| 46 | 47 | ||
| 47 | const auto& info = tilingData_->megaMoeInfo; | 48 | const auto& info = tilingData_->megaMoeInfo; |
| @@ -74,11 +75,12 @@ public: | |||
| 74 | offsetAPtr_ = reinterpret_cast<__gm__ int8_t*>(remoteWindow_.LocalBase() + peerMemoryLayout_.offsetA); | 75 | offsetAPtr_ = reinterpret_cast<__gm__ int8_t*>(remoteWindow_.LocalBase() + peerMemoryLayout_.offsetA); |
| 75 | } | 76 | } |
| 76 | 77 | ||
| 77 | - AICORE inline void Process() const | 78 | + AICORE inline void ProcessFixed(uint32_t groupLocalId, uint32_t groupSize) |
| 78 | { | 79 | { |
| 79 | - if ASCEND_IS_AIV { | 80 | + coreIdx_ = groupLocalId; |
| 80 | - ProcessRankSplitCopy(); | 81 | + coreNum_ = groupSize; |
| 81 | - } | 82 | + dispatchGatherScratchPtr_ = reinterpret_cast<__gm__ uint8_t*>(workspaceGM_ + DispatchGatherScratchCoreOffset()); |
| 83 | + ProcessRankSplitCopy(); | ||
| 82 | } | 84 | } |
| 83 | 85 | ||
| 84 | private: | 86 | private: |
| @@ -97,11 +99,6 @@ private: | |||
| 97 | problemK_ / sizeof(uint32_t) <= kDispatchGatherPackedWordTileCols; | 99 | problemK_ / sizeof(uint32_t) <= kDispatchGatherPackedWordTileCols; |
| 98 | } | 100 | } |
| 99 | 101 | ||
| 100 | - AICORE inline void SetGmm1ReadyByLogicalEvent(uint32_t logicalGroupEventIdx) const | ||
| 101 | - { | ||
| 102 | - CrossCoreSetFlag<0x2, PIPE_MTE3>(MegaMoeD2CHardFlagId(logicalGroupEventIdx)); | ||
| 103 | - } | ||
| 104 | - | ||
| 105 | AICORE inline uint64_t DispatchGatherScratchCoreOffset() const | 102 | AICORE inline uint64_t DispatchGatherScratchCoreOffset() const |
| 106 | { | 103 | { |
| 107 | return tilingData_->dispatchTiling.dispatchGatherScratchOffset + | 104 | return tilingData_->dispatchTiling.dispatchGatherScratchOffset + |
| @@ -132,10 +129,6 @@ private: | |||
| 132 | static_cast<uint64_t>(bufferId) * tilingData_->dispatchTiling.dispatchGatherTileBytes); | 129 | static_cast<uint64_t>(bufferId) * tilingData_->dispatchTiling.dispatchGatherTileBytes); |
| 133 | } | 130 | } |
| 134 | 131 | ||
| 135 | - AICORE inline void SetGmm1InitialReady() const { SetGmm1ReadyByLogicalEvent(0U); } | ||
| 136 | - | ||
| 137 | - AICORE inline void SetGmm1GroupReady(uint32_t groupIdx) const { SetGmm1ReadyByLogicalEvent(groupIdx + 1U); } | ||
| 138 | - | ||
| 139 | AICORE inline void PrepareDispatchGatherCopyEvents() const | 132 | AICORE inline void PrepareDispatchGatherCopyEvents() const |
| 140 | { | 133 | { |
| 141 | set_flag(PIPE_MTE3, PIPE_MTE2, DispatchGatherBufferEvent(0U)); | 134 | set_flag(PIPE_MTE3, PIPE_MTE2, DispatchGatherBufferEvent(0U)); |
| @@ -170,22 +163,22 @@ private: | |||
| 170 | } | 163 | } |
| 171 | 164 | ||
| 172 | AICORE inline void FetchRankGroupRows( | 165 | AICORE inline void FetchRankGroupRows( |
| 173 | - uint32_t srcRank, uint32_t groupIdx, uint32_t prevGroupSum, uint32_t& prevSum, int32_t& pingpongIdx) const | 166 | + uint32_t srcRank, uint32_t groupIdx, uint32_t prevGroupSum, int32_t& pingpongIdx) const |
| 174 | { | 167 | { |
| 175 | const uint32_t rawRows = RawRowsForLocalGroup(srcRank, groupIdx); | 168 | const uint32_t rawRows = RawRowsForLocalGroup(srcRank, groupIdx); |
| 176 | const uint32_t dstRowBase = prevGroupSum + CopyCumsumBeforeSource(srcRank, groupIdx); | 169 | const uint32_t dstRowBase = prevGroupSum + CopyCumsumBeforeSource(srcRank, groupIdx); |
| 177 | - const uint32_t srcRowBase = prevSum; | ||
| 178 | uint32_t rows = 0U; | 170 | uint32_t rows = 0U; |
| 179 | if (dstRowBase < maxOutputSize_) { | 171 | if (dstRowBase < maxOutputSize_) { |
| 180 | rows = rawRows; | 172 | rows = rawRows; |
| 181 | if (dstRowBase + rows > maxOutputSize_) { | 173 | if (dstRowBase + rows > maxOutputSize_) { |
| 182 | rows = maxOutputSize_ - dstRowBase; | 174 | rows = maxOutputSize_ - dstRowBase; |
| 183 | } | 175 | } |
| 184 | - prevSum += rows; | ||
| 185 | } | 176 | } |
| 186 | if (rows == 0U) { | 177 | if (rows == 0U) { |
| 187 | return; | 178 | return; |
| 188 | } | 179 | } |
| 180 | + const uint32_t srcRowBase = | ||
| 181 | + static_cast<uint32_t>(preSumBeforeRankPtr_[static_cast<uint64_t>(srcRank) * expertPerRank_ + groupIdx]); | ||
| 189 | __gm__ int8_t* remotePackedRows = reinterpret_cast<__gm__ int8_t*>( | 182 | __gm__ int8_t* remotePackedRows = reinterpret_cast<__gm__ int8_t*>( |
| 190 | remoteWindow_.RemoteBase(peerMemoryLayout_.offsetA, static_cast<int32_t>(srcRank))); | 183 | remoteWindow_.RemoteBase(peerMemoryLayout_.offsetA, static_cast<int32_t>(srcRank))); |
| 191 | __gm__ int8_t* remoteSrc = remotePackedRows + static_cast<uint64_t>(srcRowBase) * PackedRowStride(); | 184 | __gm__ int8_t* remoteSrc = remotePackedRows + static_cast<uint64_t>(srcRowBase) * PackedRowStride(); |
| @@ -309,32 +302,35 @@ private: | |||
| 309 | 302 | ||
| 310 | AICORE inline void ProcessRankSplitCopy() const | 303 | AICORE inline void ProcessRankSplitCopy() const |
| 311 | { | 304 | { |
| 312 | - SetGmm1InitialReady(); | 305 | + if ASCEND_IS_AIV { |
| 313 | - if (coreIdx_ < rankSize_) { | 306 | + const bool activeCopyCore = rankSize_ != 0U && coreIdx_ < rankSize_; |
| 314 | - uint32_t prevSum = | ||
| 315 | - static_cast<uint32_t>(preSumBeforeRankPtr_[static_cast<uint64_t>(coreIdx_) * expertPerRank_]); | ||
| 316 | uint32_t prevGroupSum = 0U; | 307 | uint32_t prevGroupSum = 0U; |
| 317 | int32_t pingpongIdx = 0; | 308 | int32_t pingpongIdx = 0; |
| 318 | for (uint32_t groupIdx = 0U; groupIdx < expertPerRank_; ++groupIdx) { | 309 | for (uint32_t groupIdx = 0U; groupIdx < expertPerRank_; ++groupIdx) { |
| 319 | - PrepareDispatchGatherCopyEvents(); | ||
| 320 | const uint32_t currentM = static_cast<uint32_t>( | 310 | const uint32_t currentM = static_cast<uint32_t>( |
| 321 | cumsumMMPtr_[static_cast<uint64_t>(rankSize_ - 1U) * expertPerRank_ + groupIdx]); | 311 | cumsumMMPtr_[static_cast<uint64_t>(rankSize_ - 1U) * expertPerRank_ + groupIdx]); |
| 322 | - for (uint32_t srcRank = coreIdx_; srcRank < rankSize_; srcRank += coreNum_) { | 312 | + if (activeCopyCore) { |
| 323 | - FetchRankGroupRows(srcRank, groupIdx, prevGroupSum, prevSum, pingpongIdx); | 313 | + PrepareDispatchGatherCopyEvents(); |
| 314 | + for (uint32_t srcRank = coreIdx_; srcRank < rankSize_; srcRank += coreNum_) { | ||
| 315 | + FetchRankGroupRows(srcRank, groupIdx, prevGroupSum, pingpongIdx); | ||
| 316 | + } | ||
| 317 | + prevGroupSum += currentM; | ||
| 318 | + WaitDispatchGatherCopyEvents(); | ||
| 324 | } | 319 | } |
| 325 | - prevGroupSum += currentM; | 320 | + |
| 326 | - WaitDispatchGatherCopyEvents(); | 321 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData_->fixedGroupTiling; |
| 327 | - pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); | 322 | + const MegaMoeSyncLayout sync = FixedSyncLayout(tilingData_); |
| 328 | - SetGmm1GroupReady(groupIdx); | 323 | + const uint32_t gmm1ConsumerCount = |
| 329 | - } | 324 | + groupIdx < fixed.fullAicGmm1ExpertCount ? fixed.physicalAicNum : fixed.gmm1GroupSize; |
| 330 | - } else { | 325 | + const uint32_t coordinatorLocalId = rankSize_ == 0U ? 0U : rank_ % rankSize_; |
| 331 | - for (uint32_t groupIdx = 0U; groupIdx < expertPerRank_; ++groupIdx) { | 326 | + NotifyGroupConsumersMte( |
| 332 | - pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); | 327 | + workspaceGM_, tilingData_, sync.dispatchArrivalBase, sync.dispatchReadyBase, rankSize_, |
| 333 | - SetGmm1GroupReady(groupIdx); | 328 | + gmm1ConsumerCount, coreIdx_, coordinatorLocalId, groupIdx); |
| 334 | } | 329 | } |
| 335 | } | 330 | } |
| 336 | } | 331 | } |
| 337 | 332 | ||
| 333 | + GM_ADDR workspaceGM_ = nullptr; | ||
| 338 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; | 334 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; |
| 339 | 335 | ||
| 340 | __gm__ int8_t* gmAPtr_ = nullptr; | 336 | __gm__ int8_t* gmAPtr_ = nullptr; |
| @@ -24,6 +24,7 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + | ||
| 27 | 28 | ||
| 28 | template <typename InputElement, uint32_t ExpertPerRank> | 29 | template <typename InputElement, uint32_t ExpertPerRank> |
| 29 | AICORE inline void FrontRunVmsSort(FrontReorderVmsSort<InputElement>& path) | 30 | AICORE inline void FrontRunVmsSort(FrontReorderVmsSort<InputElement>& path) |
| @@ -77,6 +78,9 @@ public: | |||
| 77 | __aicore__ inline void Process(); | 78 | __aicore__ inline void Process(); |
| 78 | 79 | ||
| 79 | private: | 80 | private: |
| 81 | + __aicore__ inline void ProcessFixedGroups(uint16_t stageNum); | ||
| 82 | + __aicore__ inline void ProcessFixedGmm1(uint32_t physicalBlockId); | ||
| 83 | + | ||
| 80 | GM_ADDR xGM_ = nullptr; | 84 | GM_ADDR xGM_ = nullptr; |
| 81 | GM_ADDR weight1GM_ = nullptr; | 85 | GM_ADDR weight1GM_ = nullptr; |
| 82 | GM_ADDR weight2GM_ = nullptr; | 86 | GM_ADDR weight2GM_ = nullptr; |
| @@ -110,35 +114,142 @@ __aicore__ inline void MegaMoe<CType_, ExpertPerRank>::Init( | |||
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | template <typename CType_, uint32_t ExpertPerRank> | 116 | template <typename CType_, uint32_t ExpertPerRank> |
| 113 | -__aicore__ inline void MegaMoe<CType_, ExpertPerRank>::Process() | 117 | +__aicore__ inline void MegaMoe<CType_, ExpertPerRank>::ProcessFixedGmm1(uint32_t physicalBlockId) |
| 114 | { | 118 | { |
| 115 | - using OutputElement = half; | ||
| 116 | - | ||
| 117 | - FrontReorderProcess<CType_, ExpertPerRank>(xGM_, expertIdGM_, expertTokenNumsGM_, workspaceGM_, tilingData_); | ||
| 118 | - | ||
| 119 | - DispatchGather<CType_> dispatchGather; | ||
| 120 | - dispatchGather.Init(expertTokenNumsGM_, workspaceGM_, tilingData_); | ||
| 121 | - dispatchGather.Process(); | ||
| 122 | - | ||
| 123 | Gmm1<CType_> gmm1; | 119 | Gmm1<CType_> gmm1; |
| 124 | gmm1.Init(weight1GM_, scale1GM_, expertTokenNumsGM_, workspaceGM_, tilingData_); | 120 | gmm1.Init(weight1GM_, scale1GM_, expertTokenNumsGM_, workspaceGM_, tilingData_); |
| 125 | - gmm1.Process(); | 121 | + gmm1.ProcessFixed(physicalBlockId, tilingData_->fixedGroupTiling.physicalAicNum); |
| 122 | +} | ||
| 126 | 123 | ||
| 127 | - Swiglu<CType_> swiglu; | 124 | +template <typename CType_, uint32_t ExpertPerRank> |
| 128 | - swiglu.Init(expertTokenNumsGM_, workspaceGM_, tilingData_); | 125 | +__aicore__ inline void MegaMoe<CType_, ExpertPerRank>::ProcessFixedGroups(uint16_t stageNum) |
| 129 | - swiglu.Process(); | 126 | +{ |
| 127 | + const MegaMoeFixedCoreRoleInfo role = FixedCoreRole(tilingData_); | ||
| 128 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData_->fixedGroupTiling; | ||
| 129 | + const bool rankStreaming = tilingData_->unpermuteTiling.unpermuteImplMode == kMegaMoeUnpermuteImplRankStreaming; | ||
| 130 | + const uint32_t rankStreamingWorkerCount = fixed.physicalAivNum; | ||
| 130 | 131 | ||
| 131 | - Gmm2<CType_> gmm2; | 132 | + if (role.role == kMegaMoeFixedRoleDispatch && role.groupLocalId < tilingData_->runtimeInfo.rankSize && |
| 132 | - gmm2.Init(weight2GM_, scale2GM_, expertTokenNumsGM_, workspaceGM_, tilingData_); | 133 | + stageNum >= 9U) { |
| 133 | - gmm2.Process(); | 134 | + DispatchGather<CType_> dispatchGather; |
| 135 | + dispatchGather.Init(expertTokenNumsGM_, workspaceGM_, tilingData_); | ||
| 136 | + dispatchGather.ProcessFixed(role.groupLocalId, role.groupSize); | ||
| 137 | + } else if ((role.role == kMegaMoeFixedRoleGmm1 || role.role == kMegaMoeFixedRoleGmm2) && stageNum >= 10U) { | ||
| 138 | + ProcessFixedGmm1(role.physicalBlockId); | ||
| 139 | + const bool dynamicGmm2Join = stageNum >= 12U; | ||
| 140 | + if (dynamicGmm2Join && role.role == kMegaMoeFixedRoleGmm1 && role.groupLocalId == 0U) { | ||
| 141 | + pipe_barrier(PIPE_ALL); | ||
| 142 | + dsb(DSB_DDR); | ||
| 143 | + PublishScalarEpoch( | ||
| 144 | + FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm1DoneSlot), | ||
| 145 | + kMegaMoeFixedGmm1DoneMarker); | ||
| 146 | + } | ||
| 147 | + if (stageNum >= 12U && (role.role == kMegaMoeFixedRoleGmm2 || dynamicGmm2Join)) { | ||
| 148 | + Gmm2<CType_> gmm2; | ||
| 149 | + gmm2.Init(weight2GM_, scale2GM_, expertTokenNumsGM_, workspaceGM_, tilingData_); | ||
| 150 | + if (role.role == kMegaMoeFixedRoleGmm2) { | ||
| 151 | + gmm2.ProcessFixed(role.groupLocalId, role.groupSize); | ||
| 152 | + } else { | ||
| 153 | + gmm2.ProcessFixedHelper(role.groupLocalId); | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + } else if ( | ||
| 157 | + role.role == kMegaMoeFixedRoleSwiglu && role.groupLocalId < fixed.swigluActiveGroupSize && stageNum >= 11U) { | ||
| 158 | + Swiglu<CType_> swiglu; | ||
| 159 | + swiglu.Init(expertTokenNumsGM_, workspaceGM_, tilingData_); | ||
| 160 | + swiglu.ProcessFixed(role.groupLocalId, fixed.swigluActiveGroupSize); | ||
| 161 | + } else if (role.role == kMegaMoeFixedRoleCombine && role.groupLocalId < role.groupSize && stageNum >= 13U) { | ||
| 162 | + Combine<half> combine; | ||
| 163 | + combine.Init(workspaceGM_, tilingData_); | ||
| 164 | + combine.ProcessFixed(role.groupLocalId, role.groupSize); | ||
| 165 | + } | ||
| 134 | 166 | ||
| 135 | - Combine<OutputElement> combine; | 167 | + if ASCEND_IS_AIV { |
| 136 | - combine.Init(workspaceGM_, tilingData_); | 168 | + if (!rankStreaming && stageNum >= 13U) { |
| 137 | - combine.Process(); | 169 | + Combine<half> finalBoundary; |
| 170 | + finalBoundary.Init(workspaceGM_, tilingData_); | ||
| 171 | + finalBoundary.ProcessFixedFinalBoundary( | ||
| 172 | + role.role, role.flatAivId, role.role != kMegaMoeFixedRoleCombine || role.groupLocalId < role.groupSize); | ||
| 173 | + } | ||
| 174 | + if (rankStreaming && stageNum == 13U && role.physicalBlockId == 0U && role.subblockId == 0U) { | ||
| 175 | + PtoRemoteWindow remoteWindow; | ||
| 176 | + remoteWindow.Init(reinterpret_cast<GM_ADDR>(tilingData_->runtimeInfo.remoteWindowContext)); | ||
| 177 | + const int32_t epoch = remoteWindow.DataReadyEpoch(); | ||
| 178 | + for (uint32_t producerRank = 0U; producerRank < tilingData_->runtimeInfo.rankSize; ++producerRank) { | ||
| 179 | + WaitEpochAcquire(remoteWindow.LocalDataReadySlot(producerRank), epoch); | ||
| 180 | + } | ||
| 181 | + } | ||
| 138 | 182 | ||
| 139 | - Unpermute<OutputElement> unpermute; | 183 | + const uint32_t initialUnpermuteGroupSize = fixed.gmm1GroupSize * 2U; |
| 140 | - unpermute.Init(workspaceGM_, probsGM_, outGM_, tilingData_); | 184 | + const bool initialUnpermuteGroup = role.physicalBlockId < fixed.gmm1GroupSize; |
| 141 | - unpermute.Process(); | 185 | + const uint32_t rankStreamingWorkerIdx = initialUnpermuteGroup ? |
| 186 | + role.physicalBlockId + role.subblockId * fixed.gmm1GroupSize : | ||
| 187 | + initialUnpermuteGroupSize + role.groupLocalId; | ||
| 188 | + const bool unpermuteWorker = !rankStreaming || rankStreamingWorkerIdx < rankStreamingWorkerCount; | ||
| 189 | + if (rankStreaming && stageNum >= 14U && initialUnpermuteGroup) { | ||
| 190 | + PtoRemoteWindow remoteWindow; | ||
| 191 | + remoteWindow.Init(reinterpret_cast<GM_ADDR>(tilingData_->runtimeInfo.remoteWindowContext)); | ||
| 192 | + const int32_t epoch = remoteWindow.DataReadyEpoch(); | ||
| 193 | + if (role.role == kMegaMoeFixedRoleDispatch) { | ||
| 194 | + remoteWindow.PublishDispatchRelease(role.groupLocalId, epoch); | ||
| 195 | + } else if (role.role == kMegaMoeFixedRoleSwiglu) { | ||
| 196 | + remoteWindow.PublishSwigluRelease(role.groupLocalId, epoch); | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + if (rankStreamingWorkerIdx == 0U) { | ||
| 200 | + remoteWindow.WaitDispatchReleaseMte(fixed.dispatchGroupSize, epoch); | ||
| 201 | + remoteWindow.WaitSwigluReleaseMte(fixed.swigluGroupSize, epoch); | ||
| 202 | + | ||
| 203 | + uint32_t readyExpertCounts[COMBINE_EXPERT_PROGRESS_MAX_RANKS] = {0U}; | ||
| 204 | + const uint32_t rankCount = tilingData_->runtimeInfo.rankSize; | ||
| 205 | + const uint32_t expertPerRank = tilingData_->megaMoeInfo.expertPerRank; | ||
| 206 | + const uint32_t configuredCut = fixed.unpermutePhase1ReadyExpertCount; | ||
| 207 | + const uint32_t readyCut = configuredCut < expertPerRank ? configuredCut : expertPerRank; | ||
| 208 | + uint32_t minimumReady = 0U; | ||
| 209 | + while (minimumReady < readyCut) { | ||
| 210 | + minimumReady = remoteWindow.ReadExpertProgressMte(epoch, expertPerRank, readyExpertCounts); | ||
| 211 | + if (minimumReady < readyCut) { | ||
| 212 | + RemoteWindowSyncPollBackoff(); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + remoteWindow.AcquireDataReady(); | ||
| 216 | + uint32_t readyRankMask = 0U; | ||
| 217 | + for (uint32_t producerRank = 0U; producerRank < rankCount; ++producerRank) { | ||
| 218 | + if (readyExpertCounts[producerRank] >= readyCut) { | ||
| 219 | + readyRankMask |= 1U << producerRank; | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + remoteWindow.PublishUnpermutePhase1Progress(readyExpertCounts, rankCount, readyRankMask, epoch); | ||
| 223 | + const uint32_t initialWorkerCount = rankStreamingWorkerCount < initialUnpermuteGroupSize ? | ||
| 224 | + rankStreamingWorkerCount : | ||
| 225 | + initialUnpermuteGroupSize; | ||
| 226 | + remoteWindow.PublishUnpermuteStartRangeMte(0U, initialWorkerCount, epoch); | ||
| 227 | + } | ||
| 228 | + } | ||
| 229 | + if (stageNum >= 14U && unpermuteWorker) { | ||
| 230 | + uint32_t workerIdx = role.flatAivId; | ||
| 231 | + uint32_t workerCount = tilingData_->fixedGroupTiling.physicalAivNum; | ||
| 232 | + if (rankStreaming) { | ||
| 233 | + workerIdx = rankStreamingWorkerIdx; | ||
| 234 | + workerCount = rankStreamingWorkerCount; | ||
| 235 | + PtoRemoteWindow remoteWindow; | ||
| 236 | + remoteWindow.Init(reinterpret_cast<GM_ADDR>(tilingData_->runtimeInfo.remoteWindowContext)); | ||
| 237 | + WaitEpochAcquire(remoteWindow.LocalUnpermuteStartSlot(workerIdx), remoteWindow.DataReadyEpoch()); | ||
| 238 | + } | ||
| 239 | + Unpermute<half> unpermute; | ||
| 240 | + unpermute.Init(workspaceGM_, expertIdGM_, probsGM_, outGM_, tilingData_, workerIdx, workerCount); | ||
| 241 | + unpermute.Process(); | ||
| 242 | + } | ||
| 243 | + } | ||
| 244 | +} | ||
| 245 | + | ||
| 246 | +template <typename CType_, uint32_t ExpertPerRank> | ||
| 247 | +__aicore__ inline void MegaMoe<CType_, ExpertPerRank>::Process() | ||
| 248 | +{ | ||
| 249 | + const uint16_t stageNum = tilingData_->frontReorderTiling.stageNum; | ||
| 250 | + | ||
| 251 | + FrontReorderProcess<CType_, ExpertPerRank>(xGM_, expertIdGM_, expertTokenNumsGM_, workspaceGM_, tilingData_); | ||
| 252 | + ProcessFixedGroups(stageNum); | ||
| 142 | } | 253 | } |
| 143 | 254 | ||
| 144 | 255 | ||
| @@ -114,29 +114,7 @@ struct MegaMoeGmm1Tiling { | |||
| 114 | struct MegaMoeSwigluTiling { | 114 | struct MegaMoeSwigluTiling { |
| 115 | uint64_t gmPermutedTokenOffset = 0; | 115 | uint64_t gmPermutedTokenOffset = 0; |
| 116 | uint64_t perTokenScale2Offset = 0; | 116 | uint64_t perTokenScale2Offset = 0; |
| 117 | - uint64_t swigluSegmentMetaOffset = 0; | 117 | + uint32_t reserved[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 118 | - uint64_t swigluSegmentMetaBytes = 0; | ||
| 119 | - uint32_t reservedSwigluParams[5] = {0, 0, 0, 0, 0}; | ||
| 120 | - uint32_t reserved0[2] = {0, 0}; | ||
| 121 | -}; | ||
| 122 | - | ||
| 123 | -struct MegaMoeSwigluSegmentRuntimeMeta { | ||
| 124 | - uint32_t segmentIdx = 0; | ||
| 125 | - uint32_t segmentStartExpert = 0; | ||
| 126 | - uint32_t segmentEndExpert = 0; | ||
| 127 | - uint32_t segmentRowBase = 0; | ||
| 128 | - uint32_t segmentRows = 0; | ||
| 129 | - uint32_t cumsumRows = 0; | ||
| 130 | - uint32_t expertTokenRows = 0; | ||
| 131 | - uint32_t rowSplitBase = 0; | ||
| 132 | - uint32_t rowSplitRem = 0; | ||
| 133 | - uint32_t valid = 0; | ||
| 134 | - uint32_t generation = 0; | ||
| 135 | - uint32_t producerCoreIdx = 0; | ||
| 136 | - uint32_t metadataMode = 0; | ||
| 137 | - uint32_t segmentNum = 0; | ||
| 138 | - uint32_t epilogueGranularity = 0; | ||
| 139 | - uint32_t marker = 0; | ||
| 140 | }; | 118 | }; |
| 141 | 119 | ||
| 142 | struct MegaMoeGmm2Tiling { | 120 | struct MegaMoeGmm2Tiling { |
| @@ -157,21 +135,104 @@ struct MegaMoeCombineTiling { | |||
| 157 | uint64_t reservedCombineScratchBytes = 0; | 135 | uint64_t reservedCombineScratchBytes = 0; |
| 158 | uint64_t reservedCombineScratchBytesPerAiv = 0; | 136 | uint64_t reservedCombineScratchBytesPerAiv = 0; |
| 159 | uint32_t reservedCombineTileCols = 0; | 137 | uint32_t reservedCombineTileCols = 0; |
| 160 | - uint32_t combineImplMode = 0; | 138 | + uint32_t reserved0 = 0; |
| 161 | }; | 139 | }; |
| 162 | 140 | ||
| 163 | struct MegaMoeUnpermuteTiling { | 141 | struct MegaMoeUnpermuteTiling { |
| 164 | uint32_t unpermuteTileCols = 1024; | 142 | uint32_t unpermuteTileCols = 1024; |
| 165 | uint32_t unpermuteTokenBatch = 256; | 143 | uint32_t unpermuteTokenBatch = 256; |
| 166 | - uint32_t reservedUnpermuteLayoutVersion = 0; | 144 | + uint32_t unpermuteImplMode = 0; |
| 167 | uint32_t reserved0[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | 145 | uint32_t reserved0[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 168 | }; | 146 | }; |
| 169 | 147 | ||
| 148 | +constexpr uint32_t kMegaMoeUnpermuteImplBarrier = 0U; | ||
| 149 | +constexpr uint32_t kMegaMoeUnpermuteImplRankStreaming = 1U; | ||
| 150 | +constexpr uint32_t kMegaMoeFixedLayoutVersion = 11U; | ||
| 151 | +constexpr uint32_t kMegaMoeFixedPhysicalAicNum = 24U; | ||
| 152 | +constexpr uint32_t kMegaMoeFixedPhysicalAivNum = 48U; | ||
| 153 | +constexpr uint32_t kMegaMoeExpertProgressMaxRanks = 16U; | ||
| 154 | +constexpr uint32_t kMegaMoeRankStreamingMaxTokensPerWorker = 256U; | ||
| 155 | +constexpr uint32_t kMegaMoeFixedMaxExperts = 32U; | ||
| 156 | + | ||
| 157 | +constexpr uint32_t kMegaMoeFixedSyncSlotBytes = 64U; | ||
| 158 | +constexpr uint32_t kMegaMoeFixedSyncHeadCanarySlot = 0U; | ||
| 159 | +constexpr uint32_t kMegaMoeFixedSyncBytes = 8704U; | ||
| 160 | +constexpr uint32_t kMegaMoeFixedHeadCanary = 0x13579BDFU; | ||
| 161 | +constexpr uint32_t kMegaMoeFixedTailCanary = 0x2468ACE0U; | ||
| 162 | +constexpr int32_t kMegaMoeFixedGmm1DoneMarker = 1; | ||
| 163 | +constexpr int32_t kMegaMoeFixedGmm2JoinDecisionBit = 0x40000000; | ||
| 164 | +constexpr int32_t kMegaMoeFixedGmm2JoinDecisionMask = kMegaMoeFixedGmm2JoinDecisionBit - 1; | ||
| 165 | + | ||
| 166 | +enum MegaMoeFixedRole : uint32_t { | ||
| 167 | + kMegaMoeFixedRoleGmm1 = 1U, | ||
| 168 | + kMegaMoeFixedRoleGmm2 = 2U, | ||
| 169 | + kMegaMoeFixedRoleSwiglu = 3U, | ||
| 170 | + kMegaMoeFixedRoleCombine = 4U, | ||
| 171 | + kMegaMoeFixedRoleDispatch = 5U, | ||
| 172 | +}; | ||
| 173 | + | ||
| 174 | +struct MegaMoeFixedGroupTiling { | ||
| 175 | + uint64_t syncOffset = 0; | ||
| 176 | + uint64_t syncBytes = 0; | ||
| 177 | + uint32_t layoutVersion = kMegaMoeFixedLayoutVersion; | ||
| 178 | + uint32_t physicalAicNum = 0; | ||
| 179 | + uint32_t physicalAivNum = 0; | ||
| 180 | + uint32_t dispatchGroupSize = 16U; | ||
| 181 | + uint32_t gmm1GroupSize = 16U; | ||
| 182 | + uint32_t gmm2GroupSize = 8U; | ||
| 183 | + uint32_t swigluGroupSize = 16U; | ||
| 184 | + uint32_t swigluActiveGroupSize = 16U; | ||
| 185 | + uint32_t combineGroupSize = 8U; | ||
| 186 | + uint32_t shapeConfigM = 0; | ||
| 187 | + uint32_t fullAicGmm1ExpertCount = 2U; | ||
| 188 | + uint32_t unpermutePhase1ReadyExpertCount = 13U; | ||
| 189 | + uint32_t gmm2JoinCheckStartExpert = 13U; | ||
| 190 | + uint32_t combineStartAfterGmm2Expert = 2U; | ||
| 191 | + uint32_t combineLargeLanesPerRank = 1U; | ||
| 192 | + uint32_t reserved0 = 0; | ||
| 193 | + uint32_t syncSlotBytes = kMegaMoeFixedSyncSlotBytes; | ||
| 194 | + uint32_t maxExperts = kMegaMoeFixedMaxExperts; | ||
| 195 | +}; | ||
| 196 | + | ||
| 197 | +struct MegaMoeSyncLayout { | ||
| 198 | + uint32_t dispatchArrivalBase = 0; | ||
| 199 | + uint32_t dispatchReadyBase = 0; | ||
| 200 | + uint32_t gmm1ArrivalBase = 0; | ||
| 201 | + uint32_t swigluReadyBase = 0; | ||
| 202 | + uint32_t swigluArrivalBase = 0; | ||
| 203 | + uint32_t gmm2ReadyBase = 0; | ||
| 204 | + uint32_t gmm2ArrivalBase = 0; | ||
| 205 | + uint32_t combineReadyBase = 0; | ||
| 206 | + uint32_t gmm1DoneSlot = 0; | ||
| 207 | + uint32_t gmm2JoinSlot = 0; | ||
| 208 | + uint32_t tailCanarySlot = 0; | ||
| 209 | + uint32_t slotCount = 0; | ||
| 210 | +}; | ||
| 211 | + | ||
| 212 | +template <typename FixedT> | ||
| 213 | +inline MegaMoeSyncLayout MakeMegaMoeSyncLayout(const FixedT& fixed) | ||
| 214 | +{ | ||
| 215 | + MegaMoeSyncLayout layout; | ||
| 216 | + layout.dispatchArrivalBase = kMegaMoeFixedSyncHeadCanarySlot + 1U; | ||
| 217 | + layout.dispatchReadyBase = layout.dispatchArrivalBase + fixed.dispatchGroupSize; | ||
| 218 | + layout.gmm1ArrivalBase = layout.dispatchReadyBase + fixed.physicalAicNum; | ||
| 219 | + layout.swigluReadyBase = layout.gmm1ArrivalBase + fixed.gmm1GroupSize; | ||
| 220 | + layout.swigluArrivalBase = layout.swigluReadyBase + fixed.swigluGroupSize; | ||
| 221 | + layout.gmm2ReadyBase = layout.swigluArrivalBase + fixed.swigluGroupSize; | ||
| 222 | + layout.gmm2ArrivalBase = layout.gmm2ReadyBase + fixed.gmm2GroupSize; | ||
| 223 | + layout.combineReadyBase = layout.gmm2ArrivalBase + fixed.physicalAicNum; | ||
| 224 | + layout.gmm1DoneSlot = layout.combineReadyBase + fixed.gmm2GroupSize; | ||
| 225 | + layout.gmm2JoinSlot = layout.gmm1DoneSlot + 1U; | ||
| 226 | + layout.tailCanarySlot = layout.gmm2JoinSlot + 1U; | ||
| 227 | + layout.slotCount = layout.tailCanarySlot + 1U; | ||
| 228 | + return layout; | ||
| 229 | +} | ||
| 230 | + | ||
| 170 | static_assert(sizeof(MegaMoeSwigluTiling) == 64); | 231 | static_assert(sizeof(MegaMoeSwigluTiling) == 64); |
| 171 | -static_assert(sizeof(MegaMoeSwigluSegmentRuntimeMeta) == 64); | ||
| 172 | static_assert(sizeof(MegaMoeGmm2Tiling) == 56); | 232 | static_assert(sizeof(MegaMoeGmm2Tiling) == 56); |
| 173 | static_assert(sizeof(MegaMoeCombineTiling) == 48); | 233 | static_assert(sizeof(MegaMoeCombineTiling) == 48); |
| 174 | static_assert(sizeof(MegaMoeUnpermuteTiling) == 56); | 234 | static_assert(sizeof(MegaMoeUnpermuteTiling) == 56); |
| 235 | +static_assert(sizeof(MegaMoeFixedGroupTiling) == 88); | ||
| 175 | 236 | ||
| 176 | struct MegaMoeTilingData { | 237 | struct MegaMoeTilingData { |
| 177 | MegaMoeInfo megaMoeInfo; | 238 | MegaMoeInfo megaMoeInfo; |
| @@ -183,4 +244,5 @@ struct MegaMoeTilingData { | |||
| 183 | MegaMoeGmm2Tiling gmm2Tiling; | 244 | MegaMoeGmm2Tiling gmm2Tiling; |
| 184 | MegaMoeCombineTiling combineTiling; | 245 | MegaMoeCombineTiling combineTiling; |
| 185 | MegaMoeUnpermuteTiling unpermuteTiling; | 246 | MegaMoeUnpermuteTiling unpermuteTiling; |
| 247 | + MegaMoeFixedGroupTiling fixedGroupTiling; | ||
| 186 | }; | 248 | }; |
| @@ -1011,7 +1011,7 @@ AICORE inline void FrontEndBuildCountExchangeAndPreSum(const FrontReorderCommonS | |||
| 1011 | template <uint32_t Pitch> | 1011 | template <uint32_t Pitch> |
| 1012 | AICORE inline void FrontEndBuildCumsumForPitch(const FrontReorderCommonState& op) | 1012 | AICORE inline void FrontEndBuildCumsumForPitch(const FrontReorderCommonState& op) |
| 1013 | { | 1013 | { |
| 1014 | - static_assert(Pitch == 8U || Pitch == 16U, "front cumsum supports expertPerRank 8 or 16"); | 1014 | + static_assert(Pitch == 8U || Pitch == 16U || Pitch == 32U, "front cumsum supports expertPerRank 8, 16 or 32"); |
| 1015 | using CumsumTile = pto::Tile<pto::TileType::Vec, int32_t, 16, Pitch, pto::BLayout::RowMajor, -1, -1>; | 1015 | using CumsumTile = pto::Tile<pto::TileType::Vec, int32_t, 16, Pitch, pto::BLayout::RowMajor, -1, -1>; |
| 1016 | using CumsumGlobal = pto::GlobalTensor<int32_t, FrontEndShapeDyn, FrontEndStrideDyn, pto::Layout::ND>; | 1016 | using CumsumGlobal = pto::GlobalTensor<int32_t, FrontEndShapeDyn, FrontEndStrideDyn, pto::Layout::ND>; |
| 1017 | const uint32_t localBegin = op.rank_ * Pitch; | 1017 | const uint32_t localBegin = op.rank_ * Pitch; |
| @@ -1050,7 +1050,9 @@ AICORE inline void FrontEndBuildCumsumForPitch(const FrontReorderCommonState& op | |||
| 1050 | template <uint32_t ExpertPerRank> | 1050 | template <uint32_t ExpertPerRank> |
| 1051 | AICORE inline void FrontEndBuildCumsumAndExpertTokenNums(const FrontReorderCommonState& op) | 1051 | AICORE inline void FrontEndBuildCumsumAndExpertTokenNums(const FrontReorderCommonState& op) |
| 1052 | { | 1052 | { |
| 1053 | - static_assert(ExpertPerRank == 8U || ExpertPerRank == 16U, "front cumsum supports expertPerRank 8 or 16"); | 1053 | + static_assert( |
| 1054 | + ExpertPerRank == 8U || ExpertPerRank == 16U || ExpertPerRank == 32U, | ||
| 1055 | + "front cumsum supports expertPerRank 8, 16 or 32"); | ||
| 1054 | if (op.coreIdx_ == 0U) { | 1056 | if (op.coreIdx_ == 0U) { |
| 1055 | FrontEndBuildCumsumForPitch<ExpertPerRank>(op); | 1057 | FrontEndBuildCumsumForPitch<ExpertPerRank>(op); |
| 1056 | } | 1058 | } |
| @@ -17,9 +17,8 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | -#include "utils/pto_sync_substrate.hpp" | 20 | +#include "utils/mega_expert_sync.hpp" |
| 21 | 21 | ||
| 22 | -constexpr uint32_t kGmm1InvalidTask = kGmmCommonInvalidTask; | ||
| 23 | using Gmm1Pipeline = GmmCommonPipeline; | 22 | using Gmm1Pipeline = GmmCommonPipeline; |
| 24 | 23 | ||
| 25 | template <typename InputElement> | 24 | template <typename InputElement> |
| @@ -28,11 +27,23 @@ public: | |||
| 28 | AICORE inline void Init( | 27 | AICORE inline void Init( |
| 29 | GM_ADDR weight1GM, GM_ADDR scale1GM, GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, | 28 | GM_ADDR weight1GM, GM_ADDR scale1GM, GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, |
| 30 | const __gm__ MegaMoeTilingData* tilingData); | 29 | const __gm__ MegaMoeTilingData* tilingData); |
| 31 | - AICORE inline void Process(); | 30 | + AICORE inline void ProcessFixed(uint32_t groupLocalId, uint32_t groupSize); |
| 32 | 31 | ||
| 33 | private: | 32 | private: |
| 34 | - AICORE inline uint32_t CoreLoops(uint32_t currentM) const | 33 | + AICORE inline bool ParticipantBalancedNPartition(uint32_t currentM) const |
| 35 | { | 34 | { |
| 35 | + const uint32_t tileM = tilingData_->gmm1Tiling.l1TileM; | ||
| 36 | + const uint32_t tileN = tilingData_->gmm1Tiling.l1TileN; | ||
| 37 | + const uint32_t defaultLoops = GmmCommonCoreLoops(currentM, problemN_, tileM, tileN); | ||
| 38 | + constexpr uint32_t kNAlign = 32U; | ||
| 39 | + return GmmCommonTileM(currentM, tileM) == 1U && defaultLoops < coreNum_ && problemN_ % kNAlign == 0U && | ||
| 40 | + problemN_ / kNAlign >= coreNum_; | ||
| 41 | + } | ||
| 42 | + AICORE inline uint32_t CoreLoops(uint32_t currentM, bool participantBalanced) const | ||
| 43 | + { | ||
| 44 | + if (participantBalanced) { | ||
| 45 | + return coreNum_; | ||
| 46 | + } | ||
| 36 | return GmmCommonCoreLoops( | 47 | return GmmCommonCoreLoops( |
| 37 | currentM, problemN_, tilingData_->gmm1Tiling.l1TileM, tilingData_->gmm1Tiling.l1TileN); | 48 | currentM, problemN_, tilingData_->gmm1Tiling.l1TileM, tilingData_->gmm1Tiling.l1TileN); |
| 38 | } | 49 | } |
| @@ -42,18 +53,48 @@ private: | |||
| 42 | } | 53 | } |
| 43 | AICORE inline void WaitDispatchGroupReady(uint32_t groupIdx) const | 54 | AICORE inline void WaitDispatchGroupReady(uint32_t groupIdx) const |
| 44 | { | 55 | { |
| 45 | - CrossCoreWaitFlag<0x2>(MegaMoeD2CHardFlagId(groupIdx + 1U)); | 56 | + WaitEpochAcquire( |
| 57 | + FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).dispatchReadyBase + coreIdx_), | ||
| 58 | + static_cast<int32_t>(groupIdx * 2U + 2U)); | ||
| 59 | + } | ||
| 60 | + AICORE inline GmmCommonTileInfo ParticipantBalancedTileInfo(uint32_t currentM, uint32_t loopIdx) const | ||
| 61 | + { | ||
| 62 | + constexpr uint32_t kNAlign = 32U; | ||
| 63 | + const uint32_t nUnits = problemN_ / kNAlign; | ||
| 64 | + const uint32_t baseUnits = nUnits / coreNum_; | ||
| 65 | + const uint32_t wideTileCount = nUnits % coreNum_; | ||
| 66 | + const uint32_t widePrefix = loopIdx < wideTileCount ? loopIdx : wideTileCount; | ||
| 67 | + | ||
| 68 | + GmmCommonTileInfo tileInfo; | ||
| 69 | + tileInfo.tileM = 1U; | ||
| 70 | + tileInfo.tileN = coreNum_; | ||
| 71 | + tileInfo.blockM = 0U; | ||
| 72 | + tileInfo.blockN = loopIdx; | ||
| 73 | + tileInfo.actualM = currentM; | ||
| 74 | + tileInfo.actualN = (baseUnits + (loopIdx < wideTileCount ? 1U : 0U)) * kNAlign; | ||
| 75 | + tileInfo.blockRowStart = 0U; | ||
| 76 | + tileInfo.blockColStart = (loopIdx * baseUnits + widePrefix) * kNAlign; | ||
| 77 | + return tileInfo; | ||
| 46 | } | 78 | } |
| 47 | AICORE inline void RunGmmTile( | 79 | AICORE inline void RunGmmTile( |
| 48 | - Gmm1Pipeline& gmmPipeline, uint32_t groupIdx, uint32_t groupBase, uint32_t currentM, uint32_t loopIdx) const | 80 | + Gmm1Pipeline& gmmPipeline, uint32_t groupIdx, uint32_t groupBase, uint32_t currentM, bool participantBalanced, |
| 81 | + uint32_t loopIdx) const | ||
| 49 | { | 82 | { |
| 83 | + if (participantBalanced) { | ||
| 84 | + const GmmCommonTileInfo tileInfo = ParticipantBalancedTileInfo(currentM, loopIdx); | ||
| 85 | + GmmCommonRunTileInfo( | ||
| 86 | + gmmPipeline, gmAPtr_, weight1Ptr_, gmCPtr_, scale1Ptr_, groupIdx, groupBase, tileInfo, problemN_, | ||
| 87 | + problemK_, problemK_, problemK_, problemN_, problemN_); | ||
| 88 | + return; | ||
| 89 | + } | ||
| 50 | GmmCommonRunTile( | 90 | GmmCommonRunTile( |
| 51 | gmmPipeline, gmAPtr_, weight1Ptr_, gmCPtr_, scale1Ptr_, groupIdx, groupBase, currentM, loopIdx, problemN_, | 91 | gmmPipeline, gmAPtr_, weight1Ptr_, gmCPtr_, scale1Ptr_, groupIdx, groupBase, currentM, loopIdx, problemN_, |
| 52 | problemK_, problemK_, problemK_, problemN_, problemN_, tilingData_->gmm1Tiling.l1TileM, | 92 | problemK_, problemK_, problemK_, problemN_, problemN_, tilingData_->gmm1Tiling.l1TileM, |
| 53 | tilingData_->gmm1Tiling.l1TileN); | 93 | tilingData_->gmm1Tiling.l1TileN); |
| 54 | } | 94 | } |
| 55 | - AICORE inline void SetC2VReady(uint32_t segmentIdx) const; | 95 | + AICORE inline void ProcessImpl(); |
| 56 | 96 | ||
| 97 | + GM_ADDR workspaceGM_ = nullptr; | ||
| 57 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; | 98 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; |
| 58 | 99 | ||
| 59 | __gm__ int8_t* gmAPtr_ = nullptr; | 100 | __gm__ int8_t* gmAPtr_ = nullptr; |
| @@ -77,6 +118,7 @@ AICORE inline void Gmm1<InputElement>::Init( | |||
| 77 | const __gm__ MegaMoeTilingData* tilingData) | 118 | const __gm__ MegaMoeTilingData* tilingData) |
| 78 | { | 119 | { |
| 79 | (void)expertTokenNumsGM; | 120 | (void)expertTokenNumsGM; |
| 121 | + workspaceGM_ = workspaceGM; | ||
| 80 | tilingData_ = tilingData; | 122 | tilingData_ = tilingData; |
| 81 | 123 | ||
| 82 | problemK_ = tilingData_->megaMoeInfo.K; | 124 | problemK_ = tilingData_->megaMoeInfo.K; |
| @@ -93,42 +135,56 @@ AICORE inline void Gmm1<InputElement>::Init( | |||
| 93 | scale1Ptr_ = reinterpret_cast<__gm__ uint64_t*>(scale1GM); | 135 | scale1Ptr_ = reinterpret_cast<__gm__ uint64_t*>(scale1GM); |
| 94 | cumsumMMPtr_ = reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.cumsumMMOffset); | 136 | cumsumMMPtr_ = reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.cumsumMMOffset); |
| 95 | } | 137 | } |
| 138 | + | ||
| 96 | template <typename InputElement> | 139 | template <typename InputElement> |
| 97 | -AICORE inline void Gmm1<InputElement>::SetC2VReady(uint32_t segmentIdx) const | 140 | +AICORE inline void Gmm1<InputElement>::ProcessFixed(uint32_t groupLocalId, uint32_t groupSize) |
| 98 | { | 141 | { |
| 99 | - CrossCoreSetFlag<0x2, PIPE_FIX>(MegaMoeC2VHardFlagId(segmentIdx)); | 142 | + coreIdx_ = groupLocalId; |
| 143 | + coreNum_ = groupSize; | ||
| 144 | + ProcessImpl(); | ||
| 100 | } | 145 | } |
| 101 | 146 | ||
| 102 | template <typename InputElement> | 147 | template <typename InputElement> |
| 103 | -AICORE inline void Gmm1<InputElement>::Process() | 148 | +AICORE inline void Gmm1<InputElement>::ProcessImpl() |
| 104 | { | 149 | { |
| 105 | if ASCEND_IS_AIV { | 150 | if ASCEND_IS_AIV { |
| 106 | return; | 151 | return; |
| 107 | } | 152 | } |
| 108 | - CrossCoreWaitFlag<0x2>(MegaMoeD2CHardFlagId(0U)); | ||
| 109 | Gmm1Pipeline gmmPipeline; | 153 | Gmm1Pipeline gmmPipeline; |
| 110 | uint32_t groupBase = 0; | 154 | uint32_t groupBase = 0; |
| 111 | uint32_t startCoreIdx = 0; | 155 | uint32_t startCoreIdx = 0; |
| 112 | - uint32_t segmentIdx = 0; | ||
| 113 | - const uint32_t firstSegmentEnd = MoeSwigluEpilogueGranularity(expertPerRank_); | ||
| 114 | for (uint32_t groupIdx = 0; groupIdx < expertPerRank_; ++groupIdx) { | 156 | for (uint32_t groupIdx = 0; groupIdx < expertPerRank_; ++groupIdx) { |
| 157 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData_->fixedGroupTiling; | ||
| 158 | + coreNum_ = groupIdx < fixed.fullAicGmm1ExpertCount ? fixed.physicalAicNum : fixed.gmm1GroupSize; | ||
| 159 | + if (coreIdx_ >= coreNum_) { | ||
| 160 | + break; | ||
| 161 | + } | ||
| 162 | + startCoreIdx %= coreNum_; | ||
| 115 | WaitDispatchGroupReady(groupIdx); | 163 | WaitDispatchGroupReady(groupIdx); |
| 116 | const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, groupIdx); | 164 | const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, groupIdx); |
| 117 | const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); | 165 | const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); |
| 118 | - const uint32_t coreLoops = CoreLoops(currentM); // 当前 expert 一共有多少个 GMM tile | 166 | + const bool participantBalanced = ParticipantBalancedNPartition(currentM); |
| 167 | + const uint32_t coreLoops = CoreLoops(currentM, participantBalanced); // 当前 expert 一共有多少个 GMM tile | ||
| 119 | const uint32_t startLoopIdx = StartLoopIdx(startCoreIdx); // 当前 AIC 在这个 expert 里的第一个 tile id | 168 | const uint32_t startLoopIdx = StartLoopIdx(startCoreIdx); // 当前 AIC 在这个 expert 里的第一个 tile id |
| 120 | for (uint32_t loopIdx = startLoopIdx; loopIdx < coreLoops; loopIdx += coreNum_) { | 169 | for (uint32_t loopIdx = startLoopIdx; loopIdx < coreLoops; loopIdx += coreNum_) { |
| 121 | - RunGmmTile(gmmPipeline, groupIdx, groupBase, currentM, loopIdx); | 170 | + RunGmmTile(gmmPipeline, groupIdx, groupBase, currentM, participantBalanced, loopIdx); |
| 122 | } | 171 | } |
| 123 | - const uint32_t groupEnd = groupIdx + 1U; | 172 | + gmmPipeline.SynchronizeBlock(); |
| 124 | - if (groupEnd == firstSegmentEnd || groupEnd == expertPerRank_) { | 173 | + if (groupIdx < fixed.fullAicGmm1ExpertCount) { |
| 125 | - gmmPipeline.SynchronizeBlock(); | 174 | + pto::SYNCALL<pto::SyncCoreType::AICOnly>(); |
| 126 | - SetC2VReady(segmentIdx); | 175 | + if (coreIdx_ == 0U) { |
| 127 | - ++segmentIdx; | 176 | + pipe_barrier(PIPE_ALL); |
| 177 | + dsb(DSB_DDR); | ||
| 178 | + PublishScalarEpochRange( | ||
| 179 | + workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).swigluReadyBase, | ||
| 180 | + fixed.swigluActiveGroupSize, static_cast<int32_t>(groupIdx * 2U + 2U)); | ||
| 181 | + } | ||
| 182 | + } else { | ||
| 183 | + PublishGroupArrival( | ||
| 184 | + workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm1ArrivalBase, coreIdx_, groupIdx); | ||
| 128 | } | 185 | } |
| 129 | groupBase += currentM; | 186 | groupBase += currentM; |
| 130 | - // 从上一个expert分配结束后的下一个 AIC 继续开始下一个exprt,负载均衡 | 187 | + startCoreIdx = GmmCommonNextStartCoreIdx(startCoreIdx, coreNum_, coreLoops); |
| 131 | - startCoreIdx = (startCoreIdx + coreLoops) % coreNum_; | ||
| 132 | } | 188 | } |
| 133 | } | 189 | } |
| 134 | 190 | ||
| @@ -17,9 +17,8 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | -#include "utils/pto_sync_substrate.hpp" | 20 | +#include "utils/mega_expert_sync.hpp" |
| 21 | 21 | ||
| 22 | -constexpr uint32_t kGmm2InvalidTask = kGmmCommonInvalidTask; | ||
| 23 | using Gmm2Pipeline = GmmCommonPipeline; | 22 | using Gmm2Pipeline = GmmCommonPipeline; |
| 24 | 23 | ||
| 25 | template <typename InputElement> | 24 | template <typename InputElement> |
| @@ -28,7 +27,8 @@ public: | |||
| 28 | AICORE inline void Init( | 27 | AICORE inline void Init( |
| 29 | GM_ADDR weight2GM, GM_ADDR scale2GM, GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, | 28 | GM_ADDR weight2GM, GM_ADDR scale2GM, GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, |
| 30 | const __gm__ MegaMoeTilingData* tilingData); | 29 | const __gm__ MegaMoeTilingData* tilingData); |
| 31 | - AICORE inline void Process(); | 30 | + AICORE inline void ProcessFixed(uint32_t groupLocalId, uint32_t groupSize); |
| 31 | + AICORE inline void ProcessFixedHelper(uint32_t groupLocalId); | ||
| 32 | 32 | ||
| 33 | private: | 33 | private: |
| 34 | AICORE inline uint32_t CoreLoops(uint32_t currentM) const | 34 | AICORE inline uint32_t CoreLoops(uint32_t currentM) const |
| @@ -47,7 +47,13 @@ private: | |||
| 47 | loopIdx, outputN_, inputK_, inputK_, inputK_, outputN_, outputN_, tilingData_->gmm2Tiling.l1TileM, | 47 | loopIdx, outputN_, inputK_, inputK_, inputK_, outputN_, outputN_, tilingData_->gmm2Tiling.l1TileM, |
| 48 | tilingData_->gmm2Tiling.l1TileN); | 48 | tilingData_->gmm2Tiling.l1TileN); |
| 49 | } | 49 | } |
| 50 | + AICORE inline uint32_t GroupBaseBefore(uint32_t groupIdx) const; | ||
| 51 | + AICORE inline uint32_t StartCoreBefore(uint32_t groupIdx, uint32_t coreNum) const; | ||
| 52 | + AICORE inline int32_t PrimaryJoinDecision(uint32_t groupIdx) const; | ||
| 53 | + AICORE inline uint32_t HelperJoinExpert() const; | ||
| 54 | + AICORE inline void ProcessImpl(bool helperGroup); | ||
| 50 | 55 | ||
| 56 | + GM_ADDR workspaceGM_ = nullptr; | ||
| 51 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; | 57 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; |
| 52 | 58 | ||
| 53 | __gm__ int8_t* gmPermutedTokenPtr_ = nullptr; | 59 | __gm__ int8_t* gmPermutedTokenPtr_ = nullptr; |
| @@ -55,7 +61,6 @@ private: | |||
| 55 | __gm__ int8_t* weight2Ptr_ = nullptr; | 61 | __gm__ int8_t* weight2Ptr_ = nullptr; |
| 56 | __gm__ uint64_t* scale2Ptr_ = nullptr; | 62 | __gm__ uint64_t* scale2Ptr_ = nullptr; |
| 57 | __gm__ int32_t* cumsumMMPtr_ = nullptr; | 63 | __gm__ int32_t* cumsumMMPtr_ = nullptr; |
| 58 | - __gm__ int32_t* expertTokenNumsPtr_ = nullptr; | ||
| 59 | 64 | ||
| 60 | uint32_t inputK_ = 0; | 65 | uint32_t inputK_ = 0; |
| 61 | uint32_t outputN_ = 0; | 66 | uint32_t outputN_ = 0; |
| @@ -64,6 +69,7 @@ private: | |||
| 64 | uint32_t rankSize_ = 0; | 69 | uint32_t rankSize_ = 0; |
| 65 | uint32_t coreIdx_ = 0; | 70 | uint32_t coreIdx_ = 0; |
| 66 | uint32_t coreNum_ = 1; | 71 | uint32_t coreNum_ = 1; |
| 72 | + uint32_t primaryLocalId_ = 0; | ||
| 67 | }; | 73 | }; |
| 68 | 74 | ||
| 69 | template <typename InputElement> | 75 | template <typename InputElement> |
| @@ -72,6 +78,8 @@ AICORE inline void Gmm2<InputElement>::Init( | |||
| 72 | const __gm__ MegaMoeTilingData* tilingData) | 78 | const __gm__ MegaMoeTilingData* tilingData) |
| 73 | { | 79 | { |
| 74 | (void)sizeof(InputElement); | 80 | (void)sizeof(InputElement); |
| 81 | + (void)expertTokenNumsGM; | ||
| 82 | + workspaceGM_ = workspaceGM; | ||
| 75 | tilingData_ = tilingData; | 83 | tilingData_ = tilingData; |
| 76 | 84 | ||
| 77 | const uint32_t problemN = tilingData_->megaMoeInfo.N; | 85 | const uint32_t problemN = tilingData_->megaMoeInfo.N; |
| @@ -90,46 +98,135 @@ AICORE inline void Gmm2<InputElement>::Init( | |||
| 90 | weight2Ptr_ = reinterpret_cast<__gm__ int8_t*>(weight2GM); | 98 | weight2Ptr_ = reinterpret_cast<__gm__ int8_t*>(weight2GM); |
| 91 | scale2Ptr_ = reinterpret_cast<__gm__ uint64_t*>(scale2GM); | 99 | scale2Ptr_ = reinterpret_cast<__gm__ uint64_t*>(scale2GM); |
| 92 | cumsumMMPtr_ = reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.cumsumMMOffset); | 100 | cumsumMMPtr_ = reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.cumsumMMOffset); |
| 93 | - expertTokenNumsPtr_ = reinterpret_cast<__gm__ int32_t*>(expertTokenNumsGM); | ||
| 94 | } | 101 | } |
| 95 | 102 | ||
| 96 | template <typename InputElement> | 103 | template <typename InputElement> |
| 97 | -AICORE inline void Gmm2<InputElement>::Process() | 104 | +AICORE inline void Gmm2<InputElement>::ProcessFixed(uint32_t groupLocalId, uint32_t groupSize) |
| 105 | +{ | ||
| 106 | + coreIdx_ = groupLocalId; | ||
| 107 | + coreNum_ = groupSize; | ||
| 108 | + primaryLocalId_ = groupLocalId; | ||
| 109 | + ProcessImpl(false); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +template <typename InputElement> | ||
| 113 | +AICORE inline void Gmm2<InputElement>::ProcessFixedHelper(uint32_t groupLocalId) | ||
| 114 | +{ | ||
| 115 | + coreIdx_ = groupLocalId; | ||
| 116 | + coreNum_ = tilingData_->fixedGroupTiling.physicalAicNum; | ||
| 117 | + primaryLocalId_ = groupLocalId; | ||
| 118 | + ProcessImpl(true); | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +template <typename InputElement> | ||
| 122 | +AICORE inline uint32_t Gmm2<InputElement>::GroupBaseBefore(uint32_t groupIdx) const | ||
| 123 | +{ | ||
| 124 | + uint32_t groupBase = 0U; | ||
| 125 | + for (uint32_t expert = 0U; expert < groupIdx; ++expert) { | ||
| 126 | + const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, expert); | ||
| 127 | + groupBase += MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); | ||
| 128 | + } | ||
| 129 | + return groupBase; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +template <typename InputElement> | ||
| 133 | +AICORE inline uint32_t Gmm2<InputElement>::StartCoreBefore(uint32_t groupIdx, uint32_t coreNum) const | ||
| 134 | +{ | ||
| 135 | + uint32_t groupBase = 0U; | ||
| 136 | + uint32_t startCoreIdx = 0U; | ||
| 137 | + for (uint32_t expert = 0U; expert < groupIdx; ++expert) { | ||
| 138 | + const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, expert); | ||
| 139 | + const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); | ||
| 140 | + groupBase += currentM; | ||
| 141 | + startCoreIdx = GmmCommonNextStartCoreIdx(startCoreIdx, coreNum, CoreLoops(currentM)); | ||
| 142 | + } | ||
| 143 | + return startCoreIdx; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +template <typename InputElement> | ||
| 147 | +AICORE inline int32_t Gmm2<InputElement>::PrimaryJoinDecision(uint32_t groupIdx) const | ||
| 148 | +{ | ||
| 149 | + volatile __gm__ int32_t* joinSlot = | ||
| 150 | + FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm2JoinSlot); | ||
| 151 | + const int32_t expectedDecision = static_cast<int32_t>(groupIdx + 1U); | ||
| 152 | + if (primaryLocalId_ != 0U) { | ||
| 153 | + return WaitEpochAcquire(joinSlot, expectedDecision); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + const int32_t done = | ||
| 157 | + ReadScalarEpoch(FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm1DoneSlot)); | ||
| 158 | + const int32_t decision = | ||
| 159 | + done >= kMegaMoeFixedGmm1DoneMarker ? (kMegaMoeFixedGmm2JoinDecisionBit | expectedDecision) : expectedDecision; | ||
| 160 | + PublishScalarEpoch(joinSlot, decision); | ||
| 161 | + return decision; | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +template <typename InputElement> | ||
| 165 | +AICORE inline uint32_t Gmm2<InputElement>::HelperJoinExpert() const | ||
| 166 | +{ | ||
| 167 | + const int32_t decision = WaitEpochAcquire( | ||
| 168 | + FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm2JoinSlot), | ||
| 169 | + kMegaMoeFixedGmm2JoinDecisionBit); | ||
| 170 | + const int32_t encodedExpert = decision & kMegaMoeFixedGmm2JoinDecisionMask; | ||
| 171 | + return encodedExpert > 0 ? static_cast<uint32_t>(encodedExpert - 1) : expertPerRank_; | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +template <typename InputElement> | ||
| 175 | +AICORE inline void Gmm2<InputElement>::ProcessImpl(bool helperGroup) | ||
| 98 | { | 176 | { |
| 99 | if ASCEND_IS_AIV { | 177 | if ASCEND_IS_AIV { |
| 100 | return; | 178 | return; |
| 101 | } | 179 | } |
| 102 | - | ||
| 103 | Gmm2Pipeline gmmPipeline; | 180 | Gmm2Pipeline gmmPipeline; |
| 104 | uint32_t groupBase = 0; | 181 | uint32_t groupBase = 0; |
| 105 | uint32_t startCoreIdx = 0; | 182 | uint32_t startCoreIdx = 0; |
| 106 | - const uint32_t segmentNum = MoeSwigluSegmentNum(expertPerRank_); | 183 | + uint32_t firstGroupIdx = 0U; |
| 107 | - for (uint32_t segmentIdx = 0; segmentIdx < segmentNum; ++segmentIdx) { | 184 | + bool joinedGroup = helperGroup; |
| 108 | - CrossCoreWaitFlag<0x2>(MegaMoeV2CHardFlagId(segmentIdx)); | 185 | + if (helperGroup) { |
| 109 | - | 186 | + firstGroupIdx = HelperJoinExpert(); |
| 110 | - uint32_t segmentStartExpert = 0; | 187 | + if (firstGroupIdx >= expertPerRank_) { |
| 111 | - uint32_t segmentEndExpert = 0; | 188 | + return; |
| 112 | - uint32_t segmentRowBase = 0; | ||
| 113 | - uint32_t segmentRows = 0; | ||
| 114 | - uint32_t cumsumRows = 0; | ||
| 115 | - uint32_t expertTokenRows = 0; | ||
| 116 | - MoeBuildSegmentMetadata( | ||
| 117 | - segmentIdx, expertPerRank_, maxOutputSize_, cumsumMMPtr_, expertTokenNumsPtr_, rankSize_, | ||
| 118 | - segmentStartExpert, segmentEndExpert, segmentRowBase, segmentRows, cumsumRows, expertTokenRows); | ||
| 119 | - | ||
| 120 | - for (uint32_t groupIdx = segmentStartExpert; groupIdx < segmentEndExpert; ++groupIdx) { | ||
| 121 | - const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, groupIdx); | ||
| 122 | - const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); | ||
| 123 | - const uint32_t coreLoops = CoreLoops(currentM); | ||
| 124 | - const uint32_t startLoopIdx = StartLoopIdx(startCoreIdx); | ||
| 125 | - for (uint32_t loopIdx = startLoopIdx; loopIdx < coreLoops; loopIdx += coreNum_) { | ||
| 126 | - RunGmmTile(gmmPipeline, groupIdx, groupBase, currentM, loopIdx); | ||
| 127 | - } | ||
| 128 | - gmmPipeline.SynchronizeBlock(); | ||
| 129 | - gmmPipeline.Finalize(static_cast<int32_t>(groupIdx), MEGA_MOE_GMM2_TO_COMBINE_HARD_FLAG_BASE); | ||
| 130 | - groupBase += currentM; | ||
| 131 | - startCoreIdx = (startCoreIdx + coreLoops) % coreNum_; | ||
| 132 | } | 189 | } |
| 190 | + groupBase = GroupBaseBefore(firstGroupIdx); | ||
| 191 | + startCoreIdx = StartCoreBefore(firstGroupIdx, tilingData_->fixedGroupTiling.gmm2GroupSize); | ||
| 192 | + } | ||
| 193 | + for (uint32_t groupIdx = firstGroupIdx; groupIdx < expertPerRank_; ++groupIdx) { | ||
| 194 | + const MegaMoeSyncLayout sync = FixedSyncLayout(tilingData_); | ||
| 195 | + const uint32_t readyLocalId = coreIdx_ % tilingData_->fixedGroupTiling.gmm2GroupSize; | ||
| 196 | + WaitEpochAcquire( | ||
| 197 | + FixedSyncSlot(workspaceGM_, tilingData_, sync.gmm2ReadyBase + readyLocalId), | ||
| 198 | + static_cast<int32_t>(groupIdx * 2U + 2U)); | ||
| 199 | + if (!helperGroup && !joinedGroup && groupIdx >= tilingData_->fixedGroupTiling.gmm2JoinCheckStartExpert) { | ||
| 200 | + const int32_t decision = PrimaryJoinDecision(groupIdx); | ||
| 201 | + const uint32_t encodedJoinExpert = static_cast<uint32_t>(decision & kMegaMoeFixedGmm2JoinDecisionMask); | ||
| 202 | + const bool joinThisExpert = (decision & kMegaMoeFixedGmm2JoinDecisionBit) != 0 && encodedJoinExpert != 0U && | ||
| 203 | + groupIdx + 1U >= encodedJoinExpert; | ||
| 204 | + if (joinThisExpert) { | ||
| 205 | + joinedGroup = true; | ||
| 206 | + coreIdx_ = tilingData_->fixedGroupTiling.gmm1GroupSize + primaryLocalId_; | ||
| 207 | + coreNum_ = tilingData_->fixedGroupTiling.physicalAicNum; | ||
| 208 | + } | ||
| 209 | + } | ||
| 210 | + const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, groupIdx); | ||
| 211 | + const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); | ||
| 212 | + const uint32_t coreLoops = CoreLoops(currentM); | ||
| 213 | + const uint32_t startLoopIdx = StartLoopIdx(startCoreIdx); | ||
| 214 | + for (uint32_t loopIdx = startLoopIdx; loopIdx < coreLoops; loopIdx += coreNum_) { | ||
| 215 | + RunGmmTile(gmmPipeline, groupIdx, groupBase, currentM, loopIdx); | ||
| 216 | + } | ||
| 217 | + gmmPipeline.SynchronizeBlock(); | ||
| 218 | + if (tilingData_->frontReorderTiling.stageNum >= 13U) { | ||
| 219 | + const uint32_t arrivalLocalId = | ||
| 220 | + helperGroup ? primaryLocalId_ : tilingData_->fixedGroupTiling.gmm1GroupSize + primaryLocalId_; | ||
| 221 | + PublishGroupArrival(workspaceGM_, tilingData_, sync.gmm2ArrivalBase, arrivalLocalId, groupIdx); | ||
| 222 | + } | ||
| 223 | + groupBase += currentM; | ||
| 224 | + startCoreIdx = GmmCommonNextStartCoreIdx(startCoreIdx, coreNum_, coreLoops); | ||
| 225 | + } | ||
| 226 | + if (!helperGroup && !joinedGroup && primaryLocalId_ == 0U) { | ||
| 227 | + const int32_t sentinel = kMegaMoeFixedGmm2JoinDecisionBit + static_cast<int32_t>(expertPerRank_ + 1U); | ||
| 228 | + PublishScalarEpoch( | ||
| 229 | + FixedSyncSlot(workspaceGM_, tilingData_, FixedSyncLayout(tilingData_).gmm2JoinSlot), sentinel); | ||
| 133 | } | 230 | } |
| 134 | } | 231 | } |
| 135 | 232 | ||
| @@ -17,7 +17,6 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | -constexpr uint32_t kGmmCommonInvalidTask = 0xFFFFFFFFU; | ||
| 21 | constexpr uint32_t kGmmCommonSwizzleOffset = 9U; | 20 | constexpr uint32_t kGmmCommonSwizzleOffset = 9U; |
| 22 | 21 | ||
| 23 | using GmmCommonPipeline = | 22 | using GmmCommonPipeline = |
| @@ -54,16 +53,36 @@ AICORE inline uint32_t GmmCommonStartLoopIdx(uint32_t coreIdx, uint32_t coreNum, | |||
| 54 | return ((coreIdx < startCoreIdx) ? (coreIdx + coreNum) : coreIdx) - startCoreIdx; | 53 | return ((coreIdx < startCoreIdx) ? (coreIdx + coreNum) : coreIdx) - startCoreIdx; |
| 55 | } | 54 | } |
| 56 | 55 | ||
| 56 | +AICORE inline uint32_t GmmCommonNextStartCoreIdx(uint32_t startCoreIdx, uint32_t coreNum, uint32_t coreLoops) | ||
| 57 | +{ | ||
| 58 | + if (coreNum > 1U && coreLoops != 0U) { | ||
| 59 | + const uint32_t remainder = coreLoops % coreNum; | ||
| 60 | + const uint32_t advance = remainder == 0U ? 1U : remainder; | ||
| 61 | + return (startCoreIdx + advance) % coreNum; | ||
| 62 | + } | ||
| 63 | + return coreNum <= 1U ? 0U : startCoreIdx; | ||
| 64 | +} | ||
| 65 | + | ||
| 57 | AICORE inline void GmmCommonGetBlockCoordMN( | 66 | AICORE inline void GmmCommonGetBlockCoordMN( |
| 58 | uint32_t loopIdx, uint32_t tileM, uint32_t tileN, uint32_t& blockM, uint32_t& blockN) | 67 | uint32_t loopIdx, uint32_t tileM, uint32_t tileN, uint32_t& blockM, uint32_t& blockN) |
| 59 | { | 68 | { |
| 69 | + blockM = 0U; | ||
| 70 | + blockN = 0U; | ||
| 71 | + if (tileM == 0U || tileN == 0U) { | ||
| 72 | + return; | ||
| 73 | + } | ||
| 74 | + const uint32_t swizzleSpan = kGmmCommonSwizzleOffset * tileM; | ||
| 60 | const uint32_t tileBlockLoop = static_cast<uint32_t>(ceilDiv(tileN, kGmmCommonSwizzleOffset)); | 75 | const uint32_t tileBlockLoop = static_cast<uint32_t>(ceilDiv(tileN, kGmmCommonSwizzleOffset)); |
| 61 | - const uint32_t tileBlockIdx = loopIdx / (kGmmCommonSwizzleOffset * tileM); | 76 | + const uint32_t tileBlockIdx = loopIdx / swizzleSpan; |
| 62 | - const uint32_t inTileBlockIdx = loopIdx % (kGmmCommonSwizzleOffset * tileM); | 77 | + const uint32_t inTileBlockIdx = loopIdx % swizzleSpan; |
| 63 | uint32_t nCol = kGmmCommonSwizzleOffset; | 78 | uint32_t nCol = kGmmCommonSwizzleOffset; |
| 64 | if (tileBlockIdx + 1U == tileBlockLoop) { | 79 | if (tileBlockIdx + 1U == tileBlockLoop) { |
| 65 | nCol = tileN - kGmmCommonSwizzleOffset * tileBlockIdx; | 80 | nCol = tileN - kGmmCommonSwizzleOffset * tileBlockIdx; |
| 66 | } | 81 | } |
| 82 | + if (nCol == 0U) { | ||
| 83 | + blockN = tileBlockIdx * kGmmCommonSwizzleOffset; | ||
| 84 | + return; | ||
| 85 | + } | ||
| 67 | blockM = inTileBlockIdx / nCol; | 86 | blockM = inTileBlockIdx / nCol; |
| 68 | blockN = tileBlockIdx * kGmmCommonSwizzleOffset + inTileBlockIdx % nCol; | 87 | blockN = tileBlockIdx * kGmmCommonSwizzleOffset + inTileBlockIdx % nCol; |
| 69 | if ((tileBlockIdx & 1U) != 0U) { | 88 | if ((tileBlockIdx & 1U) != 0U) { |
| @@ -107,11 +126,6 @@ AICORE inline uint32_t MoeCurrentMRaw( | |||
| 107 | return static_cast<uint32_t>(cumsumMMPtr[static_cast<uint64_t>(rankSize - 1U) * expertPerRank + groupIdx]); | 126 | return static_cast<uint32_t>(cumsumMMPtr[static_cast<uint64_t>(rankSize - 1U) * expertPerRank + groupIdx]); |
| 108 | } | 127 | } |
| 109 | 128 | ||
| 110 | -AICORE inline uint32_t MoeExpertTokenNums(__gm__ int32_t* expertTokenNumsPtr, uint32_t groupIdx) | ||
| 111 | -{ | ||
| 112 | - return static_cast<uint32_t>(expertTokenNumsPtr[groupIdx]); | ||
| 113 | -} | ||
| 114 | - | ||
| 115 | AICORE inline uint32_t MoeClipCurrentM(uint32_t currentMRaw, uint32_t groupBase, uint32_t maxOutputSize) | 129 | AICORE inline uint32_t MoeClipCurrentM(uint32_t currentMRaw, uint32_t groupBase, uint32_t maxOutputSize) |
| 116 | { | 130 | { |
| 117 | if (groupBase >= maxOutputSize) { | 131 | if (groupBase >= maxOutputSize) { |
| @@ -121,42 +135,12 @@ AICORE inline uint32_t MoeClipCurrentM(uint32_t currentMRaw, uint32_t groupBase, | |||
| 121 | return currentMRaw > remaining ? remaining : currentMRaw; | 135 | return currentMRaw > remaining ? remaining : currentMRaw; |
| 122 | } | 136 | } |
| 123 | 137 | ||
| 124 | -AICORE inline void MoeBuildSegmentMetadata( | 138 | +AICORE inline void GmmCommonRunTileInfo( |
| 125 | - uint32_t segmentIdx, uint32_t expertPerRank, uint32_t maxOutputSize, __gm__ int32_t* cumsumMMPtr, | ||
| 126 | - __gm__ int32_t* expertTokenNumsPtr, uint32_t rankSize, uint32_t& segmentStartExpert, uint32_t& segmentEndExpert, | ||
| 127 | - uint32_t& segmentRowBase, uint32_t& segmentRows, uint32_t& cumsumRows, uint32_t& expertTokenRows) | ||
| 128 | -{ | ||
| 129 | - segmentStartExpert = MoeSwigluSegmentStartExpert(expertPerRank, segmentIdx); | ||
| 130 | - segmentEndExpert = MoeSwigluSegmentEndExpert(expertPerRank, segmentIdx); | ||
| 131 | - segmentRowBase = 0U; | ||
| 132 | - segmentRows = 0U; | ||
| 133 | - cumsumRows = 0U; | ||
| 134 | - expertTokenRows = 0U; | ||
| 135 | - | ||
| 136 | - uint32_t groupBase = 0U; | ||
| 137 | - for (uint32_t groupIdx = 0U; groupIdx < segmentEndExpert; ++groupIdx) { | ||
| 138 | - const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr, rankSize, expertPerRank, groupIdx); | ||
| 139 | - const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize); | ||
| 140 | - if (groupIdx == segmentStartExpert) { | ||
| 141 | - segmentRowBase = groupBase; | ||
| 142 | - } | ||
| 143 | - if (groupIdx >= segmentStartExpert) { | ||
| 144 | - segmentRows += currentM; | ||
| 145 | - cumsumRows += currentMRaw; | ||
| 146 | - expertTokenRows += MoeExpertTokenNums(expertTokenNumsPtr, groupIdx); | ||
| 147 | - } | ||
| 148 | - groupBase += currentM; | ||
| 149 | - } | ||
| 150 | -} | ||
| 151 | - | ||
| 152 | -AICORE inline void GmmCommonRunTile( | ||
| 153 | GmmCommonPipeline& gmmPipeline, __gm__ int8_t* gmAPtr, __gm__ int8_t* gmWeightPtr, __gm__ half* gmCPtr, | 139 | GmmCommonPipeline& gmmPipeline, __gm__ int8_t* gmAPtr, __gm__ int8_t* gmWeightPtr, __gm__ half* gmCPtr, |
| 154 | - __gm__ uint64_t* gmScalePtr, uint32_t groupIdx, uint32_t groupBase, uint32_t currentM, uint32_t loopIdx, | 140 | + __gm__ uint64_t* gmScalePtr, uint32_t groupIdx, uint32_t groupBase, const GmmCommonTileInfo& tileInfo, |
| 155 | uint32_t problemN, uint32_t actualK, uint32_t aLeadingDim, uint32_t bFullRows, uint32_t cLeadingDim, | 141 | uint32_t problemN, uint32_t actualK, uint32_t aLeadingDim, uint32_t bFullRows, uint32_t cLeadingDim, |
| 156 | - uint32_t scaleGroupStride, uint32_t l1TileM, uint32_t l1TileN) | 142 | + uint32_t scaleGroupStride) |
| 157 | { | 143 | { |
| 158 | - const GmmCommonTileInfo tileInfo = GmmCommonBuildTileInfo(currentM, problemN, l1TileM, l1TileN, loopIdx); | ||
| 159 | - | ||
| 160 | const uint64_t gmOffsetA = static_cast<uint64_t>(groupBase + tileInfo.blockRowStart) * aLeadingDim; | 144 | const uint64_t gmOffsetA = static_cast<uint64_t>(groupBase + tileInfo.blockRowStart) * aLeadingDim; |
| 161 | const uint64_t gmOffsetB = static_cast<uint64_t>(groupIdx) * GmmCommonPackedWeightExpertStride(bFullRows, problemN); | 145 | const uint64_t gmOffsetB = static_cast<uint64_t>(groupIdx) * GmmCommonPackedWeightExpertStride(bFullRows, problemN); |
| 162 | const uint64_t gmOffsetC = static_cast<uint64_t>(groupBase + tileInfo.blockRowStart) * cLeadingDim + | 146 | const uint64_t gmOffsetC = static_cast<uint64_t>(groupBase + tileInfo.blockRowStart) * cLeadingDim + |
| @@ -178,4 +162,16 @@ AICORE inline void GmmCommonRunTile( | |||
| 178 | gmmPipeline.RunTile(blockParams); | 162 | gmmPipeline.RunTile(blockParams); |
| 179 | } | 163 | } |
| 180 | 164 | ||
| 165 | +AICORE inline void GmmCommonRunTile( | ||
| 166 | + GmmCommonPipeline& gmmPipeline, __gm__ int8_t* gmAPtr, __gm__ int8_t* gmWeightPtr, __gm__ half* gmCPtr, | ||
| 167 | + __gm__ uint64_t* gmScalePtr, uint32_t groupIdx, uint32_t groupBase, uint32_t currentM, uint32_t loopIdx, | ||
| 168 | + uint32_t problemN, uint32_t actualK, uint32_t aLeadingDim, uint32_t bFullRows, uint32_t cLeadingDim, | ||
| 169 | + uint32_t scaleGroupStride, uint32_t l1TileM, uint32_t l1TileN) | ||
| 170 | +{ | ||
| 171 | + const GmmCommonTileInfo tileInfo = GmmCommonBuildTileInfo(currentM, problemN, l1TileM, l1TileN, loopIdx); | ||
| 172 | + GmmCommonRunTileInfo( | ||
| 173 | + gmmPipeline, gmAPtr, gmWeightPtr, gmCPtr, gmScalePtr, groupIdx, groupBase, tileInfo, problemN, actualK, | ||
| 174 | + aLeadingDim, bFullRows, cLeadingDim, scaleGroupStride); | ||
| 175 | +} | ||
| 176 | + | ||
| 181 | 177 | ||
| @@ -19,12 +19,9 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | + | ||
| 22 | 23 | ||
| 23 | - | ||
| 24 | 24 | ||
| 25 | -constexpr uint32_t kSwigluWaitSourceC2VOnly = 1U; | ||
| 26 | -constexpr uint32_t kSwigluPipelineModeInputOutputSplit = 1U; | ||
| 27 | -constexpr uint32_t kSwigluMetadataModeSharedSegmentMeta = 1U; | ||
| 28 | constexpr uint32_t kSwigluVecTileElems = 1024U; | 25 | constexpr uint32_t kSwigluVecTileElems = 1024U; |
| 29 | constexpr uint32_t kSwigluFullRowIoBlockChunks = 4U; | 26 | constexpr uint32_t kSwigluFullRowIoBlockChunks = 4U; |
| 30 | constexpr uint32_t kSwigluUbStageNum = 2U; | 27 | constexpr uint32_t kSwigluUbStageNum = 2U; |
| @@ -37,19 +34,9 @@ template <typename InputElement> | |||
| 37 | class Swiglu { | 34 | class Swiglu { |
| 38 | public: | 35 | public: |
| 39 | AICORE inline void Init(GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData); | 36 | AICORE inline void Init(GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData); |
| 40 | - AICORE inline void Process(); | 37 | + AICORE inline void ProcessFixed(uint32_t groupLocalId, uint32_t groupSize); |
| 41 | 38 | ||
| 42 | private: | 39 | private: |
| 43 | - AICORE inline __gm__ MegaMoeSwigluSegmentRuntimeMeta* SegmentMetaPtr() const | ||
| 44 | - { | ||
| 45 | - return reinterpret_cast<__gm__ MegaMoeSwigluSegmentRuntimeMeta*>( | ||
| 46 | - workspaceGM_ + tilingData_->swigluTiling.swigluSegmentMetaOffset); | ||
| 47 | - } | ||
| 48 | - AICORE inline void WriteSharedSegmentMetadata(uint32_t segmentIdx) const; | ||
| 49 | - AICORE inline void ReadSharedSegmentMetadata( | ||
| 50 | - uint32_t segmentIdx, uint32_t& segmentStartExpert, uint32_t& segmentEndExpert, uint32_t& segmentRowBase, | ||
| 51 | - uint32_t& segmentRows, uint32_t& cumsumRows, uint32_t& expertTokenRows, uint32_t& rowSplitBase, | ||
| 52 | - uint32_t& rowSplitRem) const; | ||
| 53 | AICORE inline uint64_t AlignUbBytes(uint64_t value) const { return (value + 31U) / 32U * 32U; } | 40 | AICORE inline uint64_t AlignUbBytes(uint64_t value) const { return (value + 31U) / 32U * 32U; } |
| 54 | AICORE inline uint64_t SwigluMaxScratchBytes() const | 41 | AICORE inline uint64_t SwigluMaxScratchBytes() const |
| 55 | { | 42 | { |
| @@ -118,6 +105,7 @@ private: | |||
| 118 | AICORE inline void StoreFullRowOutput(uint32_t rowIdx, uint32_t bufferId) const; | 105 | AICORE inline void StoreFullRowOutput(uint32_t rowIdx, uint32_t bufferId) const; |
| 119 | AICORE inline float ComputeAndStorePreparedFullRow(uint32_t rowIdx, uint32_t bufferId, float perTokenScale) const; | 106 | AICORE inline float ComputeAndStorePreparedFullRow(uint32_t rowIdx, uint32_t bufferId, float perTokenScale) const; |
| 120 | AICORE inline void IssueStoreScale2Chunk(uint32_t rowStart, uint32_t rowCount, uint32_t scaleBufferId) const; | 107 | AICORE inline void IssueStoreScale2Chunk(uint32_t rowStart, uint32_t rowCount, uint32_t scaleBufferId) const; |
| 108 | + AICORE inline void ProcessImpl(); | ||
| 121 | 109 | ||
| 122 | GM_ADDR workspaceGM_ = nullptr; | 110 | GM_ADDR workspaceGM_ = nullptr; |
| 123 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; | 111 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; |
| @@ -126,7 +114,6 @@ private: | |||
| 126 | __gm__ int8_t* gmPermutedTokenPtr_ = nullptr; | 114 | __gm__ int8_t* gmPermutedTokenPtr_ = nullptr; |
| 127 | __gm__ float* perTokenScale2Ptr_ = nullptr; | 115 | __gm__ float* perTokenScale2Ptr_ = nullptr; |
| 128 | __gm__ int32_t* cumsumMMPtr_ = nullptr; | 116 | __gm__ int32_t* cumsumMMPtr_ = nullptr; |
| 129 | - __gm__ int32_t* expertTokenNumsPtr_ = nullptr; | ||
| 130 | uint32_t problemN_ = 0; | 117 | uint32_t problemN_ = 0; |
| 131 | uint32_t outputN_ = 0; | 118 | uint32_t outputN_ = 0; |
| 132 | uint32_t maxOutputSize_ = 0; | 119 | uint32_t maxOutputSize_ = 0; |
| @@ -134,7 +121,6 @@ private: | |||
| 134 | uint32_t rankSize_ = 0; | 121 | uint32_t rankSize_ = 0; |
| 135 | uint32_t coreIdx_ = 0; | 122 | uint32_t coreIdx_ = 0; |
| 136 | uint32_t coreNum_ = 1; | 123 | uint32_t coreNum_ = 1; |
| 137 | - uint32_t stageNum_ = 0; | ||
| 138 | }; | 124 | }; |
| 139 | 125 | ||
| 140 | template <typename InputElement> | 126 | template <typename InputElement> |
| @@ -142,6 +128,7 @@ AICORE inline void Swiglu<InputElement>::Init( | |||
| 142 | GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) | 128 | GM_ADDR expertTokenNumsGM, GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) |
| 143 | { | 129 | { |
| 144 | (void)sizeof(InputElement); | 130 | (void)sizeof(InputElement); |
| 131 | + (void)expertTokenNumsGM; | ||
| 145 | workspaceGM_ = workspaceGM; | 132 | workspaceGM_ = workspaceGM; |
| 146 | tilingData_ = tilingData; | 133 | tilingData_ = tilingData; |
| 147 | problemN_ = tilingData_->megaMoeInfo.N; | 134 | problemN_ = tilingData_->megaMoeInfo.N; |
| @@ -149,7 +136,6 @@ AICORE inline void Swiglu<InputElement>::Init( | |||
| 149 | maxOutputSize_ = tilingData_->megaMoeInfo.maxOutputSize; | 136 | maxOutputSize_ = tilingData_->megaMoeInfo.maxOutputSize; |
| 150 | expertPerRank_ = tilingData_->megaMoeInfo.expertPerRank; | 137 | expertPerRank_ = tilingData_->megaMoeInfo.expertPerRank; |
| 151 | rankSize_ = tilingData_->runtimeInfo.rankSize; | 138 | rankSize_ = tilingData_->runtimeInfo.rankSize; |
| 152 | - stageNum_ = tilingData_->frontReorderTiling.stageNum; | ||
| 153 | 139 | ||
| 154 | coreIdx_ = get_block_idx(); | 140 | coreIdx_ = get_block_idx(); |
| 155 | coreNum_ = get_block_num(); | 141 | coreNum_ = get_block_num(); |
| @@ -164,66 +150,6 @@ AICORE inline void Swiglu<InputElement>::Init( | |||
| 164 | reinterpret_cast<__gm__ int8_t*>(workspaceGM_ + tilingData_->swigluTiling.gmPermutedTokenOffset); | 150 | reinterpret_cast<__gm__ int8_t*>(workspaceGM_ + tilingData_->swigluTiling.gmPermutedTokenOffset); |
| 165 | perTokenScale2Ptr_ = reinterpret_cast<__gm__ float*>(workspaceGM_ + tilingData_->swigluTiling.perTokenScale2Offset); | 151 | perTokenScale2Ptr_ = reinterpret_cast<__gm__ float*>(workspaceGM_ + tilingData_->swigluTiling.perTokenScale2Offset); |
| 166 | cumsumMMPtr_ = reinterpret_cast<__gm__ int32_t*>(workspaceGM_ + tilingData_->frontReorderTiling.cumsumMMOffset); | 152 | cumsumMMPtr_ = reinterpret_cast<__gm__ int32_t*>(workspaceGM_ + tilingData_->frontReorderTiling.cumsumMMOffset); |
| 167 | - expertTokenNumsPtr_ = reinterpret_cast<__gm__ int32_t*>(expertTokenNumsGM); | ||
| 168 | -} | ||
| 169 | -template <typename InputElement> | ||
| 170 | -AICORE inline void Swiglu<InputElement>::WriteSharedSegmentMetadata(uint32_t segmentIdx) const | ||
| 171 | -{ | ||
| 172 | - if (coreIdx_ != 0U) { | ||
| 173 | - return; | ||
| 174 | - } | ||
| 175 | - | ||
| 176 | - uint32_t segmentStartExpert = 0; | ||
| 177 | - uint32_t segmentEndExpert = 0; | ||
| 178 | - uint32_t segmentRowBase = 0; | ||
| 179 | - uint32_t segmentRows = 0; | ||
| 180 | - uint32_t cumsumRows = 0; | ||
| 181 | - uint32_t expertTokenRows = 0; | ||
| 182 | - MoeBuildSegmentMetadata( | ||
| 183 | - segmentIdx, expertPerRank_, maxOutputSize_, cumsumMMPtr_, expertTokenNumsPtr_, rankSize_, segmentStartExpert, | ||
| 184 | - segmentEndExpert, segmentRowBase, segmentRows, cumsumRows, expertTokenRows); | ||
| 185 | - const uint32_t rowSplitBase = segmentRows / coreNum_; | ||
| 186 | - const uint32_t rowSplitRem = segmentRows - rowSplitBase * coreNum_; | ||
| 187 | - | ||
| 188 | - volatile __gm__ MegaMoeSwigluSegmentRuntimeMeta* entry = SegmentMetaPtr() + segmentIdx; | ||
| 189 | - entry->valid = 0U; | ||
| 190 | - entry->segmentIdx = segmentIdx; | ||
| 191 | - entry->segmentStartExpert = segmentStartExpert; | ||
| 192 | - entry->segmentEndExpert = segmentEndExpert; | ||
| 193 | - entry->segmentRowBase = segmentRowBase; | ||
| 194 | - entry->segmentRows = segmentRows; | ||
| 195 | - entry->cumsumRows = cumsumRows; | ||
| 196 | - entry->expertTokenRows = expertTokenRows; | ||
| 197 | - entry->rowSplitBase = rowSplitBase; | ||
| 198 | - entry->rowSplitRem = rowSplitRem; | ||
| 199 | - entry->generation = stageNum_; | ||
| 200 | - entry->producerCoreIdx = coreIdx_; | ||
| 201 | - entry->metadataMode = kSwigluMetadataModeSharedSegmentMeta; | ||
| 202 | - entry->segmentNum = MoeSwigluSegmentNum(expertPerRank_); | ||
| 203 | - entry->epilogueGranularity = MoeSwigluEpilogueGranularity(expertPerRank_); | ||
| 204 | - entry->marker = 1U; | ||
| 205 | - pipe_barrier(PIPE_ALL); | ||
| 206 | - entry->valid = 1U; | ||
| 207 | - pipe_barrier(PIPE_ALL); | ||
| 208 | - V5DcciGmRange( | ||
| 209 | - reinterpret_cast<__gm__ void*>(SegmentMetaPtr() + segmentIdx), sizeof(MegaMoeSwigluSegmentRuntimeMeta)); | ||
| 210 | -} | ||
| 211 | - | ||
| 212 | -template <typename InputElement> | ||
| 213 | -AICORE inline void Swiglu<InputElement>::ReadSharedSegmentMetadata( | ||
| 214 | - uint32_t segmentIdx, uint32_t& segmentStartExpert, uint32_t& segmentEndExpert, uint32_t& segmentRowBase, | ||
| 215 | - uint32_t& segmentRows, uint32_t& cumsumRows, uint32_t& expertTokenRows, uint32_t& rowSplitBase, | ||
| 216 | - uint32_t& rowSplitRem) const | ||
| 217 | -{ | ||
| 218 | - volatile __gm__ MegaMoeSwigluSegmentRuntimeMeta* entry = SegmentMetaPtr() + segmentIdx; | ||
| 219 | - segmentStartExpert = entry->segmentStartExpert; | ||
| 220 | - segmentEndExpert = entry->segmentEndExpert; | ||
| 221 | - segmentRowBase = entry->segmentRowBase; | ||
| 222 | - segmentRows = entry->segmentRows; | ||
| 223 | - cumsumRows = entry->cumsumRows; | ||
| 224 | - expertTokenRows = entry->expertTokenRows; | ||
| 225 | - rowSplitBase = entry->rowSplitBase; | ||
| 226 | - rowSplitRem = entry->rowSplitRem; | ||
| 227 | } | 153 | } |
| 228 | template <typename InputElement> | 154 | template <typename InputElement> |
| 229 | AICORE inline uint64_t Swiglu<InputElement>::SwigluStageBytes() const | 155 | AICORE inline uint64_t Swiglu<InputElement>::SwigluStageBytes() const |
| @@ -618,38 +544,44 @@ AICORE inline float Swiglu<InputElement>::ComputeAndStorePreparedFullRow( | |||
| 618 | } | 544 | } |
| 619 | 545 | ||
| 620 | template <typename InputElement> | 546 | template <typename InputElement> |
| 621 | -AICORE inline void Swiglu<InputElement>::Process() | 547 | +AICORE inline void Swiglu<InputElement>::ProcessFixed(uint32_t groupLocalId, uint32_t groupSize) |
| 548 | +{ | ||
| 549 | + coreIdx_ = groupLocalId; | ||
| 550 | + coreNum_ = groupSize; | ||
| 551 | + ProcessImpl(); | ||
| 552 | +} | ||
| 553 | + | ||
| 554 | +template <typename InputElement> | ||
| 555 | +AICORE inline void Swiglu<InputElement>::ProcessImpl() | ||
| 622 | { | 556 | { |
| 623 | if ASCEND_IS_AIC { | 557 | if ASCEND_IS_AIC { |
| 624 | return; | 558 | return; |
| 625 | } | 559 | } |
| 626 | 560 | ||
| 627 | - const uint32_t segmentNum = MoeSwigluSegmentNum(expertPerRank_); | 561 | + uint32_t groupBase = 0U; |
| 628 | - for (uint32_t segmentIdx = 0; segmentIdx < segmentNum; ++segmentIdx) { | 562 | + for (uint32_t groupIdx = 0U; groupIdx < expertPerRank_; ++groupIdx) { |
| 629 | - CrossCoreWaitFlag<0x2>(MegaMoeC2VHardFlagId(segmentIdx)); | 563 | + const int32_t readyEpoch = static_cast<int32_t>(groupIdx * 2U + 2U); |
| 630 | - WriteSharedSegmentMetadata(segmentIdx); // core 0负责分配任务给多个aiv | 564 | + const MegaMoeSyncLayout sync = FixedSyncLayout(tilingData_); |
| 631 | - pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); | 565 | + if (groupIdx >= tilingData_->fixedGroupTiling.fullAicGmm1ExpertCount && coreIdx_ == 0U) { |
| 566 | + CoordinateGroupConsumersMte( | ||
| 567 | + workspaceGM_, tilingData_, sync.gmm1ArrivalBase, sync.swigluReadyBase, | ||
| 568 | + tilingData_->fixedGroupTiling.gmm1GroupSize, coreNum_, groupIdx); | ||
| 569 | + } else { | ||
| 570 | + WaitEpochAcquire(FixedSyncSlot(workspaceGM_, tilingData_, sync.swigluReadyBase + coreIdx_), readyEpoch); | ||
| 571 | + } | ||
| 632 | 572 | ||
| 633 | - uint32_t segmentStartExpert = 0; | 573 | + const uint32_t currentMRaw = MoeCurrentMRaw(cumsumMMPtr_, rankSize_, expertPerRank_, groupIdx); |
| 634 | - uint32_t segmentEndExpert = 0; | 574 | + const uint32_t currentM = MoeClipCurrentM(currentMRaw, groupBase, maxOutputSize_); |
| 635 | - uint32_t segmentRowBase = 0; | 575 | + const uint32_t rowSplitBase = coreNum_ == 0U ? 0U : currentM / coreNum_; |
| 636 | - uint32_t segmentRows = 0; | 576 | + const uint32_t rowSplitRem = currentM - rowSplitBase * coreNum_; |
| 637 | - uint32_t cumsumRows = 0; | 577 | + const uint32_t localRows = rowSplitBase + (coreIdx_ < rowSplitRem ? 1U : 0U); |
| 638 | - uint32_t expertTokenRows = 0; | ||
| 639 | - uint32_t localRowStart = 0; | ||
| 640 | - uint32_t localRows = 0; | ||
| 641 | - uint32_t rowSplitBase = 0; | ||
| 642 | - uint32_t rowSplitRem = 0; | ||
| 643 | - ReadSharedSegmentMetadata( | ||
| 644 | - segmentIdx, segmentStartExpert, segmentEndExpert, segmentRowBase, segmentRows, cumsumRows, expertTokenRows, | ||
| 645 | - rowSplitBase, rowSplitRem); | ||
| 646 | - localRows = rowSplitBase + (coreIdx_ < rowSplitRem ? 1U : 0U); | ||
| 647 | const uint32_t prefixRows = coreIdx_ * rowSplitBase + (coreIdx_ < rowSplitRem ? coreIdx_ : rowSplitRem); | 578 | const uint32_t prefixRows = coreIdx_ * rowSplitBase + (coreIdx_ < rowSplitRem ? coreIdx_ : rowSplitRem); |
| 648 | - localRowStart = segmentRowBase + prefixRows; | 579 | + RunFullRowEpilogue(groupBase + prefixRows, localRows); |
| 649 | - RunFullRowEpilogue(localRowStart, localRows); | ||
| 650 | 580 | ||
| 651 | - pto::SYNCALL<pto::SyncCoreType::AIVOnly>(); | 581 | + NotifyGroupConsumersMte( |
| 652 | - CrossCoreSetFlag<0x2, PIPE_MTE3>(MegaMoeV2CHardFlagId(segmentIdx)); | 582 | + workspaceGM_, tilingData_, sync.swigluArrivalBase, sync.gmm2ReadyBase, coreNum_, |
| 583 | + tilingData_->fixedGroupTiling.gmm2GroupSize, coreIdx_, 0U, groupIdx); | ||
| 584 | + groupBase += currentM; | ||
| 653 | } | 585 | } |
| 654 | } | 586 | } |
| 655 | 587 | ||
| @@ -20,6 +20,7 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | + | ||
| 23 | 24 | ||
| 24 | 25 | ||
| 25 | constexpr uint32_t kUnpermuteVecTileElems = 2048U; | 26 | constexpr uint32_t kUnpermuteVecTileElems = 2048U; |
| @@ -27,13 +28,13 @@ constexpr uint32_t kUnpermuteMetadataBufferNum = 2U; | |||
| 27 | constexpr uint32_t kUnpermuteTokenBufferNum = 2U; | 28 | constexpr uint32_t kUnpermuteTokenBufferNum = 2U; |
| 28 | constexpr uint32_t kUnpermuteTaskSplitOutputToken = 1U; | 29 | constexpr uint32_t kUnpermuteTaskSplitOutputToken = 1U; |
| 29 | constexpr uint32_t kUnpermuteKTileMode = 1U; | 30 | constexpr uint32_t kUnpermuteKTileMode = 1U; |
| 30 | -constexpr int32_t kUnpermuteInvalidRow = -1; | ||
| 31 | 31 | ||
| 32 | template <typename OutputElement> | 32 | template <typename OutputElement> |
| 33 | class Unpermute { | 33 | class Unpermute { |
| 34 | public: | 34 | public: |
| 35 | AICORE inline void Init( | 35 | AICORE inline void Init( |
| 36 | - GM_ADDR workspaceGM, GM_ADDR probsGM, GM_ADDR outGM, const __gm__ MegaMoeTilingData* tilingData); | 36 | + GM_ADDR workspaceGM, GM_ADDR expertIdGM, GM_ADDR probsGM, GM_ADDR outGM, |
| 37 | + const __gm__ MegaMoeTilingData* tilingData, uint32_t workerIdx, uint32_t workerCount); | ||
| 37 | AICORE inline void Process(); | 38 | AICORE inline void Process(); |
| 38 | 39 | ||
| 39 | private: | 40 | private: |
| @@ -61,6 +62,10 @@ private: | |||
| 61 | uint32_t batch = tilingData_->unpermuteTiling.unpermuteTokenBatch; | 62 | uint32_t batch = tilingData_->unpermuteTiling.unpermuteTokenBatch; |
| 62 | return batch == 0U ? 1U : batch; | 63 | return batch == 0U ? 1U : batch; |
| 63 | } | 64 | } |
| 65 | + AICORE inline bool RankStreamingEnabled() const | ||
| 66 | + { | ||
| 67 | + return tilingData_->unpermuteTiling.unpermuteImplMode == kMegaMoeUnpermuteImplRankStreaming; | ||
| 68 | + } | ||
| 64 | AICORE inline event_t LoadFreeEvent(uint32_t bufferId) const { return static_cast<event_t>(bufferId); } | 69 | AICORE inline event_t LoadFreeEvent(uint32_t bufferId) const { return static_cast<event_t>(bufferId); } |
| 65 | AICORE inline event_t LoadReadyEvent(uint32_t bufferId) const { return static_cast<event_t>(bufferId + 2U); } | 70 | AICORE inline event_t LoadReadyEvent(uint32_t bufferId) const { return static_cast<event_t>(bufferId + 2U); } |
| 66 | AICORE inline event_t StoreFreeEvent() const { return EVENT_ID4; } | 71 | AICORE inline event_t StoreFreeEvent() const { return EVENT_ID4; } |
| @@ -80,12 +85,20 @@ private: | |||
| 80 | AICORE inline void AccumulateChunk(uint32_t bufferId, float prob, uint32_t cols); | 85 | AICORE inline void AccumulateChunk(uint32_t bufferId, float prob, uint32_t cols); |
| 81 | AICORE inline void StoreOutputChunk(uint32_t token, uint32_t col, uint32_t cols); | 86 | AICORE inline void StoreOutputChunk(uint32_t token, uint32_t col, uint32_t cols); |
| 82 | AICORE inline void ProcessToken(uint32_t metaBufferId, uint32_t batchStart, uint32_t localToken); | 87 | AICORE inline void ProcessToken(uint32_t metaBufferId, uint32_t batchStart, uint32_t localToken); |
| 88 | + AICORE inline bool TokenReadyForExpertProgress( | ||
| 89 | + uint32_t metaBufferId, uint32_t batchStart, uint32_t localToken, const uint32_t* readyExpertCounts) const; | ||
| 90 | + AICORE inline void BuildTokenRange( | ||
| 91 | + uint32_t workerIdx, uint32_t workerCount, uint32_t& tokenStart, uint32_t& tokenCount) const; | ||
| 92 | + AICORE inline void ProcessRankStreamingRange( | ||
| 93 | + uint32_t tokenStart, uint32_t tokenCount, const uint32_t* phase1ReadyExpertCounts, bool processPhase1); | ||
| 94 | + AICORE inline void ProcessRankStreaming(); | ||
| 83 | 95 | ||
| 84 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; | 96 | const __gm__ MegaMoeTilingData* tilingData_ = nullptr; |
| 85 | 97 | ||
| 86 | PtoRemoteWindow remoteWindow_; | 98 | PtoRemoteWindow remoteWindow_; |
| 87 | MegaMoePeerMemoryLayout peerMemoryLayout_; | 99 | MegaMoePeerMemoryLayout peerMemoryLayout_; |
| 88 | __gm__ OutputElement* offsetDPtr_ = nullptr; | 100 | __gm__ OutputElement* offsetDPtr_ = nullptr; |
| 101 | + __gm__ int32_t* expertIdPtr_ = nullptr; | ||
| 89 | __gm__ int32_t* expandedRowIdxPtr_ = nullptr; | 102 | __gm__ int32_t* expandedRowIdxPtr_ = nullptr; |
| 90 | __gm__ float* probsPtr_ = nullptr; | 103 | __gm__ float* probsPtr_ = nullptr; |
| 91 | __gm__ OutputElement* outPtr_ = nullptr; | 104 | __gm__ OutputElement* outPtr_ = nullptr; |
| @@ -94,6 +107,9 @@ private: | |||
| 94 | uint32_t problemK_ = 0; | 107 | uint32_t problemK_ = 0; |
| 95 | uint32_t topK_ = 0; | 108 | uint32_t topK_ = 0; |
| 96 | uint32_t maxOutputSize_ = 0; | 109 | uint32_t maxOutputSize_ = 0; |
| 110 | + uint32_t expertPerRank_ = 0; | ||
| 111 | + uint32_t rankSize_ = 0; | ||
| 112 | + int32_t dataReadyEpoch_ = 0; | ||
| 97 | uint32_t expandedRowsValid_ = 0; | 113 | uint32_t expandedRowsValid_ = 0; |
| 98 | uint32_t coreIdx_ = 0; | 114 | uint32_t coreIdx_ = 0; |
| 99 | uint32_t coreNum_ = 1; | 115 | uint32_t coreNum_ = 1; |
| @@ -113,7 +129,8 @@ private: | |||
| 113 | 129 | ||
| 114 | template <typename OutputElement> | 130 | template <typename OutputElement> |
| 115 | AICORE inline void Unpermute<OutputElement>::Init( | 131 | AICORE inline void Unpermute<OutputElement>::Init( |
| 116 | - GM_ADDR workspaceGM, GM_ADDR probsGM, GM_ADDR outGM, const __gm__ MegaMoeTilingData* tilingData) | 132 | + GM_ADDR workspaceGM, GM_ADDR expertIdGM, GM_ADDR probsGM, GM_ADDR outGM, const __gm__ MegaMoeTilingData* tilingData, |
| 133 | + uint32_t workerIdx, uint32_t workerCount) | ||
| 117 | { | 134 | { |
| 118 | tilingData_ = tilingData; | 135 | tilingData_ = tilingData; |
| 119 | 136 | ||
| @@ -121,14 +138,12 @@ AICORE inline void Unpermute<OutputElement>::Init( | |||
| 121 | problemK_ = tilingData_->megaMoeInfo.K; | 138 | problemK_ = tilingData_->megaMoeInfo.K; |
| 122 | topK_ = tilingData_->megaMoeInfo.topK; | 139 | topK_ = tilingData_->megaMoeInfo.topK; |
| 123 | maxOutputSize_ = tilingData_->megaMoeInfo.maxOutputSize; | 140 | maxOutputSize_ = tilingData_->megaMoeInfo.maxOutputSize; |
| 141 | + expertPerRank_ = tilingData_->megaMoeInfo.expertPerRank; | ||
| 142 | + rankSize_ = tilingData_->runtimeInfo.rankSize; | ||
| 124 | const uint32_t expandedRows = problemM_ * topK_; | 143 | const uint32_t expandedRows = problemM_ * topK_; |
| 125 | expandedRowsValid_ = expandedRows < maxOutputSize_ ? expandedRows : maxOutputSize_; | 144 | expandedRowsValid_ = expandedRows < maxOutputSize_ ? expandedRows : maxOutputSize_; |
| 126 | - coreIdx_ = get_block_idx(); | 145 | + coreIdx_ = workerIdx; |
| 127 | - coreNum_ = get_block_num(); | 146 | + coreNum_ = workerCount; |
| 128 | - if ASCEND_IS_AIV { | ||
| 129 | - coreIdx_ = get_block_idx() + get_subblockid() * get_block_num(); | ||
| 130 | - coreNum_ = get_block_num() * get_subblockdim(); | ||
| 131 | - } | ||
| 132 | 147 | ||
| 133 | splitBase_ = coreNum_ == 0U ? 0U : problemM_ / coreNum_; | 148 | splitBase_ = coreNum_ == 0U ? 0U : problemM_ / coreNum_; |
| 134 | splitRem_ = coreNum_ == 0U ? 0U : problemM_ % coreNum_; | 149 | splitRem_ = coreNum_ == 0U ? 0U : problemM_ % coreNum_; |
| @@ -138,10 +153,12 @@ AICORE inline void Unpermute<OutputElement>::Init( | |||
| 138 | remoteWindow_.Init(reinterpret_cast<GM_ADDR>(tilingData_->runtimeInfo.remoteWindowContext)); | 153 | remoteWindow_.Init(reinterpret_cast<GM_ADDR>(tilingData_->runtimeInfo.remoteWindowContext)); |
| 139 | peerMemoryLayout_.Init(remoteWindow_); | 154 | peerMemoryLayout_.Init(remoteWindow_); |
| 140 | offsetDPtr_ = reinterpret_cast<__gm__ OutputElement*>(remoteWindow_.LocalBase() + peerMemoryLayout_.offsetD); | 155 | offsetDPtr_ = reinterpret_cast<__gm__ OutputElement*>(remoteWindow_.LocalBase() + peerMemoryLayout_.offsetD); |
| 156 | + expertIdPtr_ = reinterpret_cast<__gm__ int32_t*>(expertIdGM); | ||
| 141 | expandedRowIdxPtr_ = | 157 | expandedRowIdxPtr_ = |
| 142 | reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.expandedRowIdxOffset); | 158 | reinterpret_cast<__gm__ int32_t*>(workspaceGM + tilingData_->frontReorderTiling.expandedRowIdxOffset); |
| 143 | probsPtr_ = reinterpret_cast<__gm__ float*>(probsGM); | 159 | probsPtr_ = reinterpret_cast<__gm__ float*>(probsGM); |
| 144 | outPtr_ = reinterpret_cast<__gm__ OutputElement*>(outGM); | 160 | outPtr_ = reinterpret_cast<__gm__ OutputElement*>(outGM); |
| 161 | + dataReadyEpoch_ = RankStreamingEnabled() ? remoteWindow_.DataReadyEpoch() : 0; | ||
| 145 | 162 | ||
| 146 | InitUbLayout(); | 163 | InitUbLayout(); |
| 147 | } | 164 | } |
| @@ -284,10 +301,6 @@ AICORE inline void Unpermute<OutputElement>::ProcessToken( | |||
| 284 | bool hasPending = false; | 301 | bool hasPending = false; |
| 285 | uint32_t pendingBuffer = 0; | 302 | uint32_t pendingBuffer = 0; |
| 286 | float pendingProb = 0.0f; | 303 | float pendingProb = 0.0f; |
| 287 | - uint32_t validTopk = 0; | ||
| 288 | - uint32_t topkProcessed = 0; | ||
| 289 | - int32_t firstExpandedRow = kUnpermuteInvalidRow; | ||
| 290 | - float firstProb = 0.0f; | ||
| 291 | 304 | ||
| 292 | for (uint32_t topkIdx = 0; topkIdx < topK_; ++topkIdx) { | 305 | for (uint32_t topkIdx = 0; topkIdx < topK_; ++topkIdx) { |
| 293 | const int32_t expandedRow = ReadExpandedRow(metaBufferId, localToken, topkIdx); | 306 | const int32_t expandedRow = ReadExpandedRow(metaBufferId, localToken, topkIdx); |
| @@ -302,24 +315,164 @@ AICORE inline void Unpermute<OutputElement>::ProcessToken( | |||
| 302 | LoadOffsetDChunk(bufferId, expandedRow, col, cols); | 315 | LoadOffsetDChunk(bufferId, expandedRow, col, cols); |
| 303 | if (hasPending) { | 316 | if (hasPending) { |
| 304 | AccumulateChunk(pendingBuffer, pendingProb, cols); | 317 | AccumulateChunk(pendingBuffer, pendingProb, cols); |
| 305 | - ++topkProcessed; | ||
| 306 | } | 318 | } |
| 307 | pendingBuffer = bufferId; | 319 | pendingBuffer = bufferId; |
| 308 | pendingProb = prob; | 320 | pendingProb = prob; |
| 309 | hasPending = true; | 321 | hasPending = true; |
| 310 | - ++validTopk; | ||
| 311 | - if (firstExpandedRow == kUnpermuteInvalidRow) { | ||
| 312 | - firstExpandedRow = expandedRow; | ||
| 313 | - firstProb = prob; | ||
| 314 | - } | ||
| 315 | } | 322 | } |
| 316 | if (hasPending) { | 323 | if (hasPending) { |
| 317 | AccumulateChunk(pendingBuffer, pendingProb, cols); | 324 | AccumulateChunk(pendingBuffer, pendingProb, cols); |
| 318 | - ++topkProcessed; | ||
| 319 | } | 325 | } |
| 320 | StoreOutputChunk(token, col, cols); // 写回GM | 326 | StoreOutputChunk(token, col, cols); // 写回GM |
| 321 | } | 327 | } |
| 322 | } | 328 | } |
| 329 | + | ||
| 330 | +template <typename OutputElement> | ||
| 331 | +AICORE inline bool Unpermute<OutputElement>::TokenReadyForExpertProgress( | ||
| 332 | + uint32_t metaBufferId, uint32_t batchStart, uint32_t localToken, const uint32_t* readyExpertCounts) const | ||
| 333 | +{ | ||
| 334 | + if (readyExpertCounts == nullptr || expertPerRank_ == 0U || rankSize_ == 0U || | ||
| 335 | + rankSize_ > COMBINE_EXPERT_PROGRESS_MAX_RANKS) { | ||
| 336 | + return false; | ||
| 337 | + } | ||
| 338 | + bool hasValidRoute = false; | ||
| 339 | + bool allRoutesReady = true; | ||
| 340 | + for (uint32_t topkIdx = 0U; topkIdx < topK_; ++topkIdx) { | ||
| 341 | + const int32_t expandedRow = ReadExpandedRow(metaBufferId, localToken, topkIdx); | ||
| 342 | + if (expandedRow < 0 || static_cast<uint32_t>(expandedRow) >= expandedRowsValid_) { | ||
| 343 | + continue; | ||
| 344 | + } | ||
| 345 | + hasValidRoute = true; | ||
| 346 | + const int32_t expert = expertIdPtr_[static_cast<uint64_t>(batchStart + localToken) * topK_ + topkIdx]; | ||
| 347 | + if (expert < 0) { | ||
| 348 | + allRoutesReady = false; | ||
| 349 | + continue; | ||
| 350 | + } | ||
| 351 | + const uint32_t globalExpert = static_cast<uint32_t>(expert); | ||
| 352 | + const uint32_t producerRank = globalExpert / expertPerRank_; | ||
| 353 | + const uint32_t localExpert = globalExpert - producerRank * expertPerRank_; | ||
| 354 | + if (producerRank >= rankSize_ || localExpert >= readyExpertCounts[producerRank]) { | ||
| 355 | + allRoutesReady = false; | ||
| 356 | + } | ||
| 357 | + } | ||
| 358 | + return hasValidRoute && allRoutesReady; | ||
| 359 | +} | ||
| 360 | + | ||
| 361 | +template <typename OutputElement> | ||
| 362 | +AICORE inline void Unpermute<OutputElement>::BuildTokenRange( | ||
| 363 | + uint32_t workerIdx, uint32_t workerCount, uint32_t& tokenStart, uint32_t& tokenCount) const | ||
| 364 | +{ | ||
| 365 | + tokenStart = 0U; | ||
| 366 | + tokenCount = 0U; | ||
| 367 | + if (workerCount == 0U || workerIdx >= workerCount) { | ||
| 368 | + return; | ||
| 369 | + } | ||
| 370 | + const uint32_t splitBase = problemM_ / workerCount; | ||
| 371 | + const uint32_t splitRem = problemM_ - splitBase * workerCount; | ||
| 372 | + tokenStart = workerIdx * splitBase + (workerIdx < splitRem ? workerIdx : splitRem); | ||
| 373 | + tokenCount = splitBase + (workerIdx < splitRem ? 1U : 0U); | ||
| 374 | +} | ||
| 375 | + | ||
| 376 | +template <typename OutputElement> | ||
| 377 | +AICORE inline void Unpermute<OutputElement>::ProcessRankStreamingRange( | ||
| 378 | + uint32_t tokenStart, uint32_t tokenCount, const uint32_t* phase1ReadyExpertCounts, bool processPhase1) | ||
| 379 | +{ | ||
| 380 | + if (tokenCount == 0U) { | ||
| 381 | + return; | ||
| 382 | + } | ||
| 383 | + | ||
| 384 | + const uint32_t batchLimit = TokenBatch(); | ||
| 385 | + uint32_t currentBatchStart = tokenStart; | ||
| 386 | + uint32_t currentBatchTokens = tokenCount < batchLimit ? tokenCount : batchLimit; | ||
| 387 | + uint32_t currentBuffer = 0U; | ||
| 388 | + PrefetchMetadata(currentBuffer, currentBatchStart, currentBatchTokens); | ||
| 389 | + | ||
| 390 | + uint32_t consumedTokens = 0U; | ||
| 391 | + while (consumedTokens < tokenCount) { | ||
| 392 | + WaitMetadata(currentBuffer); | ||
| 393 | + const uint32_t nextConsumed = consumedTokens + currentBatchTokens; | ||
| 394 | + const bool hasNext = nextConsumed < tokenCount; | ||
| 395 | + const uint32_t nextBuffer = (currentBuffer + 1U) % kUnpermuteMetadataBufferNum; | ||
| 396 | + uint32_t nextBatchStart = 0U; | ||
| 397 | + uint32_t nextBatchTokens = 0U; | ||
| 398 | + if (hasNext) { | ||
| 399 | + nextBatchStart = tokenStart + nextConsumed; | ||
| 400 | + const uint32_t remaining = tokenCount - nextConsumed; | ||
| 401 | + nextBatchTokens = remaining < batchLimit ? remaining : batchLimit; | ||
| 402 | + PrefetchMetadata(nextBuffer, nextBatchStart, nextBatchTokens); | ||
| 403 | + } | ||
| 404 | + | ||
| 405 | + for (uint32_t localToken = 0U; localToken < currentBatchTokens; ++localToken) { | ||
| 406 | + const bool phase1Task = | ||
| 407 | + TokenReadyForExpertProgress(currentBuffer, currentBatchStart, localToken, phase1ReadyExpertCounts); | ||
| 408 | + if (phase1Task == processPhase1) { | ||
| 409 | + ProcessToken(currentBuffer, currentBatchStart, localToken); | ||
| 410 | + } | ||
| 411 | + } | ||
| 412 | + | ||
| 413 | + consumedTokens = nextConsumed; | ||
| 414 | + currentBatchStart = nextBatchStart; | ||
| 415 | + currentBatchTokens = nextBatchTokens; | ||
| 416 | + currentBuffer = nextBuffer; | ||
| 417 | + } | ||
| 418 | +} | ||
| 419 | + | ||
| 420 | +template <typename OutputElement> | ||
| 421 | +AICORE inline void Unpermute<OutputElement>::ProcessRankStreaming() | ||
| 422 | +{ | ||
| 423 | + const uint32_t rankCount = rankSize_; | ||
| 424 | + const uint32_t initialWorkerCount = coreNum_ < tilingData_->fixedGroupTiling.gmm1GroupSize * 2U ? | ||
| 425 | + coreNum_ : | ||
| 426 | + tilingData_->fixedGroupTiling.gmm1GroupSize * 2U; | ||
| 427 | + | ||
| 428 | + WaitEpochAcquire(remoteWindow_.LocalUnpermutePhase1ProgressEpochSlot(), dataReadyEpoch_); | ||
| 429 | + uint32_t phase1ReadyExpertCounts[COMBINE_EXPERT_PROGRESS_MAX_RANKS] = {0U}; | ||
| 430 | + remoteWindow_.ReadUnpermutePhase1Progress(phase1ReadyExpertCounts, rankCount); | ||
| 431 | + bool phase1AllReady = true; | ||
| 432 | + for (uint32_t producerRank = 0U; producerRank < rankCount; ++producerRank) { | ||
| 433 | + if (phase1ReadyExpertCounts[producerRank] < expertPerRank_) { | ||
| 434 | + phase1AllReady = false; | ||
| 435 | + } | ||
| 436 | + } | ||
| 437 | + | ||
| 438 | + if (coreIdx_ < initialWorkerCount) { | ||
| 439 | + uint32_t phase1TokenStart = 0U; | ||
| 440 | + uint32_t phase1TokenCount = 0U; | ||
| 441 | + BuildTokenRange(coreIdx_, initialWorkerCount, phase1TokenStart, phase1TokenCount); | ||
| 442 | + ProcessRankStreamingRange(phase1TokenStart, phase1TokenCount, phase1ReadyExpertCounts, true); | ||
| 443 | + remoteWindow_.PublishPhase1Done(coreIdx_, dataReadyEpoch_); | ||
| 444 | + } | ||
| 445 | + | ||
| 446 | + if (coreIdx_ == 0U) { | ||
| 447 | + remoteWindow_.WaitPhase1DoneMte(initialWorkerCount, dataReadyEpoch_); | ||
| 448 | + uint32_t liveReadyExpertCounts[COMBINE_EXPERT_PROGRESS_MAX_RANKS] = {0U}; | ||
| 449 | + uint32_t minimumReady = 0U; | ||
| 450 | + while (minimumReady < expertPerRank_) { | ||
| 451 | + const uint32_t observedMinimum = | ||
| 452 | + remoteWindow_.ReadExpertProgressMte(dataReadyEpoch_, expertPerRank_, liveReadyExpertCounts); | ||
| 453 | + if (observedMinimum > minimumReady) { | ||
| 454 | + remoteWindow_.AcquireDataReady(); | ||
| 455 | + minimumReady = observedMinimum; | ||
| 456 | + } else { | ||
| 457 | + EpochPollBackoff(); | ||
| 458 | + } | ||
| 459 | + } | ||
| 460 | + if (coreNum_ > initialWorkerCount) { | ||
| 461 | + WaitEpochAcquire(remoteWindow_.LocalUnpermuteStartSlot(initialWorkerCount), dataReadyEpoch_); | ||
| 462 | + } | ||
| 463 | + remoteWindow_.PublishUnpermuteAllReady(coreNum_, dataReadyEpoch_); | ||
| 464 | + } else { | ||
| 465 | + WaitEpochAcquire(remoteWindow_.LocalUnpermuteAllReadySlot(coreIdx_), dataReadyEpoch_); | ||
| 466 | + } | ||
| 467 | + | ||
| 468 | + if (!phase1AllReady) { | ||
| 469 | + uint32_t phase2TokenStart = 0U; | ||
| 470 | + uint32_t phase2TokenCount = 0U; | ||
| 471 | + BuildTokenRange(coreIdx_, coreNum_, phase2TokenStart, phase2TokenCount); | ||
| 472 | + ProcessRankStreamingRange(phase2TokenStart, phase2TokenCount, phase1ReadyExpertCounts, false); | ||
| 473 | + } | ||
| 474 | +} | ||
| 475 | + | ||
| 323 | template <typename OutputElement> | 476 | template <typename OutputElement> |
| 324 | AICORE inline void Unpermute<OutputElement>::Process() | 477 | AICORE inline void Unpermute<OutputElement>::Process() |
| 325 | { | 478 | { |
| @@ -327,6 +480,11 @@ AICORE inline void Unpermute<OutputElement>::Process() | |||
| 327 | return; | 480 | return; |
| 328 | } | 481 | } |
| 329 | SetInitialFlags(); | 482 | SetInitialFlags(); |
| 483 | + if (RankStreamingEnabled()) { | ||
| 484 | + ProcessRankStreaming(); | ||
| 485 | + FinalizeLocalPipe(); | ||
| 486 | + return; | ||
| 487 | + } | ||
| 330 | if (tokenCount_ == 0U) { | 488 | if (tokenCount_ == 0U) { |
| 331 | FinalizeLocalPipe(); | 489 | FinalizeLocalPipe(); |
| 332 | return; | 490 | return; |
| @@ -62,7 +62,7 @@ AICORE inline void V5DcciGmRangeNoFence(__gm__ void* ptr, uint64_t bytes) | |||
| 62 | const uint64_t end = (reinterpret_cast<uint64_t>(ptr) + bytes + cacheLineBytes - 1U) & ~(cacheLineBytes - 1U); | 62 | const uint64_t end = (reinterpret_cast<uint64_t>(ptr) + bytes + cacheLineBytes - 1U) & ~(cacheLineBytes - 1U); |
| 63 | for (uint64_t addr = start; addr < end; addr += cacheLineBytes) { | 63 | for (uint64_t addr = start; addr < end; addr += cacheLineBytes) { |
| 64 | __asm__ __volatile__(""); | 64 | __asm__ __volatile__(""); |
| 65 | - dcci(reinterpret_cast<__gm__ void*>(addr), cache_line_t::SINGLE_CACHE_LINE); | 65 | + dcci(reinterpret_cast<__gm__ void*>(addr), SINGLE_CACHE_LINE); |
| 66 | __asm__ __volatile__(""); | 66 | __asm__ __volatile__(""); |
| 67 | } | 67 | } |
| 68 | } | 68 | } |
| @@ -78,21 +78,4 @@ AICORE inline uint32_t MegaMoeActiveCopyCores(uint32_t rankSize, uint32_t coreNu | |||
| 78 | return rankSize < coreNum ? rankSize : coreNum; | 78 | return rankSize < coreNum ? rankSize : coreNum; |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | -AICORE inline uint16_t MegaMoeD2CHardFlagId(uint32_t logicalGroupEventIdx) | ||
| 82 | -{ | ||
| 83 | - return static_cast<uint16_t>(MEGA_MOE_D2C_HARD_FLAG_BASE + logicalGroupEventIdx / CROSS_CORE_FLAG_MAX_SET_COUNT); | ||
| 84 | -} | ||
| 85 | - | ||
| 86 | -AICORE inline uint16_t MegaMoeC2VHardFlagId(uint32_t segmentIdx) | ||
| 87 | -{ | ||
| 88 | - (void)segmentIdx; | ||
| 89 | - return MEGA_MOE_C2V_HARD_FLAG_BASE; | ||
| 90 | -} | ||
| 91 | - | ||
| 92 | -AICORE inline uint16_t MegaMoeV2CHardFlagId(uint32_t segmentIdx) | ||
| 93 | -{ | ||
| 94 | - (void)segmentIdx; | ||
| 95 | - return MEGA_MOE_V2C_HARD_FLAG_BASE; | ||
| 96 | -} | ||
| 97 | - | ||
| 98 | 81 | ||
| @@ -15,19 +15,6 @@ See LICENSE in the root of the software repository for the full text of the Lice | |||
| 15 | 15 | ||
| 16 | constexpr static uint64_t MB_SIZE = 1024 * 1024UL; | 16 | constexpr static uint64_t MB_SIZE = 1024 * 1024UL; |
| 17 | constexpr static int32_t UB_ALIGN = 32; | 17 | constexpr static int32_t UB_ALIGN = 32; |
| 18 | -constexpr uint16_t CROSS_CORE_FLAG_MAX_SET_COUNT = 15; | ||
| 19 | -constexpr uint16_t MEGA_MOE_MAX_BUSINESS_HARD_FLAG_ID = 10; | ||
| 20 | -constexpr uint16_t MEGA_MOE_D2C_HARD_FLAG_BASE = 0; | ||
| 21 | -constexpr uint16_t MEGA_MOE_D2C_HARD_FLAG_COUNT = 9; | ||
| 22 | -constexpr uint16_t MEGA_MOE_D2C_HARD_FLAG_LAST = MEGA_MOE_D2C_HARD_FLAG_BASE + MEGA_MOE_D2C_HARD_FLAG_COUNT - 1; | ||
| 23 | -constexpr uint16_t MEGA_MOE_C2V_HARD_FLAG_BASE = 9; | ||
| 24 | -constexpr uint16_t MEGA_MOE_V2C_HARD_FLAG_BASE = 10; | ||
| 25 | -constexpr uint16_t MEGA_MOE_GMM2_TO_COMBINE_HARD_FLAG_BASE = 0; | ||
| 26 | -constexpr uint32_t MEGA_MOE_D2C_MAX_LOGICAL_GROUP_EVENTS = MEGA_MOE_D2C_HARD_FLAG_COUNT * CROSS_CORE_FLAG_MAX_SET_COUNT; | ||
| 27 | -constexpr uint32_t MEGA_MOE_GMM2_TO_COMBINE_MAX_LOGICAL_GROUP_EVENTS = | ||
| 28 | - MEGA_MOE_D2C_HARD_FLAG_COUNT * CROSS_CORE_FLAG_MAX_SET_COUNT; | ||
| 29 | -static_assert(MEGA_MOE_D2C_HARD_FLAG_LAST < MEGA_MOE_C2V_HARD_FLAG_BASE); | ||
| 30 | -static_assert(MEGA_MOE_V2C_HARD_FLAG_BASE <= MEGA_MOE_MAX_BUSINESS_HARD_FLAG_ID); | ||
| 31 | 18 | ||
| 32 | struct AtlasA2 { | 19 | struct AtlasA2 { |
| 33 | static constexpr uint32_t BIAS_SIZE = 1024; | 20 | static constexpr uint32_t BIAS_SIZE = 1024; |
| @@ -39,6 +26,4 @@ struct AtlasA2 { | |||
| 39 | static constexpr uint32_t L0C_SIZE = 128 * 1024; | 26 | static constexpr uint32_t L0C_SIZE = 128 * 1024; |
| 40 | }; | 27 | }; |
| 41 | 28 | ||
| 42 | - | ||
| 43 | - | ||
| 44 | 29 | ||
| @@ -27,6 +27,88 @@ constexpr uint32_t START_AIV_BARRIER_COUNTER_BASE_INDEX = 14336; | |||
| 27 | constexpr uint32_t START_AIV_BARRIER_EPOCH_INDEX = 18432; | 27 | constexpr uint32_t START_AIV_BARRIER_EPOCH_INDEX = 18432; |
| 28 | constexpr uint32_t START_AIC_BARRIER_COUNTER_BASE_INDEX = 20480; | 28 | constexpr uint32_t START_AIC_BARRIER_COUNTER_BASE_INDEX = 20480; |
| 29 | constexpr uint32_t START_AIC_BARRIER_EPOCH_INDEX = 24576; | 29 | constexpr uint32_t START_AIC_BARRIER_EPOCH_INDEX = 24576; |
| 30 | +// Keep one cache line per producer slot. These slots are independent from the | ||
| 31 | +// older all-rank barrier counters above and are reused with a launch epoch. | ||
| 32 | +constexpr uint32_t COMBINE_DATA_READY_BASE_INDEX = 26624; | ||
| 33 | +constexpr uint32_t COMBINE_DATA_READY_STRIDE = 16; | ||
| 34 | +constexpr uint32_t COMBINE_EXPERT_PROGRESS_OFFSET = 1; | ||
| 35 | +constexpr uint32_t COMBINE_EXPERT_PROGRESS_COUNT_BITS = 6; | ||
| 36 | +constexpr uint32_t COMBINE_EXPERT_PROGRESS_COUNT_MASK = (1U << COMBINE_EXPERT_PROGRESS_COUNT_BITS) - 1U; | ||
| 37 | +constexpr uint32_t COMBINE_EXPERT_PROGRESS_MAX_RANKS = kMegaMoeExpertProgressMaxRanks; | ||
| 38 | +constexpr uint32_t COMBINE_DATA_READY_EPOCH_INDEX = 28672; | ||
| 39 | +constexpr uint32_t UNPERMUTE_ALL_READY_BASE_INDEX = COMBINE_DATA_READY_EPOCH_INDEX + COMBINE_DATA_READY_STRIDE; | ||
| 40 | +constexpr uint32_t UNPERMUTE_ALL_READY_STRIDE = 16; | ||
| 41 | +constexpr uint32_t UNPERMUTE_ALL_READY_SLOT_COUNT = kMegaMoeFixedPhysicalAivNum; | ||
| 42 | +constexpr uint32_t UNPERMUTE_START_BASE_INDEX = | ||
| 43 | + UNPERMUTE_ALL_READY_BASE_INDEX + UNPERMUTE_ALL_READY_SLOT_COUNT * UNPERMUTE_ALL_READY_STRIDE; | ||
| 44 | +constexpr uint32_t UNPERMUTE_START_STRIDE = 16; | ||
| 45 | +constexpr uint32_t UNPERMUTE_START_SLOT_COUNT = kMegaMoeFixedPhysicalAivNum; | ||
| 46 | +constexpr uint32_t COMBINE_LOCAL_DONE_BASE_INDEX = | ||
| 47 | + UNPERMUTE_START_BASE_INDEX + UNPERMUTE_START_SLOT_COUNT * UNPERMUTE_START_STRIDE; | ||
| 48 | +constexpr uint32_t COMBINE_LOCAL_DONE_STRIDE = 16; | ||
| 49 | +constexpr uint32_t COMBINE_LOCAL_DONE_SLOT_COUNT = kMegaMoeFixedPhysicalAivNum; | ||
| 50 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_BASE_INDEX = | ||
| 51 | + COMBINE_LOCAL_DONE_BASE_INDEX + COMBINE_LOCAL_DONE_SLOT_COUNT * COMBINE_LOCAL_DONE_STRIDE; | ||
| 52 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_STRIDE = 32; | ||
| 53 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_EPOCH_OFFSET = 0; | ||
| 54 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_MASK_OFFSET = 1; | ||
| 55 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_COUNTS_OFFSET = 2; | ||
| 56 | +constexpr uint32_t REMOTE_WINDOW_CACHE_LINE_VALUES = 64U / sizeof(int32_t); | ||
| 57 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_VALUE_COUNT = | ||
| 58 | + UNPERMUTE_PHASE1_PROGRESS_COUNTS_OFFSET + COMBINE_EXPERT_PROGRESS_MAX_RANKS; | ||
| 59 | +constexpr uint32_t UNPERMUTE_PHASE1_PROGRESS_MAX_CACHE_LINE_COUNT = | ||
| 60 | + (UNPERMUTE_PHASE1_PROGRESS_VALUE_COUNT + REMOTE_WINDOW_CACHE_LINE_VALUES - 1U) / REMOTE_WINDOW_CACHE_LINE_VALUES; | ||
| 61 | +constexpr uint32_t UNPERMUTE_DISPATCH_RELEASE_BASE_INDEX = | ||
| 62 | + UNPERMUTE_PHASE1_PROGRESS_BASE_INDEX + UNPERMUTE_PHASE1_PROGRESS_STRIDE; | ||
| 63 | +constexpr uint32_t UNPERMUTE_DISPATCH_RELEASE_STRIDE = 16; | ||
| 64 | +constexpr uint32_t UNPERMUTE_DISPATCH_RELEASE_SLOT_COUNT = kMegaMoeFixedPhysicalAicNum; | ||
| 65 | +constexpr uint32_t UNPERMUTE_SWIGLU_RELEASE_BASE_INDEX = | ||
| 66 | + UNPERMUTE_DISPATCH_RELEASE_BASE_INDEX + UNPERMUTE_DISPATCH_RELEASE_SLOT_COUNT * UNPERMUTE_DISPATCH_RELEASE_STRIDE; | ||
| 67 | +constexpr uint32_t UNPERMUTE_SWIGLU_RELEASE_STRIDE = 16; | ||
| 68 | +constexpr uint32_t UNPERMUTE_SWIGLU_RELEASE_SLOT_COUNT = kMegaMoeFixedPhysicalAicNum; | ||
| 69 | +constexpr uint32_t UNPERMUTE_PHASE1_DONE_BASE_INDEX = | ||
| 70 | + UNPERMUTE_SWIGLU_RELEASE_BASE_INDEX + UNPERMUTE_SWIGLU_RELEASE_SLOT_COUNT * UNPERMUTE_SWIGLU_RELEASE_STRIDE; | ||
| 71 | +constexpr uint32_t UNPERMUTE_PHASE1_DONE_STRIDE = 16; | ||
| 72 | +constexpr uint32_t UNPERMUTE_PHASE1_DONE_SLOT_COUNT = kMegaMoeFixedPhysicalAivNum; | ||
| 73 | +constexpr uint32_t REMOTE_WINDOW_SYNC_MAX_SLOTS = kMegaMoeFixedPhysicalAivNum; | ||
| 74 | +constexpr uint32_t REMOTE_WINDOW_SYNC_VALUES_PER_SLOT = 16; | ||
| 75 | +constexpr uint32_t REMOTE_WINDOW_SYNC_MAX_VALUES = REMOTE_WINDOW_SYNC_MAX_SLOTS * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT; | ||
| 76 | +constexpr uint64_t REMOTE_WINDOW_SYNC_SNAPSHOT_BYTES = | ||
| 77 | + static_cast<uint64_t>(REMOTE_WINDOW_SYNC_MAX_VALUES) * sizeof(int32_t); | ||
| 78 | +constexpr uint64_t REMOTE_WINDOW_SYNC_SNAPSHOT_UB_OFFSET = AtlasA2::UB_SIZE - REMOTE_WINDOW_SYNC_SNAPSHOT_BYTES; | ||
| 79 | +constexpr event_t REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT = EVENT_ID0; | ||
| 80 | +static_assert( | ||
| 81 | + COMBINE_DATA_READY_BASE_INDEX + COMBINE_EXPERT_PROGRESS_MAX_RANKS * COMBINE_DATA_READY_STRIDE <= | ||
| 82 | + COMBINE_DATA_READY_EPOCH_INDEX); | ||
| 83 | +static_assert(COMBINE_LOCAL_DONE_SLOT_COUNT >= COMBINE_EXPERT_PROGRESS_MAX_RANKS); | ||
| 84 | +static_assert(UNPERMUTE_PHASE1_PROGRESS_VALUE_COUNT <= UNPERMUTE_PHASE1_PROGRESS_STRIDE); | ||
| 85 | +static_assert( | ||
| 86 | + UNPERMUTE_PHASE1_PROGRESS_MAX_CACHE_LINE_COUNT * REMOTE_WINDOW_CACHE_LINE_VALUES <= | ||
| 87 | + UNPERMUTE_PHASE1_PROGRESS_STRIDE); | ||
| 88 | +static_assert(UNPERMUTE_PHASE1_PROGRESS_BASE_INDEX % REMOTE_WINDOW_CACHE_LINE_VALUES == 0U); | ||
| 89 | +static_assert( | ||
| 90 | + UNPERMUTE_PHASE1_DONE_BASE_INDEX + UNPERMUTE_PHASE1_DONE_SLOT_COUNT * UNPERMUTE_PHASE1_DONE_STRIDE < | ||
| 91 | + MB_SIZE / sizeof(int32_t)); | ||
| 92 | +static_assert(REMOTE_WINDOW_SYNC_SNAPSHOT_UB_OFFSET % UB_ALIGN == 0U); | ||
| 93 | + | ||
| 94 | +AICORE inline void RemoteWindowSyncPollBackoff() | ||
| 95 | +{ | ||
| 96 | + constexpr uint32_t kDelayTicks = 3U; | ||
| 97 | + const uint64_t deadline = get_sys_cnt() + kDelayTicks; | ||
| 98 | + while (get_sys_cnt() < deadline) { | ||
| 99 | + __asm__ __volatile__(""); | ||
| 100 | + } | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +AICORE inline void DcciUnpermutePhase1Progress(volatile __gm__ int32_t* base, uint32_t rankCount) | ||
| 104 | +{ | ||
| 105 | + const uint32_t valueCount = UNPERMUTE_PHASE1_PROGRESS_COUNTS_OFFSET + rankCount; | ||
| 106 | + const uint32_t cacheLineCount = | ||
| 107 | + (valueCount + REMOTE_WINDOW_CACHE_LINE_VALUES - 1U) / REMOTE_WINDOW_CACHE_LINE_VALUES; | ||
| 108 | + for (uint32_t line = 0U; line < cacheLineCount; ++line) { | ||
| 109 | + dcci((__gm__ void*)(base + line * REMOTE_WINDOW_CACHE_LINE_VALUES), SINGLE_CACHE_LINE); | ||
| 110 | + } | ||
| 111 | +} | ||
| 30 | 112 | ||
| 31 | class PtoRemoteWindow { | 113 | class PtoRemoteWindow { |
| 32 | public: | 114 | public: |
| @@ -74,6 +156,407 @@ public: | |||
| 74 | return RemotePtr(LocalSignalBase(), rankId); | 156 | return RemotePtr(LocalSignalBase(), rankId); |
| 75 | } | 157 | } |
| 76 | 158 | ||
| 159 | + // The epoch is advanced by one AIV before the first stage starts. This | ||
| 160 | + // permits repeated launches even when the host does not clear the window. | ||
| 161 | + AICORE inline int32_t PrepareDataReadyEpoch() const | ||
| 162 | + { | ||
| 163 | + __gm__ int32_t* epochSlot = LocalSignalBase() + COMBINE_DATA_READY_EPOCH_INDEX; | ||
| 164 | + dcci((__gm__ void*)epochSlot, SINGLE_CACHE_LINE); | ||
| 165 | + __asm__ __volatile__(""); | ||
| 166 | + int32_t epoch = *epochSlot + 1; | ||
| 167 | + if (epoch <= 0) { | ||
| 168 | + epoch = 1; | ||
| 169 | + } | ||
| 170 | + *epochSlot = epoch; | ||
| 171 | + dcci((__gm__ void*)epochSlot, SINGLE_CACHE_LINE); | ||
| 172 | + PublishUnpermuteAllReady(UNPERMUTE_ALL_READY_SLOT_COUNT, 0); | ||
| 173 | + dsb(DSB_DDR); | ||
| 174 | + return epoch; | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + AICORE inline int32_t DataReadyEpoch() const | ||
| 178 | + { | ||
| 179 | + __gm__ int32_t* epochSlot = LocalSignalBase() + COMBINE_DATA_READY_EPOCH_INDEX; | ||
| 180 | + dcci((__gm__ void*)epochSlot, SINGLE_CACHE_LINE); | ||
| 181 | + __asm__ __volatile__(""); | ||
| 182 | + return *epochSlot; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + AICORE inline volatile __gm__ int32_t* LocalDataReadySlot(int32_t producerRank) const | ||
| 186 | + { | ||
| 187 | + if (producerRank < 0 || producerRank >= rankSize_) { | ||
| 188 | + return nullptr; | ||
| 189 | + } | ||
| 190 | + return LocalSignalBase() + COMBINE_DATA_READY_BASE_INDEX + | ||
| 191 | + static_cast<uint32_t>(producerRank) * COMBINE_DATA_READY_STRIDE; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + AICORE inline void PublishDataReady(int32_t ownerRank, int32_t epoch) const | ||
| 195 | + { | ||
| 196 | + if (ownerRank < 0 || ownerRank >= rankSize_) { | ||
| 197 | + return; | ||
| 198 | + } | ||
| 199 | + if (ownerRank == rank_) { | ||
| 200 | + volatile __gm__ int32_t* localSlot = LocalDataReadySlot(rank_); | ||
| 201 | + *localSlot = epoch; | ||
| 202 | + dcci((__gm__ void*)localSlot, SINGLE_CACHE_LINE); | ||
| 203 | + __asm__ __volatile__(""); | ||
| 204 | + return; | ||
| 205 | + } | ||
| 206 | + __gm__ int32_t* remoteSlot = RemoteSignalBase(ownerRank) + COMBINE_DATA_READY_BASE_INDEX + | ||
| 207 | + static_cast<uint32_t>(rank_) * COMBINE_DATA_READY_STRIDE; | ||
| 208 | + auto signal = pto::comm::Signal(remoteSlot); | ||
| 209 | + pto::comm::TNOTIFY(signal, epoch, pto::comm::NotifyOp::Set); | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + AICORE inline int32_t EncodeExpertProgress(int32_t epoch, uint32_t readyExpertCount) const | ||
| 213 | + { | ||
| 214 | + const uint32_t count = readyExpertCount & COMBINE_EXPERT_PROGRESS_COUNT_MASK; | ||
| 215 | + return static_cast<int32_t>((static_cast<uint32_t>(epoch) << COMBINE_EXPERT_PROGRESS_COUNT_BITS) | count); | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + AICORE inline void PublishExpertProgress(int32_t consumerRank, uint32_t readyExpertCount, int32_t epoch) const | ||
| 219 | + { | ||
| 220 | + if (consumerRank < 0 || consumerRank >= rankSize_ || readyExpertCount > COMBINE_EXPERT_PROGRESS_COUNT_MASK || | ||
| 221 | + readyExpertCount > kMegaMoeFixedMaxExperts) { | ||
| 222 | + return; | ||
| 223 | + } | ||
| 224 | + const int32_t encoded = EncodeExpertProgress(epoch, readyExpertCount); | ||
| 225 | + if (consumerRank == rank_) { | ||
| 226 | + volatile __gm__ int32_t* localSlot = LocalDataReadySlot(rank_) + COMBINE_EXPERT_PROGRESS_OFFSET; | ||
| 227 | + *localSlot = encoded; | ||
| 228 | + dcci((__gm__ void*)localSlot, SINGLE_CACHE_LINE); | ||
| 229 | + dsb(DSB_DDR); | ||
| 230 | + return; | ||
| 231 | + } | ||
| 232 | + __gm__ int32_t* remoteSlot = RemoteSignalBase(consumerRank) + COMBINE_DATA_READY_BASE_INDEX + | ||
| 233 | + static_cast<uint32_t>(rank_) * COMBINE_DATA_READY_STRIDE + | ||
| 234 | + COMBINE_EXPERT_PROGRESS_OFFSET; | ||
| 235 | + auto signal = pto::comm::Signal(remoteSlot); | ||
| 236 | + pto::comm::TNOTIFY(signal, encoded, pto::comm::NotifyOp::Set); | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + AICORE inline void AcquireDataReady() const | ||
| 240 | + { | ||
| 241 | + pipe_barrier(PIPE_ALL); | ||
| 242 | + dsb(DSB_DDR); | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + // AIV-only snapshot helpers. Each signal owns one 64-byte cache line and | ||
| 246 | + // only the first int32_t carries the epoch or mask. | ||
| 247 | + AICORE inline uint32_t ReadEpochMaskMte(__gm__ int32_t* base, uint32_t count, int32_t epoch) const | ||
| 248 | + { | ||
| 249 | + if (base == nullptr || count == 0U || count > REMOTE_WINDOW_SYNC_MAX_SLOTS) { | ||
| 250 | + return 0U; | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + using SnapshotShape = pto::Shape<1, 1, 1, 1, pto::DYNAMIC>; | ||
| 254 | + using SnapshotStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | ||
| 255 | + using SnapshotGlobal = pto::GlobalTensor<int32_t, SnapshotShape, SnapshotStride, pto::Layout::ND>; | ||
| 256 | + using SnapshotTile = | ||
| 257 | + pto::Tile<pto::TileType::Vec, int32_t, 1, REMOTE_WINDOW_SYNC_MAX_VALUES, pto::BLayout::RowMajor, -1, -1>; | ||
| 258 | + | ||
| 259 | + const uint32_t snapshotValues = count * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT; | ||
| 260 | + SnapshotShape snapshotShape(snapshotValues); | ||
| 261 | + SnapshotStride snapshotStride(snapshotValues, snapshotValues, snapshotValues, snapshotValues); | ||
| 262 | + SnapshotGlobal snapshotGlobal(base, snapshotShape, snapshotStride); | ||
| 263 | + SnapshotTile snapshotTile(1U, snapshotValues); | ||
| 264 | + pto::TASSIGN(snapshotTile, REMOTE_WINDOW_SYNC_SNAPSHOT_UB_OFFSET); | ||
| 265 | + pto::TLOAD(snapshotTile, snapshotGlobal); | ||
| 266 | + pto::PtoSetWaitFlag<PIPE_MTE2, PIPE_S>(REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT, REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT); | ||
| 267 | + | ||
| 268 | + uint32_t readyMask = 0U; | ||
| 269 | + for (uint32_t slot = 0U; slot < count; ++slot) { | ||
| 270 | + if (snapshotTile.GetValue(slot * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT) >= epoch) { | ||
| 271 | + readyMask |= 1U << slot; | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + return readyMask; | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + AICORE inline void PublishEpochRangeMte(__gm__ int32_t* base, uint32_t count, int32_t value) const | ||
| 278 | + { | ||
| 279 | + if (base == nullptr || count == 0U || count > REMOTE_WINDOW_SYNC_MAX_SLOTS) { | ||
| 280 | + return; | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + using SnapshotShape = pto::Shape<1, 1, 1, 1, pto::DYNAMIC>; | ||
| 284 | + using SnapshotStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | ||
| 285 | + using SnapshotGlobal = pto::GlobalTensor<int32_t, SnapshotShape, SnapshotStride, pto::Layout::ND>; | ||
| 286 | + using SnapshotTile = | ||
| 287 | + pto::Tile<pto::TileType::Vec, int32_t, 1, REMOTE_WINDOW_SYNC_MAX_VALUES, pto::BLayout::RowMajor, -1, -1>; | ||
| 288 | + | ||
| 289 | + const uint32_t snapshotValues = count * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT; | ||
| 290 | + SnapshotShape snapshotShape(snapshotValues); | ||
| 291 | + SnapshotStride snapshotStride(snapshotValues, snapshotValues, snapshotValues, snapshotValues); | ||
| 292 | + SnapshotGlobal snapshotGlobal(base, snapshotShape, snapshotStride); | ||
| 293 | + SnapshotTile snapshotTile(1U, snapshotValues); | ||
| 294 | + pto::TASSIGN(snapshotTile, REMOTE_WINDOW_SYNC_SNAPSHOT_UB_OFFSET); | ||
| 295 | + for (uint32_t slot = 0U; slot < count; ++slot) { | ||
| 296 | + snapshotTile.SetValue(slot * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT, value); | ||
| 297 | + } | ||
| 298 | + pto::PtoSetWaitFlag<PIPE_S, PIPE_MTE3>(REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT, REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT); | ||
| 299 | + pto::TSTORE(snapshotGlobal, snapshotTile); | ||
| 300 | + pto::PtoSetWaitFlag<PIPE_MTE3, PIPE_S>(REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT, REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT); | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + AICORE inline uint32_t ReadDataReadyMaskMte(int32_t epoch) const | ||
| 304 | + { | ||
| 305 | + return ReadEpochMaskMte( | ||
| 306 | + const_cast<__gm__ int32_t*>(LocalDataReadySlot(0)), static_cast<uint32_t>(rankSize_), epoch); | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + AICORE inline uint32_t ReadExpertProgressMte( | ||
| 310 | + int32_t epoch, uint32_t expertPerRank, uint32_t* readyExpertCounts) const | ||
| 311 | + { | ||
| 312 | + if (readyExpertCounts == nullptr || rankSize_ <= 0 || | ||
| 313 | + static_cast<uint32_t>(rankSize_) > COMBINE_EXPERT_PROGRESS_MAX_RANKS) { | ||
| 314 | + return 0U; | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + using SnapshotShape = pto::Shape<1, 1, 1, 1, pto::DYNAMIC>; | ||
| 318 | + using SnapshotStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | ||
| 319 | + using SnapshotGlobal = pto::GlobalTensor<int32_t, SnapshotShape, SnapshotStride, pto::Layout::ND>; | ||
| 320 | + using SnapshotTile = | ||
| 321 | + pto::Tile<pto::TileType::Vec, int32_t, 1, REMOTE_WINDOW_SYNC_MAX_VALUES, pto::BLayout::RowMajor, -1, -1>; | ||
| 322 | + | ||
| 323 | + const uint32_t count = static_cast<uint32_t>(rankSize_); | ||
| 324 | + const uint32_t snapshotValues = count * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT; | ||
| 325 | + SnapshotShape snapshotShape(snapshotValues); | ||
| 326 | + SnapshotStride snapshotStride(snapshotValues, snapshotValues, snapshotValues, snapshotValues); | ||
| 327 | + SnapshotGlobal snapshotGlobal( | ||
| 328 | + const_cast<__gm__ int32_t*>(LocalDataReadySlot(0)), snapshotShape, snapshotStride); | ||
| 329 | + SnapshotTile snapshotTile(1U, snapshotValues); | ||
| 330 | + pto::TASSIGN(snapshotTile, REMOTE_WINDOW_SYNC_SNAPSHOT_UB_OFFSET); | ||
| 331 | + pto::TLOAD(snapshotTile, snapshotGlobal); | ||
| 332 | + pto::PtoSetWaitFlag<PIPE_MTE2, PIPE_S>(REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT, REMOTE_WINDOW_SYNC_SNAPSHOT_EVENT); | ||
| 333 | + | ||
| 334 | + uint32_t minimumReady = expertPerRank; | ||
| 335 | + const uint32_t expectedEpoch = static_cast<uint32_t>(epoch); | ||
| 336 | + for (uint32_t producerRank = 0U; producerRank < count; ++producerRank) { | ||
| 337 | + const uint32_t encoded = static_cast<uint32_t>(snapshotTile.GetValue( | ||
| 338 | + producerRank * REMOTE_WINDOW_SYNC_VALUES_PER_SLOT + COMBINE_EXPERT_PROGRESS_OFFSET)); | ||
| 339 | + const uint32_t observedEpoch = encoded >> COMBINE_EXPERT_PROGRESS_COUNT_BITS; | ||
| 340 | + uint32_t readyCount = observedEpoch == expectedEpoch ? encoded & COMBINE_EXPERT_PROGRESS_COUNT_MASK : 0U; | ||
| 341 | + if (readyCount > expertPerRank) { | ||
| 342 | + readyCount = expertPerRank; | ||
| 343 | + } | ||
| 344 | + readyExpertCounts[producerRank] = readyCount; | ||
| 345 | + minimumReady = readyCount < minimumReady ? readyCount : minimumReady; | ||
| 346 | + } | ||
| 347 | + return minimumReady; | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + AICORE inline volatile __gm__ int32_t* LocalUnpermuteAllReadySlot(uint32_t workerIdx) const | ||
| 351 | + { | ||
| 352 | + if (workerIdx >= UNPERMUTE_ALL_READY_SLOT_COUNT) { | ||
| 353 | + return nullptr; | ||
| 354 | + } | ||
| 355 | + return LocalSignalBase() + UNPERMUTE_ALL_READY_BASE_INDEX + workerIdx * UNPERMUTE_ALL_READY_STRIDE; | ||
| 356 | + } | ||
| 357 | + | ||
| 358 | + AICORE inline void PublishUnpermuteAllReady(uint32_t workerCount, int32_t epoch) const | ||
| 359 | + { | ||
| 360 | + const uint32_t count = | ||
| 361 | + workerCount < UNPERMUTE_ALL_READY_SLOT_COUNT ? workerCount : UNPERMUTE_ALL_READY_SLOT_COUNT; | ||
| 362 | + PublishEpochRangeMte(const_cast<__gm__ int32_t*>(LocalUnpermuteAllReadySlot(0)), count, epoch); | ||
| 363 | + } | ||
| 364 | + | ||
| 365 | + AICORE inline volatile __gm__ int32_t* LocalUnpermuteStartSlot(uint32_t workerIdx) const | ||
| 366 | + { | ||
| 367 | + if (workerIdx >= UNPERMUTE_START_SLOT_COUNT) { | ||
| 368 | + return nullptr; | ||
| 369 | + } | ||
| 370 | + return LocalSignalBase() + UNPERMUTE_START_BASE_INDEX + workerIdx * UNPERMUTE_START_STRIDE; | ||
| 371 | + } | ||
| 372 | + | ||
| 373 | + AICORE inline void PublishUnpermuteStart(uint32_t workerCount, int32_t epoch) const | ||
| 374 | + { | ||
| 375 | + const uint32_t count = workerCount < UNPERMUTE_START_SLOT_COUNT ? workerCount : UNPERMUTE_START_SLOT_COUNT; | ||
| 376 | + for (uint32_t workerIdx = 0U; workerIdx < count; ++workerIdx) { | ||
| 377 | + *LocalUnpermuteStartSlot(workerIdx) = epoch; | ||
| 378 | + } | ||
| 379 | + for (uint32_t workerIdx = 0U; workerIdx < count; ++workerIdx) { | ||
| 380 | + dcci((__gm__ void*)LocalUnpermuteStartSlot(workerIdx), SINGLE_CACHE_LINE); | ||
| 381 | + } | ||
| 382 | + dsb(DSB_DDR); | ||
| 383 | + __asm__ __volatile__(""); | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + AICORE inline void PublishUnpermuteStartRangeMte(uint32_t firstWorker, uint32_t workerCount, int32_t epoch) const | ||
| 387 | + { | ||
| 388 | + if (firstWorker >= UNPERMUTE_START_SLOT_COUNT) { | ||
| 389 | + return; | ||
| 390 | + } | ||
| 391 | + const uint32_t remaining = UNPERMUTE_START_SLOT_COUNT - firstWorker; | ||
| 392 | + const uint32_t count = workerCount < remaining ? workerCount : remaining; | ||
| 393 | + PublishEpochRangeMte(const_cast<__gm__ int32_t*>(LocalUnpermuteStartSlot(firstWorker)), count, epoch); | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + AICORE inline volatile __gm__ int32_t* LocalUnpermutePhase1ProgressEpochSlot() const | ||
| 397 | + { | ||
| 398 | + return LocalSignalBase() + UNPERMUTE_PHASE1_PROGRESS_BASE_INDEX + UNPERMUTE_PHASE1_PROGRESS_EPOCH_OFFSET; | ||
| 399 | + } | ||
| 400 | + | ||
| 401 | + AICORE inline void PublishUnpermutePhase1Progress( | ||
| 402 | + const uint32_t* readyExpertCounts, uint32_t rankCount, uint32_t readyRankMask, int32_t epoch) const | ||
| 403 | + { | ||
| 404 | + if (readyExpertCounts == nullptr || rankCount == 0U || rankCount > COMBINE_EXPERT_PROGRESS_MAX_RANKS) { | ||
| 405 | + return; | ||
| 406 | + } | ||
| 407 | + volatile __gm__ int32_t* base = LocalSignalBase() + UNPERMUTE_PHASE1_PROGRESS_BASE_INDEX; | ||
| 408 | + base[UNPERMUTE_PHASE1_PROGRESS_MASK_OFFSET] = static_cast<int32_t>(readyRankMask); | ||
| 409 | + for (uint32_t producerRank = 0U; producerRank < rankCount; ++producerRank) { | ||
| 410 | + base[UNPERMUTE_PHASE1_PROGRESS_COUNTS_OFFSET + producerRank] = | ||
| 411 | + static_cast<int32_t>(readyExpertCounts[producerRank]); | ||
| 412 | + } | ||
| 413 | + DcciUnpermutePhase1Progress(base, rankCount); | ||
| 414 | + dsb(DSB_DDR); | ||
| 415 | + base[UNPERMUTE_PHASE1_PROGRESS_EPOCH_OFFSET] = epoch; | ||
| 416 | + dcci((__gm__ void*)base, SINGLE_CACHE_LINE); | ||
| 417 | + dsb(DSB_DDR); | ||
| 418 | + } | ||
| 419 | + | ||
| 420 | + AICORE inline uint32_t ReadUnpermutePhase1Progress(uint32_t* readyExpertCounts, uint32_t rankCount) const | ||
| 421 | + { | ||
| 422 | + if (readyExpertCounts == nullptr || rankCount == 0U || rankCount > COMBINE_EXPERT_PROGRESS_MAX_RANKS) { | ||
| 423 | + return 0U; | ||
| 424 | + } | ||
| 425 | + volatile __gm__ int32_t* base = LocalSignalBase() + UNPERMUTE_PHASE1_PROGRESS_BASE_INDEX; | ||
| 426 | + DcciUnpermutePhase1Progress(base, rankCount); | ||
| 427 | + dsb(DSB_DDR); | ||
| 428 | + for (uint32_t producerRank = 0U; producerRank < rankCount; ++producerRank) { | ||
| 429 | + readyExpertCounts[producerRank] = | ||
| 430 | + static_cast<uint32_t>(base[UNPERMUTE_PHASE1_PROGRESS_COUNTS_OFFSET + producerRank]); | ||
| 431 | + } | ||
| 432 | + return static_cast<uint32_t>(base[UNPERMUTE_PHASE1_PROGRESS_MASK_OFFSET]); | ||
| 433 | + } | ||
| 434 | + | ||
| 435 | + AICORE inline volatile __gm__ int32_t* LocalDispatchReleaseSlot(uint32_t workerIdx) const | ||
| 436 | + { | ||
| 437 | + if (workerIdx >= UNPERMUTE_DISPATCH_RELEASE_SLOT_COUNT) { | ||
| 438 | + return nullptr; | ||
| 439 | + } | ||
| 440 | + return LocalSignalBase() + UNPERMUTE_DISPATCH_RELEASE_BASE_INDEX + | ||
| 441 | + workerIdx * UNPERMUTE_DISPATCH_RELEASE_STRIDE; | ||
| 442 | + } | ||
| 443 | + | ||
| 444 | + AICORE inline volatile __gm__ int32_t* LocalSwigluReleaseSlot(uint32_t workerIdx) const | ||
| 445 | + { | ||
| 446 | + if (workerIdx >= UNPERMUTE_SWIGLU_RELEASE_SLOT_COUNT) { | ||
| 447 | + return nullptr; | ||
| 448 | + } | ||
| 449 | + return LocalSignalBase() + UNPERMUTE_SWIGLU_RELEASE_BASE_INDEX + workerIdx * UNPERMUTE_SWIGLU_RELEASE_STRIDE; | ||
| 450 | + } | ||
| 451 | + | ||
| 452 | + AICORE inline void PublishDispatchRelease(uint32_t workerIdx, int32_t epoch) const | ||
| 453 | + { | ||
| 454 | + volatile __gm__ int32_t* slot = LocalDispatchReleaseSlot(workerIdx); | ||
| 455 | + if (slot == nullptr) { | ||
| 456 | + return; | ||
| 457 | + } | ||
| 458 | + *slot = epoch; | ||
| 459 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 460 | + dsb(DSB_DDR); | ||
| 461 | + } | ||
| 462 | + | ||
| 463 | + AICORE inline void PublishSwigluRelease(uint32_t workerIdx, int32_t epoch) const | ||
| 464 | + { | ||
| 465 | + volatile __gm__ int32_t* slot = LocalSwigluReleaseSlot(workerIdx); | ||
| 466 | + if (slot == nullptr) { | ||
| 467 | + return; | ||
| 468 | + } | ||
| 469 | + *slot = epoch; | ||
| 470 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 471 | + dsb(DSB_DDR); | ||
| 472 | + } | ||
| 473 | + | ||
| 474 | + AICORE inline void WaitDispatchReleaseMte(uint32_t workerCount, int32_t epoch) const | ||
| 475 | + { | ||
| 476 | + const uint32_t count = | ||
| 477 | + workerCount < UNPERMUTE_DISPATCH_RELEASE_SLOT_COUNT ? workerCount : UNPERMUTE_DISPATCH_RELEASE_SLOT_COUNT; | ||
| 478 | + const uint32_t allReadyMask = count >= 32U ? 0xFFFFFFFFU : ((1U << count) - 1U); | ||
| 479 | + __gm__ int32_t* base = const_cast<__gm__ int32_t*>(LocalDispatchReleaseSlot(0U)); | ||
| 480 | + while (ReadEpochMaskMte(base, count, epoch) != allReadyMask) { | ||
| 481 | + RemoteWindowSyncPollBackoff(); | ||
| 482 | + } | ||
| 483 | + AcquireDataReady(); | ||
| 484 | + } | ||
| 485 | + | ||
| 486 | + AICORE inline void WaitSwigluReleaseMte(uint32_t workerCount, int32_t epoch) const | ||
| 487 | + { | ||
| 488 | + const uint32_t count = | ||
| 489 | + workerCount < UNPERMUTE_SWIGLU_RELEASE_SLOT_COUNT ? workerCount : UNPERMUTE_SWIGLU_RELEASE_SLOT_COUNT; | ||
| 490 | + const uint32_t allReadyMask = count >= 32U ? 0xFFFFFFFFU : ((1U << count) - 1U); | ||
| 491 | + __gm__ int32_t* base = const_cast<__gm__ int32_t*>(LocalSwigluReleaseSlot(0U)); | ||
| 492 | + while (ReadEpochMaskMte(base, count, epoch) != allReadyMask) { | ||
| 493 | + RemoteWindowSyncPollBackoff(); | ||
| 494 | + } | ||
| 495 | + AcquireDataReady(); | ||
| 496 | + } | ||
| 497 | + | ||
| 498 | + AICORE inline volatile __gm__ int32_t* LocalPhase1DoneSlot(uint32_t workerIdx) const | ||
| 499 | + { | ||
| 500 | + if (workerIdx >= UNPERMUTE_PHASE1_DONE_SLOT_COUNT) { | ||
| 501 | + return nullptr; | ||
| 502 | + } | ||
| 503 | + return LocalSignalBase() + UNPERMUTE_PHASE1_DONE_BASE_INDEX + workerIdx * UNPERMUTE_PHASE1_DONE_STRIDE; | ||
| 504 | + } | ||
| 505 | + | ||
| 506 | + AICORE inline void PublishPhase1Done(uint32_t workerIdx, int32_t epoch) const | ||
| 507 | + { | ||
| 508 | + volatile __gm__ int32_t* slot = LocalPhase1DoneSlot(workerIdx); | ||
| 509 | + if (slot == nullptr) { | ||
| 510 | + return; | ||
| 511 | + } | ||
| 512 | + *slot = epoch; | ||
| 513 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 514 | + dsb(DSB_DDR); | ||
| 515 | + } | ||
| 516 | + | ||
| 517 | + AICORE inline void WaitPhase1DoneMte(uint32_t workerCount, int32_t epoch) const | ||
| 518 | + { | ||
| 519 | + const uint32_t count = | ||
| 520 | + workerCount < UNPERMUTE_PHASE1_DONE_SLOT_COUNT ? workerCount : UNPERMUTE_PHASE1_DONE_SLOT_COUNT; | ||
| 521 | + const uint32_t allReadyMask = count == 32U ? 0xFFFFFFFFU : ((1U << count) - 1U); | ||
| 522 | + __gm__ int32_t* base = const_cast<__gm__ int32_t*>(LocalPhase1DoneSlot(0U)); | ||
| 523 | + while (ReadEpochMaskMte(base, count, epoch) != allReadyMask) { | ||
| 524 | + RemoteWindowSyncPollBackoff(); | ||
| 525 | + } | ||
| 526 | + AcquireDataReady(); | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + AICORE inline volatile __gm__ int32_t* LocalCombineDoneSlot(uint32_t laneIdx) const | ||
| 530 | + { | ||
| 531 | + if (laneIdx >= COMBINE_LOCAL_DONE_SLOT_COUNT) { | ||
| 532 | + return nullptr; | ||
| 533 | + } | ||
| 534 | + return LocalSignalBase() + COMBINE_LOCAL_DONE_BASE_INDEX + laneIdx * COMBINE_LOCAL_DONE_STRIDE; | ||
| 535 | + } | ||
| 536 | + | ||
| 537 | + AICORE inline void PublishLocalCombineDone(uint32_t laneIdx, int32_t epoch) const | ||
| 538 | + { | ||
| 539 | + volatile __gm__ int32_t* slot = LocalCombineDoneSlot(laneIdx); | ||
| 540 | + if (slot == nullptr) { | ||
| 541 | + return; | ||
| 542 | + } | ||
| 543 | + *slot = epoch; | ||
| 544 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 545 | + __asm__ __volatile__(""); | ||
| 546 | + } | ||
| 547 | + | ||
| 548 | + AICORE inline void WaitLocalCombineDoneMte(uint32_t laneCount, int32_t epoch) const | ||
| 549 | + { | ||
| 550 | + const uint32_t count = laneCount < COMBINE_LOCAL_DONE_SLOT_COUNT ? laneCount : COMBINE_LOCAL_DONE_SLOT_COUNT; | ||
| 551 | + const uint32_t allReadyMask = count >= 32U ? 0xFFFFFFFFU : ((1U << count) - 1U); | ||
| 552 | + __gm__ int32_t* base = const_cast<__gm__ int32_t*>(LocalCombineDoneSlot(0)); | ||
| 553 | + while (ReadEpochMaskMte(base, count, epoch) != allReadyMask) { | ||
| 554 | + RemoteWindowSyncPollBackoff(); | ||
| 555 | + } | ||
| 556 | + pipe_barrier(PIPE_ALL); | ||
| 557 | + dsb(DSB_DDR); | ||
| 558 | + } | ||
| 559 | + | ||
| 77 | AICORE inline void CrossRankSync() const | 560 | AICORE inline void CrossRankSync() const |
| 78 | { | 561 | { |
| 79 | __gm__ int32_t* localSignalBase = LocalSignalBase(); | 562 | __gm__ int32_t* localSignalBase = LocalSignalBase(); |
| @@ -0,0 +1,315 @@ | |||
| 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 | +struct MegaMoeFixedCoreRoleInfo { | ||
| 23 | + uint32_t role = 0U; | ||
| 24 | + uint32_t physicalBlockId = 0U; | ||
| 25 | + uint32_t subblockId = 0U; | ||
| 26 | + uint32_t flatAivId = 0U; | ||
| 27 | + uint32_t groupLocalId = 0U; | ||
| 28 | + uint32_t groupSize = 0U; | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +AICORE inline MegaMoeFixedCoreRoleInfo FixedCoreRole(const __gm__ MegaMoeTilingData* tilingData) | ||
| 32 | +{ | ||
| 33 | + MegaMoeFixedCoreRoleInfo info; | ||
| 34 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData->fixedGroupTiling; | ||
| 35 | + info.physicalBlockId = get_block_idx(); | ||
| 36 | + const bool firstGroup = info.physicalBlockId < fixed.gmm1GroupSize; | ||
| 37 | + if ASCEND_IS_AIC { | ||
| 38 | + if (firstGroup) { | ||
| 39 | + info.role = kMegaMoeFixedRoleGmm1; | ||
| 40 | + info.groupLocalId = info.physicalBlockId; | ||
| 41 | + info.groupSize = fixed.gmm1GroupSize; | ||
| 42 | + } else { | ||
| 43 | + info.role = kMegaMoeFixedRoleGmm2; | ||
| 44 | + info.groupLocalId = info.physicalBlockId - fixed.gmm1GroupSize; | ||
| 45 | + info.groupSize = fixed.gmm2GroupSize; | ||
| 46 | + } | ||
| 47 | + return info; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + info.subblockId = get_subblockid(); | ||
| 51 | + info.flatAivId = info.physicalBlockId + info.subblockId * fixed.physicalAicNum; | ||
| 52 | + if (firstGroup) { | ||
| 53 | + info.groupLocalId = info.physicalBlockId; | ||
| 54 | + if (info.subblockId == 0U) { | ||
| 55 | + info.role = kMegaMoeFixedRoleDispatch; | ||
| 56 | + info.groupSize = fixed.dispatchGroupSize; | ||
| 57 | + } else { | ||
| 58 | + info.role = kMegaMoeFixedRoleSwiglu; | ||
| 59 | + info.groupSize = fixed.swigluGroupSize; | ||
| 60 | + } | ||
| 61 | + } else { | ||
| 62 | + info.role = kMegaMoeFixedRoleCombine; | ||
| 63 | + const uint32_t pairedGmm2LocalId = info.physicalBlockId - fixed.gmm1GroupSize; | ||
| 64 | + info.groupLocalId = pairedGmm2LocalId + info.subblockId * fixed.gmm2GroupSize; | ||
| 65 | + info.groupSize = fixed.combineGroupSize; | ||
| 66 | + } | ||
| 67 | + return info; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +AICORE inline bool FixedRoleActiveForStage(const MegaMoeFixedCoreRoleInfo& info, uint32_t stageNum) | ||
| 71 | +{ | ||
| 72 | + return (info.role == kMegaMoeFixedRoleDispatch && stageNum >= 9U) || | ||
| 73 | + (info.role == kMegaMoeFixedRoleGmm1 && stageNum >= 10U) || | ||
| 74 | + (info.role == kMegaMoeFixedRoleSwiglu && stageNum >= 11U) || | ||
| 75 | + (info.role == kMegaMoeFixedRoleGmm2 && stageNum >= 12U) || | ||
| 76 | + (info.role == kMegaMoeFixedRoleCombine && info.groupLocalId < info.groupSize && stageNum >= 13U); | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +AICORE inline MegaMoeSyncLayout FixedSyncLayout(const __gm__ MegaMoeTilingData* tilingData) | ||
| 80 | +{ | ||
| 81 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData->fixedGroupTiling; | ||
| 82 | + MegaMoeSyncLayout layout; | ||
| 83 | + layout.dispatchArrivalBase = kMegaMoeFixedSyncHeadCanarySlot + 1U; | ||
| 84 | + layout.dispatchReadyBase = layout.dispatchArrivalBase + fixed.dispatchGroupSize; | ||
| 85 | + layout.gmm1ArrivalBase = layout.dispatchReadyBase + fixed.physicalAicNum; | ||
| 86 | + layout.swigluReadyBase = layout.gmm1ArrivalBase + fixed.gmm1GroupSize; | ||
| 87 | + layout.swigluArrivalBase = layout.swigluReadyBase + fixed.swigluGroupSize; | ||
| 88 | + layout.gmm2ReadyBase = layout.swigluArrivalBase + fixed.swigluGroupSize; | ||
| 89 | + layout.gmm2ArrivalBase = layout.gmm2ReadyBase + fixed.gmm2GroupSize; | ||
| 90 | + layout.combineReadyBase = layout.gmm2ArrivalBase + fixed.physicalAicNum; | ||
| 91 | + layout.gmm1DoneSlot = layout.combineReadyBase + fixed.gmm2GroupSize; | ||
| 92 | + layout.gmm2JoinSlot = layout.gmm1DoneSlot + 1U; | ||
| 93 | + layout.tailCanarySlot = layout.gmm2JoinSlot + 1U; | ||
| 94 | + layout.slotCount = layout.tailCanarySlot + 1U; | ||
| 95 | + return layout; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +AICORE inline volatile __gm__ int32_t* FixedSyncSlot( | ||
| 99 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t slot) | ||
| 100 | +{ | ||
| 101 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData->fixedGroupTiling; | ||
| 102 | + return reinterpret_cast<volatile __gm__ int32_t*>( | ||
| 103 | + workspaceGM + fixed.syncOffset + static_cast<uint64_t>(slot) * fixed.syncSlotBytes); | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +// The doorbell is a scalar GM store. The caller drains payload pipelines before publishing. | ||
| 107 | +AICORE inline void PublishScalarEpoch(volatile __gm__ int32_t* slot, int32_t epoch) | ||
| 108 | +{ | ||
| 109 | + *slot = epoch; | ||
| 110 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 111 | + __asm__ __volatile__(""); | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +AICORE inline void PublishScalarEpochRange( | ||
| 115 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t baseSlot, uint32_t count, int32_t epoch) | ||
| 116 | +{ | ||
| 117 | + for (uint32_t localId = 0U; localId < count; ++localId) { | ||
| 118 | + *FixedSyncSlot(workspaceGM, tilingData, baseSlot + localId) = epoch; | ||
| 119 | + } | ||
| 120 | + for (uint32_t localId = 0U; localId < count; ++localId) { | ||
| 121 | + dcci((__gm__ void*)FixedSyncSlot(workspaceGM, tilingData, baseSlot + localId), SINGLE_CACHE_LINE); | ||
| 122 | + } | ||
| 123 | + __asm__ __volatile__(""); | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +AICORE inline void ResetFixedSyncWorkspace(GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData) | ||
| 127 | +{ | ||
| 128 | + const MegaMoeSyncLayout sync = FixedSyncLayout(tilingData); | ||
| 129 | + for (uint32_t slotId = 0U; slotId < sync.slotCount; ++slotId) { | ||
| 130 | + volatile __gm__ int32_t* slot = FixedSyncSlot(workspaceGM, tilingData, slotId); | ||
| 131 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 132 | + __asm__ __volatile__(""); | ||
| 133 | + uint32_t value = 0U; | ||
| 134 | + if (slotId == kMegaMoeFixedSyncHeadCanarySlot) { | ||
| 135 | + value = kMegaMoeFixedHeadCanary; | ||
| 136 | + } else if (slotId == sync.tailCanarySlot) { | ||
| 137 | + value = kMegaMoeFixedTailCanary; | ||
| 138 | + } | ||
| 139 | + *slot = static_cast<int32_t>(value); | ||
| 140 | + } | ||
| 141 | + for (uint32_t slotId = 0U; slotId < sync.slotCount; ++slotId) { | ||
| 142 | + dcci((__gm__ void*)FixedSyncSlot(workspaceGM, tilingData, slotId), SINGLE_CACHE_LINE); | ||
| 143 | + } | ||
| 144 | + dsb(DSB_DDR); | ||
| 145 | + pipe_barrier(PIPE_ALL); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +AICORE inline int32_t ReadScalarEpoch(volatile __gm__ int32_t* slot) | ||
| 149 | +{ | ||
| 150 | + dcci((__gm__ void*)slot, SINGLE_CACHE_LINE); | ||
| 151 | + __asm__ __volatile__(""); | ||
| 152 | + return *slot; | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +AICORE inline bool TestEpoch(volatile __gm__ int32_t* slot, int32_t epoch, int32_t& observed) | ||
| 156 | +{ | ||
| 157 | + observed = ReadScalarEpoch(slot); | ||
| 158 | + return observed >= epoch; | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +AICORE inline void EpochPollBackoff() | ||
| 162 | +{ | ||
| 163 | + constexpr uint32_t kDelayTicks = 3U; | ||
| 164 | + const uint64_t deadline = get_sys_cnt() + kDelayTicks; | ||
| 165 | + while (get_sys_cnt() < deadline) { | ||
| 166 | + __asm__ __volatile__(""); | ||
| 167 | + } | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +AICORE inline int32_t WaitEpochRaw(volatile __gm__ int32_t* slot, int32_t epoch) | ||
| 171 | +{ | ||
| 172 | + while (true) { | ||
| 173 | + int32_t observed = 0; | ||
| 174 | + if (TestEpoch(slot, epoch, observed)) { | ||
| 175 | + return observed; | ||
| 176 | + } | ||
| 177 | + EpochPollBackoff(); | ||
| 178 | + } | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +AICORE inline int32_t WaitEpochAcquire(volatile __gm__ int32_t* slot, int32_t epoch) | ||
| 182 | +{ | ||
| 183 | + const int32_t observed = WaitEpochRaw(slot, epoch); | ||
| 184 | + pipe_barrier(PIPE_ALL); | ||
| 185 | + dsb(DSB_DDR); | ||
| 186 | + return observed; | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +// These snapshot helpers require AIV UB and MTE2/MTE3; AIC callers publish scalar arrivals only. | ||
| 190 | +constexpr uint32_t kMegaMoeSyncSnapshotValuesPerSlot = kMegaMoeFixedSyncSlotBytes / sizeof(int32_t); | ||
| 191 | +constexpr uint32_t kMegaMoeSyncSnapshotMaxValues = kMegaMoeFixedPhysicalAicNum * kMegaMoeSyncSnapshotValuesPerSlot; | ||
| 192 | +constexpr uint64_t kMegaMoeSyncSnapshotUbBytes = static_cast<uint64_t>(kMegaMoeSyncSnapshotMaxValues) * sizeof(int32_t); | ||
| 193 | +constexpr uint64_t kMegaMoeSyncSnapshotUbOffset = AtlasA2::UB_SIZE - kMegaMoeSyncSnapshotUbBytes; | ||
| 194 | +constexpr event_t kMegaMoeSyncSnapshotEvent = EVENT_ID0; | ||
| 195 | + | ||
| 196 | +static_assert(kMegaMoeFixedSyncSlotBytes % sizeof(int32_t) == 0U); | ||
| 197 | +static_assert(kMegaMoeSyncSnapshotUbOffset % UB_ALIGN == 0U); | ||
| 198 | + | ||
| 199 | +AICORE inline int32_t ReadArrivalMinMte( | ||
| 200 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t arrivalBaseSlot, uint32_t producerCount) | ||
| 201 | +{ | ||
| 202 | + if (producerCount == 0U || producerCount > kMegaMoeFixedPhysicalAicNum) { | ||
| 203 | + return 0; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + using SnapshotShape = pto::Shape<1, 1, 1, 1, pto::DYNAMIC>; | ||
| 207 | + using SnapshotStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | ||
| 208 | + using SnapshotGlobal = pto::GlobalTensor<int32_t, SnapshotShape, SnapshotStride, pto::Layout::ND>; | ||
| 209 | + using SnapshotTile = | ||
| 210 | + pto::Tile<pto::TileType::Vec, int32_t, 1, kMegaMoeSyncSnapshotMaxValues, pto::BLayout::RowMajor, -1, -1>; | ||
| 211 | + | ||
| 212 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData->fixedGroupTiling; | ||
| 213 | + __gm__ int32_t* arrivalBase = reinterpret_cast<__gm__ int32_t*>( | ||
| 214 | + workspaceGM + fixed.syncOffset + static_cast<uint64_t>(arrivalBaseSlot) * fixed.syncSlotBytes); | ||
| 215 | + const uint32_t snapshotValues = producerCount * kMegaMoeSyncSnapshotValuesPerSlot; | ||
| 216 | + SnapshotShape snapshotShape(snapshotValues); | ||
| 217 | + SnapshotStride snapshotStride(snapshotValues, snapshotValues, snapshotValues, snapshotValues); | ||
| 218 | + SnapshotGlobal snapshotGlobal(arrivalBase, snapshotShape, snapshotStride); | ||
| 219 | + SnapshotTile snapshotTile(1U, snapshotValues); | ||
| 220 | + pto::TASSIGN(snapshotTile, kMegaMoeSyncSnapshotUbOffset); | ||
| 221 | + pto::TLOAD(snapshotTile, snapshotGlobal); | ||
| 222 | + pto::PtoSetWaitFlag<PIPE_MTE2, PIPE_S>(kMegaMoeSyncSnapshotEvent, kMegaMoeSyncSnapshotEvent); | ||
| 223 | + | ||
| 224 | + int32_t observedMin = snapshotTile.GetValue(0U); | ||
| 225 | + for (uint32_t peer = 1U; peer < producerCount; ++peer) { | ||
| 226 | + const int32_t observed = snapshotTile.GetValue(peer * kMegaMoeSyncSnapshotValuesPerSlot); | ||
| 227 | + observedMin = observed < observedMin ? observed : observedMin; | ||
| 228 | + } | ||
| 229 | + return observedMin; | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +AICORE inline int32_t WaitArrivalMinMte( | ||
| 233 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t arrivalBaseSlot, uint32_t producerCount, | ||
| 234 | + int32_t epoch) | ||
| 235 | +{ | ||
| 236 | + while (true) { | ||
| 237 | + const int32_t observedMin = ReadArrivalMinMte(workspaceGM, tilingData, arrivalBaseSlot, producerCount); | ||
| 238 | + if (observedMin >= epoch) { | ||
| 239 | + return observedMin; | ||
| 240 | + } | ||
| 241 | + EpochPollBackoff(); | ||
| 242 | + } | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +AICORE inline void PublishEpochRangeMte( | ||
| 246 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t baseSlot, uint32_t count, int32_t epoch) | ||
| 247 | +{ | ||
| 248 | + if (count == 0U || count > kMegaMoeFixedPhysicalAicNum) { | ||
| 249 | + return; | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + using SnapshotShape = pto::Shape<1, 1, 1, 1, pto::DYNAMIC>; | ||
| 253 | + using SnapshotStride = pto::Stride<pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, pto::DYNAMIC, 1>; | ||
| 254 | + using SnapshotGlobal = pto::GlobalTensor<int32_t, SnapshotShape, SnapshotStride, pto::Layout::ND>; | ||
| 255 | + using SnapshotTile = | ||
| 256 | + pto::Tile<pto::TileType::Vec, int32_t, 1, kMegaMoeSyncSnapshotMaxValues, pto::BLayout::RowMajor, -1, -1>; | ||
| 257 | + | ||
| 258 | + const __gm__ MegaMoeFixedGroupTiling& fixed = tilingData->fixedGroupTiling; | ||
| 259 | + __gm__ int32_t* readyBase = reinterpret_cast<__gm__ int32_t*>( | ||
| 260 | + workspaceGM + fixed.syncOffset + static_cast<uint64_t>(baseSlot) * fixed.syncSlotBytes); | ||
| 261 | + const uint32_t snapshotValues = count * kMegaMoeSyncSnapshotValuesPerSlot; | ||
| 262 | + SnapshotShape snapshotShape(snapshotValues); | ||
| 263 | + SnapshotStride snapshotStride(snapshotValues, snapshotValues, snapshotValues, snapshotValues); | ||
| 264 | + SnapshotGlobal snapshotGlobal(readyBase, snapshotShape, snapshotStride); | ||
| 265 | + SnapshotTile snapshotTile(1U, snapshotValues); | ||
| 266 | + pto::TASSIGN(snapshotTile, kMegaMoeSyncSnapshotUbOffset); | ||
| 267 | + for (uint32_t localId = 0U; localId < count; ++localId) { | ||
| 268 | + snapshotTile.SetValue(localId * kMegaMoeSyncSnapshotValuesPerSlot, epoch); | ||
| 269 | + } | ||
| 270 | + pto::PtoSetWaitFlag<PIPE_S, PIPE_MTE3>(kMegaMoeSyncSnapshotEvent, kMegaMoeSyncSnapshotEvent); | ||
| 271 | + pto::TSTORE(snapshotGlobal, snapshotTile); | ||
| 272 | + pto::PtoSetWaitFlag<PIPE_MTE3, PIPE_S>(kMegaMoeSyncSnapshotEvent, kMegaMoeSyncSnapshotEvent); | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +AICORE inline void PublishGroupArrival( | ||
| 276 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t arrivalBaseSlot, uint32_t localId, | ||
| 277 | + uint32_t notifyCall) | ||
| 278 | +{ | ||
| 279 | + const int32_t epoch = static_cast<int32_t>(notifyCall * 2U + 1U); | ||
| 280 | + pipe_barrier(PIPE_ALL); | ||
| 281 | + dsb(DSB_DDR); | ||
| 282 | + PublishScalarEpoch(FixedSyncSlot(workspaceGM, tilingData, arrivalBaseSlot + localId), epoch); | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +AICORE inline void CoordinateGroupConsumersMte( | ||
| 286 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t arrivalBaseSlot, uint32_t readyBaseSlot, | ||
| 287 | + uint32_t producerCount, uint32_t consumerCount, uint32_t notifyCall) | ||
| 288 | +{ | ||
| 289 | + const int32_t arriveEpoch = static_cast<int32_t>(notifyCall * 2U + 1U); | ||
| 290 | + const int32_t releaseEpoch = arriveEpoch + 1; | ||
| 291 | + WaitArrivalMinMte(workspaceGM, tilingData, arrivalBaseSlot, producerCount, arriveEpoch); | ||
| 292 | + pipe_barrier(PIPE_ALL); | ||
| 293 | + dsb(DSB_DDR); | ||
| 294 | + PublishEpochRangeMte(workspaceGM, tilingData, readyBaseSlot, consumerCount, releaseEpoch); | ||
| 295 | +} | ||
| 296 | + | ||
| 297 | +AICORE inline void NotifyGroupConsumersMte( | ||
| 298 | + GM_ADDR workspaceGM, const __gm__ MegaMoeTilingData* tilingData, uint32_t arrivalBaseSlot, uint32_t readyBaseSlot, | ||
| 299 | + uint32_t producerCount, uint32_t consumerCount, uint32_t localId, uint32_t coordinatorLocalId, uint32_t notifyCall) | ||
| 300 | +{ | ||
| 301 | + const int32_t arriveEpoch = static_cast<int32_t>(notifyCall * 2U + 1U); | ||
| 302 | + const int32_t releaseEpoch = arriveEpoch + 1; | ||
| 303 | + pipe_barrier(PIPE_ALL); | ||
| 304 | + dsb(DSB_DDR); | ||
| 305 | + PublishScalarEpoch(FixedSyncSlot(workspaceGM, tilingData, arrivalBaseSlot + localId), arriveEpoch); | ||
| 306 | + | ||
| 307 | + if (localId == coordinatorLocalId) { | ||
| 308 | + WaitArrivalMinMte(workspaceGM, tilingData, arrivalBaseSlot, producerCount, arriveEpoch); | ||
| 309 | + pipe_barrier(PIPE_ALL); | ||
| 310 | + dsb(DSB_DDR); | ||
| 311 | + PublishEpochRangeMte(workspaceGM, tilingData, readyBaseSlot, consumerCount, releaseEpoch); | ||
| 312 | + } | ||
| 313 | +} | ||
| 314 | + | ||
| 315 | + | ||