Merged
[9.2.0] refactor/perf: 回合算子迁移清理与 Sort 性能优化 #5809
[9.2.0] refactor/perf: 回合算子迁移清理与 Sort 性能优化 #5809
Merged
黄晓彬Add on Sep 18
Total: 81 files changed+1864-9919
@@ -633,9 +633,6 @@ sort@ops-math:
633 - ops/ops-math/math/sort/633 - ops/ops-math/math/sort/
634 - ops/ops-math/math/sort_with_index/634 - ops/ops-math/math/sort_with_index/
635 - ops/ops-math/math/top_k_v2/635 - ops/ops-math/math/top_k_v2/
636- - ops/ops-math/math/kth_value/
637- - ops/ops-math/math/median/
638- - ops/ops-math/math/nan_median/
639 opensource_style: null636 opensource_style: null
640 kernel_style: null637 kernel_style: null
641 unrelease:638 unrelease:
@@ -646,8 +643,6 @@ sort@ops-math:
646 - ops/ops-math/math/sort_with_index/tests/643 - ops/ops-math/math/sort_with_index/tests/
647 - ops/ops-math/math/top_k_v2/examples/644 - ops/ops-math/math/top_k_v2/examples/
648 - ops/ops-math/math/top_k_v2/tests/645 - ops/ops-math/math/top_k_v2/tests/
649- - ops/ops-math/math/median/tests/
650- - ops/ops-math/math/nan_median/tests/
651 non_delivery: null646 non_delivery: null
652 open_source: null647 open_source: null
653 648 
@@ -1,23 +0,0 @@
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. Please see LICENSE for the full license text.
6-# This software is provided on an "AS IS" basis, without warranties.
7-# ----------------------------------------------------------------------------
8- 
9-# 设置算子定义时支持的芯片类型
10-set(SUPPORT_COMPUTE_UNIT "ascend950")
11-# 设置每种芯片类型对应的tiling文件目录,即采用op_host目录下哪个文件夹下的tiling文件编译
12-set(SUPPORT_TILING_DIR "arch35")
13-add_all_modules_sources(OPTYPE kth_value ACLNNTYPE aclnn_exclude
14- DEPENDENCIES sort
15- COMPUTE_UNIT ${SUPPORT_COMPUTE_UNIT}
16- TILING_DIR ${SUPPORT_TILING_DIR}
17- DISABLE_IN_OPP TRUE)
18- 
19-# Keep graph_base loaded for the Host UT shared library under --as-needed linkers.
20-if(OP_HOST_UT AND TARGET ${OP_TILING_MODULE_NAME}_cases_obj)
21- target_link_libraries(${OP_TILING_MODULE_NAME}_cases_obj INTERFACE
22- "-Wl,--no-as-needed,$<TARGET_FILE:graph_base>,--as-needed")
23-endif()
@@ -1,108 +0,0 @@
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-#include "kth_value.h"
12- 
13-#include "op_api/aclnn_check.h"
14-#include "opdev/make_op_executor.h"
15-#include "opdev/op_dfx.h"
16-#include "opdev/op_executor.h"
17-#include "opdev/op_log.h"
18- 
19-#include <initializer_list>
20- 
21-using namespace op;
22- 
23-namespace l0op {
24-OP_TYPE_REGISTER(KthValue);
25- 
26-static const std::initializer_list<op::DataType> KTH_VALUE_DTYPE_SUPPORT_LIST = {
27- op::DataType::DT_FLOAT16, op::DataType::DT_FLOAT, op::DataType::DT_INT16, op::DataType::DT_INT8,
28- op::DataType::DT_UINT8, op::DataType::DT_INT32, op::DataType::DT_INT64, op::DataType::DT_BF16,
29- op::DataType::DT_UINT32, op::DataType::DT_UINT16, op::DataType::DT_UINT64};
30- 
31-static constexpr int64_t NON_LAST_SMALL_AXIS_MIN = 2;
32-static constexpr int64_t NON_LAST_SMALL_AXIS_MAX = 2048;
33- 
34-static bool IsLastAxisOrSupportedNonLastAxis(int64_t rank, int64_t normDim, int64_t axisLen)
35-{
36- if (normDim == rank - 1) {
37- return true;
38- }
39- return axisLen >= NON_LAST_SMALL_AXIS_MIN && axisLen <= NON_LAST_SMALL_AXIS_MAX;
40-}
41- 
42-static bool CheckParams(const aclTensor* self, int64_t k, int64_t dim)
43-{
44- OP_CHECK_NULL(self, return false);
45- OP_CHECK_DTYPE_NOT_SUPPORT(self, KTH_VALUE_DTYPE_SUPPORT_LIST, return false);
46- auto rank = static_cast<int64_t>(self->GetViewShape().GetDimNum());
47- if (rank <= 0) {
48- OP_LOGE(ACLNN_ERR_PARAM_INVALID, "kth_value only supports tensor with rank > 0.");
49- return false;
50- }
51- if (dim < -rank || dim >= rank) {
52- OP_LOGE(ACLNN_ERR_PARAM_INVALID, "dim should be in range [%ld, %ld].", -rank, rank - 1);
53- return false;
54- }
55- auto normDim = dim < 0 ? dim + rank : dim;
56- auto axisLen = static_cast<int64_t>(self->GetViewShape().GetDim(static_cast<size_t>(normDim)));
57- if (axisLen <= 0) {
58- OP_LOGE(ACLNN_ERR_PARAM_INVALID, "kth_value axis length must be greater than 0.");
59- return false;
60- }
61- if (!IsLastAxisOrSupportedNonLastAxis(rank, normDim, axisLen)) {
62- OP_LOGE(
63- ACLNN_ERR_PARAM_INVALID,
64- "Dim must be the last axis or a supported non-last axis with axis size in [2, 2048]. Current dim is %ld.",
65- dim);
66- return false;
67- }
68- if (k < 1 || k > axisLen) {
69- OP_LOGE(ACLNN_ERR_PARAM_INVALID, "k should be in range [1, %ld].", axisLen);
70- return false;
71- }
72- return true;
73-}
74- 
75-static op::Shape MakeKthShape(const op::Shape& shape, int64_t dim)
76-{
77- auto outShape = shape;
78- outShape.SetDim(static_cast<size_t>(dim), 1);
79- return outShape;
80-}
81- 
82-static std::tuple<aclTensor*, aclTensor*> KthValueAiCore(const aclTensor* self, int64_t k, int64_t dim,
83- aclOpExecutor* executor)
84-{
85- auto rank = static_cast<int64_t>(self->GetViewShape().GetDimNum());
86- auto normDim = dim < 0 ? dim + rank : dim;
87- auto outShape = MakeKthShape(self->GetViewShape(), normDim);
88- auto values = executor->AllocTensor(outShape, self->GetDataType(), op::Format::FORMAT_ND);
89- auto indices = executor->AllocTensor(outShape, op::DataType::DT_INT64, op::Format::FORMAT_ND);
90- OP_CHECK_NULL(values, return {});
91- OP_CHECK_NULL(indices, return {});
92- 
93- auto ret = ADD_TO_LAUNCHER_LIST_AICORE(KthValue, OP_INPUT(self), OP_OUTPUT(values, indices), OP_ATTR(k, dim));
94- OP_CHECK(ret == ACLNN_SUCCESS, OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "KthValue ADD_TO_LAUNCHER_LIST_AICORE failed."),
95- return {});
96- return std::tie(values, indices);
97-}
98- 
99-const std::tuple<aclTensor*, aclTensor*> KthValue(const aclTensor* self, int64_t k, int64_t dim,
100- aclOpExecutor* executor)
101-{
102- L0_DFX(KthValue, self, k, dim);
103- if (!CheckParams(self, k, dim)) {
104- return {};
105- }
106- return KthValueAiCore(self, k, dim, executor);
107-}
108-} // namespace l0op
@@ -1,21 +0,0 @@
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-#ifndef PTA_NPU_OP_API_INC_LEVEL0_OP_KTH_VALUE_OP_H_
12-#define PTA_NPU_OP_API_INC_LEVEL0_OP_KTH_VALUE_OP_H_
13- 
14-#include "opdev/op_executor.h"
15- 
16-namespace l0op {
17-const std::tuple<aclTensor*, aclTensor*> KthValue(
18- const aclTensor* self, int64_t k, int64_t dim, aclOpExecutor* executor);
19-}
20- 
21-#endif // PTA_NPU_OP_API_INC_LEVEL0_OP_KTH_VALUE_OP_H_
@@ -1,33 +0,0 @@
1-/**
2- * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10- 
11-/*!
12- * \file kth_value_tiling_arch35.h
13- * \brief kth_value tiling
14- */
15-#ifndef AIR_CXX_RUNTIME_V2_OP_IMPL_KTH_VALUE_TILING_ARCH35_H
16-#define AIR_CXX_RUNTIME_V2_OP_IMPL_KTH_VALUE_TILING_ARCH35_H
17- 
18-#include "register/op_def_registry.h"
19-#include "register/op_impl_registry.h"
20-#include "register/tilingdata_base.h"
21- 
22-namespace optiling {
23-struct KthValueCompileInfo {
24- int32_t coreNum;
25-};
26- 
27-ge::graphStatus Tiling4Median(gert::TilingContext* context);
28-ge::graphStatus TilingPrepare4Median(gert::TilingParseContext* context);
29-ge::graphStatus Tiling4NanMedian(gert::TilingContext* context);
30-ge::graphStatus TilingPrepare4NanMedian(gert::TilingParseContext* context);
31-} // namespace optiling
32- 
33-#endif // AIR_CXX_RUNTIME_V2_OP_IMPL_KTH_VALUE_TILING_ARCH35_H
@@ -1,544 +0,0 @@
1-{
2- "op_type": "KthValue",
3- "op_list": [
4- {
5- "bin_filename": "KthValue_x_float16_indices_int64",
6- "inputs": [
7- {
8- "name": "x",
9- "index": 0,
10- "dtype": "float16",
11- "format": "ND",
12- "paramType": "required",
13- "shape": [
14- -2
15- ]
16- }
17- ],
18- "outputs": [
19- {
20- "name": "y",
21- "index": 0,
22- "dtype": "float16",
23- "format": "ND",
24- "paramType": "required",
25- "shape": [
26- -2
27- ]
28- },
29- {
30- "name": "indices",
31- "index": 1,
32- "dtype": "int64",
33- "format": "ND",
34- "paramType": "required",
35- "shape": [
36- -2
37- ]
38- }
39- ],
40- "attrs": [
41- {
42- "name": "k",
43- "dtype": "int",
44- "value": null
45- },
46- {
47- "name": "dim",
48- "dtype": "int",
49- "value": null
50- }
51- ]
52- },
53- {
54- "bin_filename": "KthValue_x_float32_indices_int64",
55- "inputs": [
56- {
57- "name": "x",
58- "index": 0,
59- "dtype": "float32",
60- "format": "ND",
61- "paramType": "required",
62- "shape": [
63- -2
64- ]
65- }
66- ],
67- "outputs": [
68- {
69- "name": "y",
70- "index": 0,
71- "dtype": "float32",
72- "format": "ND",
73- "paramType": "required",
74- "shape": [
75- -2
76- ]
77- },
78- {
79- "name": "indices",
80- "index": 1,
81- "dtype": "int64",
82- "format": "ND",
83- "paramType": "required",
84- "shape": [
85- -2
86- ]
87- }
88- ],
89- "attrs": [
90- {
91- "name": "k",
92- "dtype": "int",
93- "value": null
94- },
95- {
96- "name": "dim",
97- "dtype": "int",
98- "value": null
99- }
100- ]
@@ -1,3 +0,0 @@
1-; 该文件主要影响 opc 工具 编译二进制kernel时, --simplified_key_mode 选项中填写的值。
2-[KthValue]
3-default=0
@@ -1,61 +0,0 @@
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-#include "register/op_def_registry.h"
12- 
13-namespace ops {
14-static const std::vector<ge::DataType> DataTypeXAndValues = {ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_INT16, ge::DT_INT8,
15- ge::DT_UINT8, ge::DT_INT32, ge::DT_INT64, ge::DT_BF16,
16- ge::DT_UINT32, ge::DT_UINT16, ge::DT_UINT64};
17- 
18-static const std::vector<ge::DataType> DataTypeIndices = {ge::DT_INT64, ge::DT_INT64, ge::DT_INT64, ge::DT_INT64,
19- ge::DT_INT64, ge::DT_INT64, ge::DT_INT64, ge::DT_INT64,
20- ge::DT_INT64, ge::DT_INT64, ge::DT_INT64};
21- 
22-static const std::vector<ge::Format> FormatNd = {ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND,
23- ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND,
24- ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND};
25- 
26-class KthValue : public OpDef {
27-public:
28- explicit KthValue(const char* name) : OpDef(name)
29- {
30- this->Input("x")
31- .ParamType(REQUIRED)
32- .DataType(DataTypeXAndValues)
33- .Format(FormatNd)
34- .UnknownShapeFormat(FormatNd);
35- this->Output("y")
36- .ParamType(REQUIRED)
37- .DataType(DataTypeXAndValues)
38- .Format(FormatNd)
39- .UnknownShapeFormat(FormatNd);
40- this->Output("indices")
41- .ParamType(REQUIRED)
42- .DataType(DataTypeIndices)
43- .Format(FormatNd)
44- .UnknownShapeFormat(FormatNd);
45- this->Attr("k").AttrType(REQUIRED).Int();
46- this->Attr("dim").AttrType(OPTIONAL).Int(-1);
47- 
48- OpAICoreConfig aicoreConfig;
49- aicoreConfig.DynamicCompileStaticFlag(true)
50- .DynamicFormatFlag(false)
51- .DynamicRankSupportFlag(true)
52- .DynamicShapeSupportFlag(true)
53- .NeedCheckSupportFlag(false)
54- .ExtendCfgInfo("opFile.value", "kth_value_apt")
55- .ExtendCfgInfo("opInterface.value", "kth_value");
56- this->AICore().AddConfig("ascend950", aicoreConfig);
57- }
58-};
59- 
60-OP_ADD(KthValue);
61-} // namespace ops
@@ -1,56 +0,0 @@
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-#include <string>
12- 
13-#include "register/op_impl_registry.h"
14-#include "log/log.h"
15-#include "util/shape_util.h"
16- 
17-namespace ops {
18-static ge::graphStatus KthValueInferShapeFunc(gert::InferShapeContext* context)
19-{
20- const gert::Shape* xShape = context->GetInputShape(0);
21- OP_CHECK_NULL_WITH_CONTEXT(context, xShape);
22- auto* valuesShape = context->GetOutputShape(0);
23- auto* indicesShape = context->GetOutputShape(1);
24- OP_CHECK_NULL_WITH_CONTEXT(context, valuesShape);
25- OP_CHECK_NULL_WITH_CONTEXT(context, indicesShape);
26- 
27- if (Ops::Base::IsUnknownRank(*xShape)) {
28- Ops::Base::SetUnknownRank(*valuesShape);
29- Ops::Base::SetUnknownRank(*indicesShape);
30- return ge::GRAPH_SUCCESS;
31- }
32- 
33- const int64_t rank = static_cast<int64_t>(xShape->GetDimNum());
34- if (rank <= 0) {
35- return ge::GRAPH_FAILED;
36- }
37- auto attrs = context->GetAttrs();
38- OP_CHECK_NULL_WITH_CONTEXT(context, attrs);
39- const int64_t* dimAttr = attrs->GetAttrPointer<int64_t>(1);
40- int64_t dim = (dimAttr == nullptr) ? -1 : *dimAttr;
41- int64_t normDim = dim < 0 ? dim + rank : dim;
42- if (normDim < 0 || normDim >= rank) {
43- std::string dimValue = std::to_string(dim);
44- std::string dimRange = "[" + std::to_string(-rank) + ", " + std::to_string(rank - 1) + "]";
45- OP_LOGE_WITH_INVALID_ATTR(context->GetNodeName(), "dim", dimValue.c_str(), dimRange.c_str());
46- return ge::GRAPH_FAILED;
47- }
48- *valuesShape = *xShape;
49- *indicesShape = *xShape;
50- valuesShape->SetDim(normDim, 1);
51- indicesShape->SetDim(normDim, 1);
52- return ge::GRAPH_SUCCESS;
53-}
54- 
55-IMPL_OP_INFERSHAPE(KthValue).InferShape(KthValueInferShapeFunc);
56-} // namespace ops
@@ -1,127 +0,0 @@
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-#ifndef KTH_VALUE_AXIS_ONE_COPY_H
12-#define KTH_VALUE_AXIS_ONE_COPY_H
13- 
14-#include "kernel_operator.h"
15-#include "kth_value_tiling_data.h"
16- 
17-namespace KthValue {
18-using namespace AscendC;
19- 
20-template <typename T>
21-class KthValueAxisOneCopy {
22-public:
23- __aicore__ inline KthValueAxisOneCopy() {}
24- __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR idx, GM_ADDR workspace,
25- const KthValueTilingData *tilingData, TPipe *pipe);
26- __aicore__ inline void Process();
27- 
28-private:
29- __aicore__ inline void CopyValue(int64_t offset, uint32_t validElems);
30- __aicore__ inline void CopyIndex(int64_t offset, uint32_t validElems);
31- 
32- static constexpr uint32_t BUFFER_NUM = 2;
33- 
34- TQueBind<QuePosition::VECIN, QuePosition::VECOUT, BUFFER_NUM> valueQueue_;
35- TQue<QuePosition::VECOUT, BUFFER_NUM> idxQueue_;
36- 
37- GlobalTensor<T> inputXGm_;
38- GlobalTensor<T> outValueGm_;
39- GlobalTensor<int64_t> outIdxGm_;
40- 
41- uint32_t blockIdx_ = 0;
42- uint32_t blockDim_ = 0;
43- uint32_t copyElemsPerLoop_ = 0;
44- uint32_t loopTimes_ = 0;
45- int64_t totalElems_ = 0;
46-};
47- 
48-template <typename T>
49-__aicore__ inline void KthValueAxisOneCopy<T>::Init(GM_ADDR x, GM_ADDR y, GM_ADDR idx, GM_ADDR workspace,
50- const KthValueTilingData *tilingData, TPipe *pipe)
51-{
52- (void)workspace;
53- if (tilingData == nullptr || pipe == nullptr) {
54- return;
55- }
56- 
57- blockIdx_ = GetBlockIdx();
58- blockDim_ = GetBlockNum();
59- copyElemsPerLoop_ = tilingData->keyParams0;
60- loopTimes_ = tilingData->keyParams1;
61- totalElems_ = tilingData->unsortedDimNum * tilingData->lastAxisNum;
62- 
63- inputXGm_.SetGlobalBuffer((__gm__ T *)x);
64- outValueGm_.SetGlobalBuffer((__gm__ T *)y);
65- outIdxGm_.SetGlobalBuffer((__gm__ int64_t *)idx);
66- 
67- if (copyElemsPerLoop_ == 0) {
68- return;
69- }
70- 
71- pipe->InitBuffer(valueQueue_, BUFFER_NUM, copyElemsPerLoop_ * sizeof(T));
72- pipe->InitBuffer(idxQueue_, BUFFER_NUM, copyElemsPerLoop_ * sizeof(int64_t));
73-}
74- 
75-template <typename T>
76-__aicore__ inline void KthValueAxisOneCopy<T>::CopyValue(int64_t offset, uint32_t validElems)
77-{
78- LocalTensor<T> valueLocal = valueQueue_.AllocTensor<T>();
79- DataCopyExtParams copyParam{ 1, static_cast<uint32_t>(validElems * sizeof(T)), 0, 0, 0 };
80- DataCopyPadExtParams<T> padParams{ false, 0, 0, 0 };
81- DataCopyPad(valueLocal, inputXGm_[offset], copyParam, padParams);
82- valueQueue_.EnQue(valueLocal);
83- 
84- valueLocal = valueQueue_.DeQue<T>();
85- DataCopyPad(outValueGm_[offset], valueLocal, copyParam);
86- valueQueue_.FreeTensor(valueLocal);
87-}
88- 
89-template <typename T>
90-__aicore__ inline void KthValueAxisOneCopy<T>::CopyIndex(int64_t offset, uint32_t validElems)
91-{
92- LocalTensor<int64_t> idxLocal = idxQueue_.AllocTensor<int64_t>();
93- Duplicate(idxLocal, static_cast<int64_t>(0), validElems);
94- idxQueue_.EnQue(idxLocal);
95- 
96- idxLocal = idxQueue_.DeQue<int64_t>();
97- DataCopyExtParams copyParam{ 1, static_cast<uint32_t>(validElems * sizeof(int64_t)), 0, 0, 0 };
98- DataCopyPad(outIdxGm_[offset], idxLocal, copyParam);
99- idxQueue_.FreeTensor(idxLocal);
100-}
101- 
102-template <typename T>
103-__aicore__ inline void KthValueAxisOneCopy<T>::Process()
104-{
105- if (blockIdx_ >= blockDim_ || blockDim_ == 0 || copyElemsPerLoop_ == 0 || totalElems_ <= 0) {
106- return;
107- }
108- 
109- uint32_t loopsPerCore = (loopTimes_ + blockDim_ - 1U) / blockDim_;
110- uint32_t startLoop = blockIdx_ * loopsPerCore;
111- uint32_t endLoop = (startLoop + loopsPerCore < loopTimes_) ? (startLoop + loopsPerCore) : loopTimes_;
112- for (uint32_t loopId = startLoop; loopId < endLoop; ++loopId) {
113- int64_t offset = static_cast<int64_t>(loopId) * static_cast<int64_t>(copyElemsPerLoop_);
114- int64_t remain = totalElems_ - offset;
115- if (remain <= 0) {
116- continue;
117- }
118- uint32_t validElems = remain >= static_cast<int64_t>(copyElemsPerLoop_) ?
119- copyElemsPerLoop_ : static_cast<uint32_t>(remain);
120- CopyValue(offset, validElems);
121- CopyIndex(offset, validElems);
122- }
123-}
124- 
125-} // namespace KthValue
126- 
127-#endif
@@ -1,267 +0,0 @@
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-#ifndef KTH_VALUE_KERNEL_DISPATCH_H
12-#define KTH_VALUE_KERNEL_DISPATCH_H
13- 
14-#include "kth_value_axis_one_copy.h"
15-#include "kth_value_median_utils.h"
16-#include "kth_value_merge_intra_core.h"
17-#include "kth_value_merge_sort_one_core.h"
18-#include "kth_value_merge_sort_more_core.h"
19-#include "kth_value_non_last_small_axis.h"
20-#include "kth_value_radix_more_core.h"
21-#include "kth_value_radix_one_core.h"
22-#include "kth_value_radix_select.h"
23-#include "kth_value_small_axis_insertion.h"
24-#include "kth_value_small_axis_short_rank_select.h"
25-#include "kth_value_small_axis_two_stage.h"
26-#include "kth_value_tiling_data.h"
27-#include "kth_value_tiling_key.h"
28- 
29-namespace KthValue {
30-using namespace AscendC;
31- 
32-template <bool EnableMedian, uint64_t schId>
33-__aicore__ inline void RunMergeSortRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, KthValueTilingData* tilingData, TPipe* pipe)
34-{
35- constexpr uint64_t isSort32SmallAxis = (schId == KTH_VALUE_SCHID_SORT32_SMALL_AXIS);
36- if constexpr (IsSameType<bfloat16_t, DTYPE_X>::value) {
37- KthValueMergeSortOneCore<DTYPE_X, float, isSort32SmallAxis, EnableMedian> op;
38- op.Init(x, y1, y2, tilingData, pipe);
39- op.Process();
40- } else if constexpr (IsSameType<float, DTYPE_X>::value || IsSameType<half, DTYPE_X>::value) {
41- KthValueMergeSortOneCore<DTYPE_X, DTYPE_X, isSort32SmallAxis, EnableMedian> op;
42- op.Init(x, y1, y2, tilingData, pipe);
43- op.Process();
44- }
45-}
46- 
47-template <bool EnableMedian, uint64_t isInt32>
48-__aicore__ inline void RunRadixMoreCoreRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace,
49- KthValueTilingData* tilingData, TPipe* pipe)
50-{
51- constexpr bool enableNanMode = EnableMedian && IS_MEDIAN_FLOAT_TYPE<DTYPE_X>;
52- if constexpr (sizeof(DTYPE_X) == sizeof(uint8_t)) {
53- if constexpr (isInt32 == 1) {
54- KthValueRadixMoreCore<DTYPE_X, uint32_t, uint8_t, enableNanMode> op;
55- op.Init(x, y1, y2, workspace, tilingData, pipe);
56- op.Process();
57- } else {
58- KthValueRadixMoreCore<DTYPE_X, int64_t, uint8_t, enableNanMode> op;
59- op.Init(x, y1, y2, workspace, tilingData, pipe);
60- op.Process();
61- }
62- } else if constexpr (sizeof(DTYPE_X) == sizeof(uint16_t)) {
63- if constexpr (isInt32 == 1) {
64- KthValueRadixMoreCore<DTYPE_X, uint32_t, uint16_t, enableNanMode> op;
65- op.Init(x, y1, y2, workspace, tilingData, pipe);
66- op.Process();
67- } else {
68- KthValueRadixMoreCore<DTYPE_X, int64_t, uint16_t, enableNanMode> op;
69- op.Init(x, y1, y2, workspace, tilingData, pipe);
70- op.Process();
71- }
72- } else if constexpr (sizeof(DTYPE_X) == sizeof(uint32_t)) {
73- if constexpr (isInt32 == 1) {
74- KthValueRadixMoreCore<DTYPE_X, uint32_t, uint32_t, enableNanMode> op;
75- op.Init(x, y1, y2, workspace, tilingData, pipe);
76- op.Process();
77- } else {
78- KthValueRadixMoreCore<DTYPE_X, int64_t, uint32_t, enableNanMode> op;
79- op.Init(x, y1, y2, workspace, tilingData, pipe);
80- op.Process();
81- }
82- } else if constexpr (sizeof(DTYPE_X) == sizeof(uint64_t)) {
83- if constexpr (isInt32 == 1) {
84- KthValueRadixMoreCore<DTYPE_X, uint32_t, uint64_t, enableNanMode> op;
85- op.Init(x, y1, y2, workspace, tilingData, pipe);
86- op.Process();
87- } else {
88- KthValueRadixMoreCore<DTYPE_X, int64_t, uint64_t, enableNanMode> op;
89- op.Init(x, y1, y2, workspace, tilingData, pipe);
90- op.Process();
91- }
92- }
93-}
94- 
95-template <bool EnableMedian>
96-__aicore__ inline void RunSmallAxisInsertionRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, KthValueTilingData* tilingData,
97- TPipe* pipe)
98-{
99- constexpr bool enableNanMode = EnableMedian && IS_MEDIAN_FLOAT_TYPE<DTYPE_X>;
100- if constexpr (IsSameType<bfloat16_t, DTYPE_X>::value) {
101- KthValueSmallAxisInsertion<DTYPE_X, float, enableNanMode> op;
102- op.Init(x, y1, y2, tilingData, pipe);
103- op.Process();
104- } else {
105- KthValueSmallAxisInsertion<DTYPE_X, DTYPE_X, enableNanMode> op;
106- op.Init(x, y1, y2, tilingData, pipe);
107- op.Process();
108- }
109-}
110- 
111-template <bool EnableMedian>
112-__aicore__ inline void RunMergeMoreCoreRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace,
113- KthValueTilingData* tilingData, TPipe* pipe)
114-{
115- if constexpr (IsSameType<float, DTYPE_X>::value) {
116- KthValueMergeSortMoreCore<DTYPE_X, DTYPE_X, false, int64_t, EnableMedian> op;
117- op.Init(x, y1, y2, workspace, tilingData, pipe);
118- op.Process();
119- }
120-}
121- 
122-template <bool EnableMedian>
123-__aicore__ inline void RunMergeIntraCoreRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace,
124- KthValueTilingData* tilingData, TPipe* pipe)
125-{
126- if constexpr (IsSameType<float, DTYPE_X>::value) {
127- KthValueMergeIntraCore<DTYPE_X, int64_t, false, EnableMedian> op;
128- op.Init(x, y1, y2, workspace, tilingData, pipe);
129- op.Process();
130- }
131-}
132- 
133-template <bool EnableMedian, bool useMergeSort>
134-__aicore__ inline void RunNonLastSmallAxisRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace,
135- KthValueTilingData* tilingData, TPipe* pipe)
136-{
137- constexpr bool enableNanMode = EnableMedian && IS_MEDIAN_FLOAT_TYPE<DTYPE_X>;
138- if constexpr (useMergeSort) {
139- if constexpr (IsSameType<DTYPE_X, float>::value || IsSameType<DTYPE_X, half>::value ||
140- IsSameType<DTYPE_X, bfloat16_t>::value) {
141- KthValueNonLastSmallAxis<DTYPE_X, false, true, enableNanMode> op;
142- op.Init(x, y1, y2, workspace, tilingData, pipe);
143- op.Process();
144- }
145- } else {
146- KthValueNonLastSmallAxis<DTYPE_X, false, false, enableNanMode> op;
147- op.Init(x, y1, y2, workspace, tilingData, pipe);
148- op.Process();
149- }
150-}
151- 
152-__aicore__ inline void RunAxisOneCopyRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace,
153- KthValueTilingData* tilingData, TPipe* pipe)
154-{
155- KthValueAxisOneCopy<DTYPE_X> op;
156- op.Init(x, y1, y2, workspace, tilingData, pipe);
157- op.Process();
158-}
159- 
160-template <bool EnableMedian>
161-__aicore__ inline void RunRadixOneCoreRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, KthValueTilingData* tilingData,
162- TPipe* pipe)
163-{
164- constexpr bool enableNanMode = EnableMedian && IS_MEDIAN_FLOAT_TYPE<DTYPE_X>;
165- KthValueRadixOneCore<DTYPE_X, enableNanMode> op;
166- op.Init(x, y1, y2, tilingData, pipe);
167- op.Process();
168-}
169- 
170-template <bool EnableMedian>
171-__aicore__ inline void RunRadixSelectRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace,
172- KthValueTilingData* tilingData, TPipe* pipe)
173-{
174- constexpr bool enableNanMode = EnableMedian && IS_MEDIAN_FLOAT_TYPE<DTYPE_X>;
175- if constexpr (sizeof(DTYPE_X) == 1) {
176- KthValueRadixSelect<DTYPE_X, uint8_t, enableNanMode> op;
177- op.Init(x, y1, y2, workspace, tilingData, pipe);
178- op.Process();
179- } else if constexpr (sizeof(DTYPE_X) == 2) {
180- KthValueRadixSelect<DTYPE_X, uint16_t, enableNanMode> op;
181- op.Init(x, y1, y2, workspace, tilingData, pipe);
182- op.Process();
183- } else if constexpr (sizeof(DTYPE_X) == 4) {
184- KthValueRadixSelect<DTYPE_X, uint32_t, enableNanMode> op;
185- op.Init(x, y1, y2, workspace, tilingData, pipe);
186- op.Process();
187- } else if constexpr (sizeof(DTYPE_X) == 8) {
188- KthValueRadixSelect<DTYPE_X, uint64_t, enableNanMode> op;
189- op.Init(x, y1, y2, workspace, tilingData, pipe);
190- op.Process();
191- }
192-}
193- 
194-template <bool EnableMedian>
195-__aicore__ inline void RunSmallAxisTwoStageRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, KthValueTilingData* tilingData,
196- TPipe* pipe)
197-{
198- constexpr bool enableNanMode = EnableMedian && IS_MEDIAN_FLOAT_TYPE<DTYPE_X>;
199- KthValueSmallAxisTwoStage<DTYPE_X, enableNanMode> op;
200- op.Init(x, y1, y2, tilingData, pipe);
201- op.Process();
202-}
203- 
204-__aicore__ inline void RunSmallAxisShortRankSelectRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2,
205- KthValueTilingData* tilingData, TPipe* pipe)
206-{
207- if constexpr (sizeof(DTYPE_X) == 8) {
208- KthValueSmallAxisShortRankSelect<DTYPE_X> op;
209- op.Init(x, y1, y2, tilingData, pipe);
210- op.Process();
211- }
212-}
213- 
214-template <bool EnableMedian, uint64_t schId>
215-__aicore__ inline bool TryRunSmallAxisRoute(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, KthValueTilingData* tilingData,
216- TPipe* pipe)
217-{
218- if constexpr (schId == KTH_VALUE_SCHID_SMALL_AXIS_INSERTION) {
219- RunSmallAxisInsertionRoute<EnableMedian>(x, y1, y2, tilingData, pipe);
220- return true;
221- }
222- if constexpr (schId == KTH_VALUE_SCHID_SMALL_AXIS_TWO_STAGE) {
223- RunSmallAxisTwoStageRoute<EnableMedian>(x, y1, y2, tilingData, pipe);
224- return true;
225- }
226- if constexpr (schId == KTH_VALUE_SCHID_SMALL_AXIS_SHORT_RANK_SELECT) {
227- RunSmallAxisShortRankSelectRoute(x, y1, y2, tilingData, pipe);
228- return true;
229- }
230- return false;
231-}
232- 
233-// EnableMedian selects the shared Median/NanMedian behavior. tilingData->medianMode
234-// distinguishes propagate-NaN Median from ignore-NaN NanMedian at runtime.
235-template <bool EnableMedian, uint64_t schId, uint64_t isInt32>
236-__aicore__ inline void Dispatch(GM_ADDR x, GM_ADDR y1, GM_ADDR y2, GM_ADDR workspace, KthValueTilingData* tilingData,
237- TPipe* pipe)
238-{
239- GM_ADDR userWorkspace = AscendC::GetUserWorkspace(workspace);
240- if constexpr (schId == KTH_VALUE_SCHID_AXIS_ONE_COPY) {
241- RunAxisOneCopyRoute(x, y1, y2, userWorkspace, tilingData, pipe);
242- } else if constexpr (schId == KTH_VALUE_SCHID_MERGE_SORT || schId == KTH_VALUE_SCHID_SORT32_SMALL_AXIS) {
243- RunMergeSortRoute<EnableMedian, schId>(x, y1, y2, tilingData, pipe);
244- } else if constexpr (schId == KTH_VALUE_SCHID_RADIX_ONE_CORE) {
245- RunRadixOneCoreRoute<EnableMedian>(x, y1, y2, tilingData, pipe);
246- } else if constexpr (schId == KTH_VALUE_SCHID_RADIX_MORE_CORE) {
247- RunRadixMoreCoreRoute<EnableMedian, isInt32>(x, y1, y2, userWorkspace, tilingData, pipe);
248- } else if constexpr (schId == KTH_VALUE_SCHID_RADIX_SELECT) {
249- RunRadixSelectRoute<EnableMedian>(x, y1, y2, userWorkspace, tilingData, pipe);
250- } else if constexpr (schId == KTH_VALUE_SCHID_SMALL_AXIS_INSERTION ||
251- schId == KTH_VALUE_SCHID_SMALL_AXIS_TWO_STAGE ||
252- schId == KTH_VALUE_SCHID_SMALL_AXIS_SHORT_RANK_SELECT) {
253- TryRunSmallAxisRoute<EnableMedian, schId>(x, y1, y2, tilingData, pipe);
254- } else if constexpr (schId == KTH_VALUE_SCHID_MERGE_MORE_CORE) {
255- RunMergeMoreCoreRoute<EnableMedian>(x, y1, y2, userWorkspace, tilingData, pipe);
256- } else if constexpr (schId == KTH_VALUE_SCHID_MERGE_INTRA_CORE) {
257- RunMergeIntraCoreRoute<EnableMedian>(x, y1, y2, userWorkspace, tilingData, pipe);
258- } else if constexpr (schId == KTH_VALUE_SCHID_NON_LAST_SMALL_AXIS) {
259- RunNonLastSmallAxisRoute<EnableMedian, true>(x, y1, y2, userWorkspace, tilingData, pipe);
260- } else if constexpr (schId == KTH_VALUE_SCHID_NON_LAST_SMALL_AXIS_RADIX) {
261- RunNonLastSmallAxisRoute<EnableMedian, false>(x, y1, y2, userWorkspace, tilingData, pipe);
262- }
263-}
264- 
265-} // namespace KthValue
266- 
267-#endif
@@ -1,232 +0,0 @@
1-/**
2- * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10- 
11-/**
12- * @file kth_value_median_utils.h
13- * @brief Median-mode helper primitives shared by kth_value schedule kernels
14- *
15- * @details This file provides the common primitives required by median semantics
16- * (NaN-aware median), reused by all schedule kernels:
17- * 1. Sort-value canonicalization: replaces every NaN with the canonical quiet NaN
18- * bit pattern so that NaNs sort identically into a deterministic suffix. The
19- * scalar CanonicalizeMedianSortValue additionally normalizes both signed zeros
20- * to +0 so stable sorting preserves their original order.
21- * 2. Non-NaN counting: vector reduction of the non-NaN element count of a UB
22- * segment into a scalar slot, with cross-chunk accumulation support
23- * (AccumulateNonNanCount / CountNonNan)
24- * 3. Median K resolution: maps the median to a sorted-array index according to
25- * medianMode (PROPAGATE_NAN/IGNORE_NAN/STATIC); the non-NaN count is either
26- * passed in by the caller or derived by binary search over sorted rows
27- * (ResolveMedianK / ResolveMedianKFromSorted / IsNanValue)
28- *
29- * All primitives operate on contiguous UB segments only and are axis/row agnostic;
30- * row iteration, multi-core splitting and tiling orchestration are owned by the
31- * callers (radix/merge/small_axis/non_last schedule files).
32- */
33- 
34-#ifndef KTH_VALUE_MEDIAN_UTILS_H
35-#define KTH_VALUE_MEDIAN_UTILS_H
36- 
37-#include <cmath>
38- 
39-#include "kernel_operator.h"
40-#include "op_kernel/platform_util.h"
41-#include "../../sort/arch35/common/util_type_simd.h"
42- 
43-namespace KthValue {
44-using namespace AscendC;
45- 
46-constexpr Reg::CastTrait MEDIAN_CAST_TO_FLOAT = {Reg::RegLayout::ZERO, Reg::SatMode::UNKNOWN,
47- Reg::MaskMergeMode::ZEROING, RoundMode::UNKNOWN};
48-constexpr Reg::CastTrait MEDIAN_CAST_FLOAT_TO_INT32 = {Reg::RegLayout::UNKNOWN, Reg::SatMode::NO_SAT,
49- Reg::MaskMergeMode::ZEROING, RoundMode::CAST_TRUNC};
50- 
51-template <typename T>
52-constexpr bool IS_MEDIAN_FLOAT_TYPE = IsSameType<T, float>::value || IsSameType<T, half>::value ||
53- IsSameType<T, bfloat16_t>::value;
54- 
55-// Replaces every NaN in-place with the canonical quiet NaN bit pattern so that all
56-// NaNs compare/sort identically and form a deterministic suffix after sorting.
57-template <typename T>
58-__aicore__ inline void CanonicalizeNanValues(LocalTensor<T> input, uint32_t count)
59-{
60- static_assert(IS_MEDIAN_FLOAT_TYPE<T>, "CanonicalizeNanValues only supports floating-point types");
61- if constexpr (IsSameType<float, T>::value) {
62- constexpr uint32_t VECTOR_LEN = Ops::Base::GetVRegSize() / sizeof(uint32_t);
63- constexpr uint32_t ABS_MASK = 0x7FFFFFFFU;
64- constexpr uint32_t INF_BITS = 0x7F800000U;
65- constexpr uint32_t CANONICAL_NAN_BITS = 0x7FC00000U;
66- uint16_t repeatTime = static_cast<uint16_t>(CeilDivision(count, VECTOR_LEN));
67- uint32_t remaining = count;
68- __ubuf__ uint32_t* inputPtr = reinterpret_cast<__ubuf__ uint32_t*>(input.GetPhyAddr());
69- __VEC_SCOPE__
70- {
71- Reg::RegTensor<uint32_t> rawBits, absBits, absMask, canonicalNan, result;
72- Reg::MaskReg validMask, nanMask;
73- Reg::Duplicate(absMask, ABS_MASK);
74- Reg::Duplicate(canonicalNan, CANONICAL_NAN_BITS);
75- for (uint16_t i = 0; i < repeatTime; ++i) {
76- validMask = Reg::UpdateMask<uint32_t>(remaining);
77- Reg::LoadAlign<uint32_t, Reg::LoadDist::DIST_NORM>(rawBits, inputPtr + i * VECTOR_LEN);
78- Reg::And(absBits, rawBits, absMask, validMask);
79- Reg::Compares<uint32_t, CMPMODE::GT>(nanMask, absBits, INF_BITS, validMask);
80- Reg::Select(result, canonicalNan, rawBits, nanMask);
81- Reg::StoreAlign<uint32_t, Reg::StoreDist::DIST_NORM_B32>(inputPtr + i * VECTOR_LEN, result, validMask);
82- }
83- }
84- } else {
85- constexpr uint32_t VECTOR_LEN = Ops::Base::GetVRegSize() / sizeof(uint16_t);
86- constexpr uint16_t ABS_MASK = 0x7FFFU;
87- constexpr uint16_t INF_BITS = IsSameType<half, T>::value ? 0x7C00U : 0x7F80U;
88- constexpr uint16_t CANONICAL_NAN_BITS = IsSameType<half, T>::value ? 0x7E00U : 0x7FC0U;
89- uint16_t repeatTime = static_cast<uint16_t>(CeilDivision(count, VECTOR_LEN));
90- uint32_t remaining = count;
91- __ubuf__ uint16_t* inputPtr = reinterpret_cast<__ubuf__ uint16_t*>(input.GetPhyAddr());
92- __VEC_SCOPE__
93- {
94- Reg::RegTensor<uint16_t> rawBits, absBits, absMask, canonicalNan, result;
95- Reg::MaskReg validMask, nanMask;
96- Reg::Duplicate(absMask, ABS_MASK);
97- Reg::Duplicate(canonicalNan, CANONICAL_NAN_BITS);
98- for (uint16_t i = 0; i < repeatTime; ++i) {
99- validMask = Reg::UpdateMask<uint16_t>(remaining);
100- Reg::LoadAlign<uint16_t, Reg::LoadDist::DIST_NORM>(rawBits, inputPtr + i * VECTOR_LEN);
101- Reg::And(absBits, rawBits, absMask, validMask);
102- Reg::Compares<uint16_t, CMPMODE::GT>(nanMask, absBits, INF_BITS, validMask);
103- Reg::Select(result, canonicalNan, rawBits, nanMask);
104- Reg::StoreAlign<uint16_t, Reg::StoreDist::DIST_NORM_B16>(inputPtr + i * VECTOR_LEN, result, validMask);
105- }
106- }
107- }
108-}
109- 
110-__aicore__ inline uint32_t ResolveMedianK(uint32_t staticK, uint32_t validCount, uint32_t nonNanCount,
111- uint32_t medianMode)
112-{
113- if (medianMode == MEDIAN_MODE_PROPAGATE_NAN && nonNanCount < validCount) {
114- return nonNanCount;
115- }
116- if (medianMode == MEDIAN_MODE_IGNORE_NAN) {
117- return nonNanCount == 0U ? 0U : (nonNanCount - 1U) / 2U;
118- }
119- return staticK;
120-}
121- 
122-template <typename T>
123-__simt_callee__ __aicore__ inline bool IsNanValue(T value)
124-{
125- float valueFp32 = static_cast<float>(value);
126- return valueFp32 != valueFp32;
127-}
128- 
129-template <typename T>
130-__simt_callee__ __aicore__ inline T CanonicalizeMedianSortValue(T value)
131-{
132- float valueFp32 = static_cast<float>(value);
133- if (valueFp32 != valueFp32) {
134- return static_cast<T>(NAN);
135- }
136- if (valueFp32 == 0.0f) {
137- return static_cast<T>(0.0f);
138- }
139- return value;
140-}
141- 
142-// Resolves the sorted-array index of the median for the given mode: derives the non-NaN
143-// count by lower-bound binary search (NaNs form a suffix) and maps it per medianMode.
144-template <typename T>
145-__simt_callee__ __aicore__ inline uint32_t ResolveMedianKFromSorted(const __ubuf__ T* sortedValues, uint32_t rowOffset,
146- uint32_t validCount, uint32_t staticK,
147- uint32_t medianMode)
148-{
149- if (medianMode == MEDIAN_MODE_STATIC) {
150- return staticK;
151- }
152- 
153- // NaNs are canonicalized before sorting and form a suffix. Find the first
154- // NaN (which is also the number of non-NaN values) with a lower-bound search.
155- uint32_t left = 0U;
156- uint32_t right = validCount;
157- while (left < right) {
158- uint32_t mid = left + (right - left) / 2U;
159- if (IsNanValue(sortedValues[rowOffset + mid])) {
160- right = mid;
161- } else {
162- left = mid + 1U;
163- }
164- }
165- uint32_t nonNanCount = left;
166- if (medianMode == MEDIAN_MODE_PROPAGATE_NAN && nonNanCount < validCount) {
167- return nonNanCount;
168- }
169- if (medianMode == MEDIAN_MODE_IGNORE_NAN) {
170- return nonNanCount == 0U ? 0U : (nonNanCount - 1U) / 2U;
171- }
172- return staticK;
173-}
174- 
175-// Vector-reduces the number of non-NaN elements in input[count] into a single UB scalar slot;
176-// with Reset=false the result is accumulated on top of the slot's existing value.
177-template <bool Reset, typename T>
178-__aicore__ inline void AccumulateNonNanCount(LocalTensor<T> input, uint32_t count, LocalTensor<uint32_t> scalar)
179-{
180- constexpr uint32_t FLOAT_VL = Ops::Base::GetVRegSize() / sizeof(float);
181- uint16_t repeatTime = static_cast<uint16_t>((count + FLOAT_VL - 1U) / FLOAT_VL);
182- uint32_t remaining = count;
183- __ubuf__ T* inputPtr = reinterpret_cast<__ubuf__ T*>(input.GetPhyAddr());
184- __ubuf__ int32_t* scalarPtr = reinterpret_cast<__ubuf__ int32_t*>(scalar.GetPhyAddr());
185- __VEC_SCOPE__
186- {
187- Reg::RegTensor<float> value, one, zero, nonNanFlag, reduced, total;
188- Reg::RegTensor<int32_t> totalInt;
189- Reg::MaskReg validMask, nanMask;
190- Reg::MaskReg oneMask = Reg::CreateMask<float, Reg::MaskPattern::VL1>();
191- Reg::Duplicate(one, 1.0f);
192- Reg::Duplicate(zero, 0.0f);
193- Reg::Duplicate(total, 0.0f, oneMask);
194- for (uint16_t i = 0; i < repeatTime; ++i) {
195- validMask = Reg::UpdateMask<float>(remaining);
196- if constexpr (IsSameType<float, T>::value) {
197- Reg::LoadAlign<float, Reg::LoadDist::DIST_NORM>(
198- value, reinterpret_cast<__ubuf__ float*>(inputPtr) + i * FLOAT_VL);
199- } else {
200- Reg::RegTensor<T> valueB16;
201- Reg::LoadAlign<T, Reg::LoadDist::DIST_UNPACK_B16>(valueB16, inputPtr + i * FLOAT_VL);
202- Reg::Cast<float, T, MEDIAN_CAST_TO_FLOAT>(value, valueB16, validMask);
203- }
204- Reg::Compare<float, CMPMODE::NE>(nanMask, value, value, validMask);
205- Reg::Select(nonNanFlag, zero, one, nanMask);
206- Reg::Reduce<Reg::ReduceType::SUM>(reduced, nonNanFlag, validMask);
207- Reg::Add(total, total, reduced, oneMask);
208- }
209- Reg::Cast<int32_t, float, MEDIAN_CAST_FLOAT_TO_INT32>(totalInt, total, oneMask);
210- if constexpr (!Reset) {
211- Reg::RegTensor<int32_t> accumulated;
212- Reg::LoadAlign<int32_t, Reg::LoadDist::DIST_NORM>(accumulated, scalarPtr);
213- Reg::Add(totalInt, totalInt, accumulated, oneMask);
214- }
215- Reg::StoreAlign<int32_t, Reg::StoreDist::DIST_NORM_B32>(scalarPtr, totalInt, oneMask);
216- }
217-}
218- 
219-template <typename T>
220-__aicore__ inline uint32_t CountNonNan(LocalTensor<T> input, uint32_t count, LocalTensor<float> scalar, TPipe* pipe)
221-{
222- LocalTensor<uint32_t> scalarU32 = scalar.template ReinterpretCast<uint32_t>();
223- AccumulateNonNanCount<true>(input, count, scalarU32);
224- event_t countEvent = static_cast<event_t>(pipe->FetchEventID(HardEvent::V_S));
225- SetFlag<HardEvent::V_S>(countEvent);
226- WaitFlag<HardEvent::V_S>(countEvent);
227- return scalarU32.GetValue(0);
228-}
229- 
230-} // namespace KthValue
231- 
232-#endif
@@ -1,301 +0,0 @@
1-/**
2- * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10- 
11-/*!
12- * \file kth_value_merge_intra_core.h
13- * \brief Intra-core block merge sort for kth_value (fp32)
14- * \details Each core independently sorts blocks and merges them via 4-way MrgSort.
15- * No cross-core synchronization needed in merge phase.
16- * Phase 3 extracts only the kth element instead of the full sorted output.
17- */
18- 
19-#ifndef KTH_VALUE_MERGE_INTRA_CORE_H
20-#define KTH_VALUE_MERGE_INTRA_CORE_H
21- 
22-#include <cmath>
23-#include "kernel_operator.h"
24-#include "op_kernel/math_util.h"
25-#include "op_kernel/platform_util.h"
26-#include "kth_value_median_utils.h"
27-#include "kth_value_tiling_data.h"
28-#include "../../sort/arch35/common/merge_sort_constants.h"
29-#include "../../sort/arch35/common/merge_intra_core_base.h"
30- 
31-namespace KthValue {
32-using namespace AscendC;
33- 
34-// Import shared constants from MergeSortConstants namespace
35-using MergeSortConstants::DEALING_CONCAT_NUM_ONCE;
36-using MergeSortConstants::DEALING_EXTRACT_NUM_ONCE;
37-using MergeSortConstants::DEALING_SORT_NUM_ONCE;
38-using MergeSortConstants::MERGE_INTRA_BUFFER_NUM;
39-using MergeSortConstants::MERGE_LIST_MAX_NUM;
40-using MergeSortConstants::UB_BLOCK_BYTES;
41- 
42-/**
43- * @brief Intra-core block merge sort for kth_value: independent per-core sort+merge,
44- * then extracts only the kth element from the sorted result.
45- * @tparam ValueType Input data type (float)
46- * @tparam IndexType Index data type (int32_t or int64_t)
47- * @tparam IsDescend Sort order: true for descending, false for ascending
48- * @tparam EnableMedian Enables NaN canonicalization and runtime median-k resolution for Median/NanMedian
49- */
50-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian = false>
51-class KthValueMergeIntraCore
52- : public MergeIntraCoreCommon::MergeIntraCoreBase<
53- KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>, ValueType, IndexType, IsDescend> {
54- using Base = MergeIntraCoreCommon::MergeIntraCoreBase<
55- KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>, ValueType, IndexType, IsDescend>;
56- friend Base;
57- 
58-public:
59- __aicore__ inline KthValueMergeIntraCore() {}
60- __aicore__ inline void Init(GM_ADDR x, GM_ADDR value, GM_ADDR indices, GM_ADDR workspace,
61- const KthValueTilingData* tilingData, TPipe* pipe);
62- 
63-private:
64- // KthValue-specific members
65- uint32_t kthIndex_ = 0;
66- uint32_t staticKthIndex_ = 0;
67- uint32_t medianMode_ = 0;
68- TQue<QuePosition::VECOUT, 1> kthValueQueue_, kthIdxQueue_;
69- 
70- __aicore__ inline void InitPhase1Buffers();
71- __aicore__ inline void InitPhase2Buffers();
72- __aicore__ inline void InitPhase3Buffers();
73- __aicore__ inline void PrepareInputForSort(LocalTensor<ValueType> inputLocal, uint32_t alignedCount);
74- __aicore__ inline void ExtractAndCopyOut(int64_t batchIdx, uint32_t resultRegion);
75- __aicore__ inline void ExtractAndCopyChunk(int64_t cacheBatchOffset, uint32_t cacheOffset, int64_t outputOffset,
76- uint32_t elemProcessed, uint32_t elemCount);
77- __aicore__ inline uint32_t ResolveBatchK(int64_t batchIdx);
78-};
79- 
80-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
81-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::Init(
82- GM_ADDR x, GM_ADDR value, GM_ADDR indices, GM_ADDR workspace, const KthValueTilingData* tilingData, TPipe* pipe)
83-{
84- if (tilingData == nullptr || pipe == nullptr) {
85- return;
86- }
87- this->blockIdx_ = GetBlockIdx();
88- this->pipe_ = pipe;
89- 
90- // Parse tiling data
91- this->batchNum_ = tilingData->unsortedDimNum;
92- this->sortAxisNum_ = tilingData->lastAxisNum;
93- this->batchPerCore_ = tilingData->keyParams0;
94- this->blockSortSize_ = tilingData->numTileDataSize;
95- kthIndex_ = tilingData->kthIndex;
96- staticKthIndex_ = kthIndex_;
97- medianMode_ = tilingData->medianMode;
98- this->extractChunkSize_ = tilingData->keyParams4;
99- this->blocksPerRow_ = tilingData->lastDimTileNum;
100- this->maxCoreNum_ = tilingData->lastDimNeedCore;
101- this->alignNum_ = tilingData->keyParams3;
102- this->maxMergeIterations_ = tilingData->keyParams5;
103- 
104- if (this->blockSortSize_ == 0 || this->extractChunkSize_ == 0 || this->blocksPerRow_ == 0 ||
105- this->sortAxisNum_ <= 0) {
106- return;
107- }
108- 
109- // Set GM buffers before deriving cache sizes. This keeps KthValue init local while avoiding
110- // a long identical block with SortMergeIntraCore.
111- this->inputXGm_.SetGlobalBuffer((__gm__ ValueType*)x);
112- this->outValueGm_.SetGlobalBuffer((__gm__ ValueType*)value);
113- this->outIdxGm_.SetGlobalBuffer((__gm__ IndexType*)indices);
114- 
115- // Precompute constants
116- this->blockSortLen_ = AscendC::GetSortLen<ValueType>(this->blockSortSize_);
117- this->batchSortLen_ = AscendC::GetSortLen<ValueType>(this->alignNum_);
118- this->sortBufferSize_ = this->blockSortLen_ * sizeof(ValueType);
119- this->sortRepeatTimes_ = this->blockSortSize_ / DEALING_SORT_NUM_ONCE;
120- this->concatRepeatTimes_ = this->blockSortSize_ / DEALING_CONCAT_NUM_ONCE;
121- this->lastBlockSize_ = static_cast<uint32_t>(this->sortAxisNum_ -
122- static_cast<int64_t>(this->blocksPerRow_ - 1) * this->blockSortSize_);
123- 
124- // Cache stores sort struct data (8 bytes per element: index + value)
125- // Each core has its own cache region, reused across batches
126- // perBatchCacheLen: sort struct length for one batch (with ping-pong, in ValueType units)
127- int64_t perCoreCacheLen = static_cast<int64_t>(this->batchSortLen_) * 2; // ping-pong, reused per batch
128- 
129- this->cacheGm_.SetGlobalBuffer((__gm__ ValueType*)workspace +
130- static_cast<int64_t>(this->blockIdx_) * perCoreCacheLen);
131-}
132- 
133-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
134-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::InitPhase1Buffers()
135-{
136- this->pipe_->InitBuffer(this->inQueueX_, MERGE_INTRA_BUFFER_NUM, this->blockSortSize_ * sizeof(ValueType));
137- this->pipe_->InitBuffer(this->concatTmpBuf_, this->sortBufferSize_);
138- this->pipe_->InitBuffer(this->sortTmpBuf_, this->sortBufferSize_);
139- this->pipe_->InitBuffer(this->sortedOutQueue_, MERGE_INTRA_BUFFER_NUM, this->sortBufferSize_);
140- this->pipe_->InitBuffer(this->indexTmpBuf_, this->blockSortSize_ * sizeof(uint32_t));
141-}
142- 
143-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
144-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::PrepareInputForSort(
145- LocalTensor<ValueType> inputLocal, uint32_t alignedCount)
146-{
147- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<ValueType>) {
148- if (medianMode_ != MEDIAN_MODE_STATIC) {
149- CanonicalizeNanValues(inputLocal, alignedCount);
150- }
151- }
152-}
153- 
154-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
155-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::InitPhase2Buffers()
156-{
157- uint32_t mergeBufferSize = MERGE_LIST_MAX_NUM * this->sortBufferSize_;
158- this->pipe_->InitBuffer(this->mergeInQueue_, 1, mergeBufferSize);
159- this->pipe_->InitBuffer(this->mergeOutQueue_, 1, mergeBufferSize);
160-}
161- 
162-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
163-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::InitPhase3Buffers()
164-{
165- uint32_t extractInSize = AscendC::GetSortLen<ValueType>(this->extractChunkSize_) * sizeof(ValueType);
166- this->pipe_->InitBuffer(this->extractInQueue_, MERGE_INTRA_BUFFER_NUM, extractInSize);
167- this->pipe_->InitBuffer(this->outValueQueue_, MERGE_INTRA_BUFFER_NUM, this->extractChunkSize_ * sizeof(ValueType));
168- this->pipe_->InitBuffer(this->outIdxQueue_, MERGE_INTRA_BUFFER_NUM, this->extractChunkSize_ * sizeof(uint32_t));
169- this->pipe_->InitBuffer(kthValueQueue_, 1, UB_BLOCK_BYTES);
170- if constexpr (IsSameType<int64_t, IndexType>::value) {
171- this->pipe_->InitBuffer(kthIdxQueue_, 1, UB_BLOCK_BYTES);
172- }
173-}
174- 
175-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
176-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::ExtractAndCopyOut(
177- int64_t batchIdx, uint32_t resultRegion)
178-{
179- int64_t outputOffset = batchIdx;
180- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<ValueType>) {
181- kthIndex_ = ResolveBatchK(batchIdx);
182- }
183- 
184- // resultRegion: 0 = Ping (offset 0), 1 = Pong (offset batchSortLen_)
185- int64_t cacheBatchOffset = (resultRegion == 1) ? this->batchSortLen_ : 0;
186- 
187- uint32_t elemProcessed = 0;
188- uint32_t cacheOffset = 0;
189- 
190- // The merged cache is full sorted [N], but KthValue only needs one element. Skip extract chunks
191- // until the chunk containing kthIndex_, then write exactly one value/index pair.
192- while (elemProcessed <= kthIndex_) {
193- uint32_t remainElem = static_cast<uint32_t>(this->sortAxisNum_) - elemProcessed;
194- uint32_t elemCount = (this->extractChunkSize_ <= remainElem) ? this->extractChunkSize_ : remainElem;
195- if (elemCount == 0)
196- break;
197- 
198- if (kthIndex_ < elemProcessed + elemCount) {
199- ExtractAndCopyChunk(cacheBatchOffset, cacheOffset, outputOffset, elemProcessed, elemCount);
200- break;
201- }
202- 
203- elemProcessed += elemCount;
204- cacheOffset += AscendC::GetSortLen<ValueType>(elemCount);
205- }
206-}
207- 
208-// Resolves the effective k for one batch row: recounts non-NaN elements by re-reading the
209-// raw GM row in chunks (the merged cache is sign-flip encoded, unusable for NaN tests),
210-// then maps it to a sorted index via ResolveMedianK.
211-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
212-__aicore__ inline uint32_t KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::ResolveBatchK(
213- int64_t batchIdx)
214-{
215- uint32_t nonNanCount = 0U;
216- uint32_t elemOffset = 0U;
217- uint32_t axisLen = static_cast<uint32_t>(this->sortAxisNum_);
218- LocalTensor<ValueType> scalarLocal = kthValueQueue_.template AllocTensor<ValueType>();
219- while (elemOffset < axisLen) {
220- uint32_t remain = axisLen - elemOffset;
221- uint32_t elemCount = this->extractChunkSize_ < remain ? this->extractChunkSize_ : remain;
222- LocalTensor<ValueType> inputLocal = this->extractInQueue_.template AllocTensor<ValueType>();
223- DataCopyExtParams loadParams{1, static_cast<uint32_t>(elemCount * sizeof(ValueType)), 0, 0, 0};
224- DataCopyPad(inputLocal, this->inputXGm_[batchIdx * this->sortAxisNum_ + elemOffset], loadParams,
225- {false, 0, 0, 0});
226- this->extractInQueue_.EnQue(inputLocal);
227- inputLocal = this->extractInQueue_.template DeQue<ValueType>();
228- nonNanCount += CountNonNan(inputLocal, elemCount, scalarLocal.template ReinterpretCast<float>(), this->pipe_);
229- this->extractInQueue_.FreeTensor(inputLocal);
230- elemOffset += elemCount;
231- }
232- kthValueQueue_.FreeTensor(scalarLocal);
233- return ResolveMedianK(staticKthIndex_, axisLen, nonNanCount, medianMode_);
234-}
235- 
236-template <typename ValueType, typename IndexType, bool IsDescend, bool EnableMedian>
237-__aicore__ inline void KthValueMergeIntraCore<ValueType, IndexType, IsDescend, EnableMedian>::ExtractAndCopyChunk(
238- int64_t cacheBatchOffset, uint32_t cacheOffset, int64_t outputOffset, uint32_t elemProcessed, uint32_t elemCount)
239-{
240- LocalTensor<ValueType> kthCacheLocal = this->extractInQueue_.template AllocTensor<ValueType>();
241- DataCopyExtParams loadParams;
242- loadParams.blockCount = 1;
243- loadParams.blockLen = static_cast<uint32_t>(AscendC::GetSortLen<ValueType>(elemCount) * sizeof(ValueType));
244- loadParams.srcStride = 0;
245- loadParams.dstStride = 0;
246- DataCopyPad(kthCacheLocal, this->cacheGm_[cacheBatchOffset + cacheOffset], loadParams, {false, 0, 0, 0});
247- this->extractInQueue_.EnQue(kthCacheLocal);
248- 
249- kthCacheLocal = this->extractInQueue_.template DeQue<ValueType>();
250- LocalTensor<ValueType> valueLocal = this->outValueQueue_.template AllocTensor<ValueType>();
251- LocalTensor<uint32_t> indexLocal = this->outIdxQueue_.template AllocTensor<uint32_t>();
252- Extract(valueLocal, indexLocal, kthCacheLocal, Ops::Base::CeilDiv(elemCount, DEALING_EXTRACT_NUM_ONCE));
253- 
254- // Flip back sign bit for ascending order (was flipped in SortBlockToStruct)
255- if constexpr (!IsDescend) {
256- Adds(valueLocal.template ReinterpretCast<int32_t>(), valueLocal.template ReinterpretCast<int32_t>(), 0x80000000,
257- elemCount);
258- }
259- 
260- this->outValueQueue_.EnQue(valueLocal);
261- this->outIdxQueue_.EnQue(indexLocal);
262- this->extractInQueue_.FreeTensor(kthCacheLocal);
263- valueLocal = this->outValueQueue_.template DeQue<ValueType>();
264- indexLocal = this->outIdxQueue_.template DeQue<uint32_t>();
265- 
266- event_t eventIdVToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_S));
267- SetFlag<HardEvent::V_S>(eventIdVToS);
268- WaitFlag<HardEvent::V_S>(eventIdVToS);
269- uint32_t localK = kthIndex_ - elemProcessed;
270- // localK is relative to the extracted chunk, not the whole row.
271- LocalTensor<ValueType> outValue = kthValueQueue_.AllocTensor<ValueType>();
272- outValue.SetValue(0, valueLocal.GetValue(localK));
273- LocalTensor<int32_t> indexInt32 = indexLocal.template ReinterpretCast<int32_t>();
274- LocalTensor<int64_t> indexInt64 = kthIdxQueue_.AllocTensor<int64_t>();
275- indexInt64.SetValue(0, static_cast<int64_t>(indexInt32.GetValue(localK)));
276- kthValueQueue_.EnQue(outValue);
277- kthIdxQueue_.EnQue(indexInt64);
278- outValue = kthValueQueue_.DeQue<ValueType>();
279- indexInt64 = kthIdxQueue_.DeQue<int64_t>();
280- 
281- event_t eventIdSToMte3 = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::S_MTE3));
282- SetFlag<HardEvent::S_MTE3>(eventIdSToMte3);
283- WaitFlag<HardEvent::S_MTE3>(eventIdSToMte3);
284- DataCopyExtParams outParams{1, static_cast<uint32_t>(sizeof(ValueType)), 0, 0, 0};
285- DataCopyPad(this->outValueGm_[outputOffset], outValue, outParams);
286- if constexpr (IsSameType<int64_t, IndexType>::value) {
287- outParams.blockLen = static_cast<uint32_t>(sizeof(int64_t));
288- DataCopyPad(this->outIdxGm_[outputOffset], indexInt64, outParams);
289- }
290- event_t eventIdMte3ToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::MTE3_S));
291- SetFlag<HardEvent::MTE3_S>(eventIdMte3ToS);
292- WaitFlag<HardEvent::MTE3_S>(eventIdMte3ToS);
293- kthIdxQueue_.FreeTensor(indexInt64);
294- kthValueQueue_.FreeTensor(outValue);
295- this->outIdxQueue_.FreeTensor(indexLocal);
296- this->outValueQueue_.FreeTensor(valueLocal);
297-}
298- 
299-} // namespace KthValue
300- 
301-#endif // KTH_VALUE_MERGE_INTRA_CORE_H
@@ -1,245 +0,0 @@
1-/**
2- * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10- 
11-/*!
12- * \file merge_sort_simd.h
13- * \brief merge_sort kernel entry
14- */
15-#ifndef KTH_VALUE_MERGE_SORT_MORE_CORE_H
16-#define KTH_VALUE_MERGE_SORT_MORE_CORE_H
17-#include <cmath>
18-#include "op_kernel/platform_util.h"
19-#include "kernel_operator.h"
20-#include "kth_value_median_utils.h"
21-#include "kth_value_tiling_data.h"
22-#include "../../sort/arch35/common/merge_sort_constants.h"
23-#include "../../sort/arch35/common/merge_more_core_base.h"
24- 
25-namespace KthValue {
26-using namespace AscendC;
27- 
28-// Import shared constants from MergeSortConstants namespace
29-using MergeSortConstants::DEALING_CONCAT_NUM_ONCE;
30-using MergeSortConstants::DEALING_EXTRACT_NUM_ONCE;
31-using MergeSortConstants::DEALING_SORT_NUM_ONCE;
32-using MergeSortConstants::MERGE_LIST_MAX_NUM;
33-using MergeSortConstants::MERGE_MORE_BUFFER_NUM;
34-using MergeSortConstants::MERGE_SORT_WORKSPACE_PARAM;
35-using MergeSortConstants::UB_BLOCK_BYTES;
36-using MergeSortConstants::XOR_OP_VALUE_FP;
37-using MergeSortConstants::XOR_OP_VALUE_HALF;
38- 
39-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian = false>
40-struct KthValueMergeSortMoreCore : public MergeMoreCoreCommon::MergeMoreCoreBase<
41- KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>,
42- T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE> {
43- using Base = MergeMoreCoreCommon::MergeMoreCoreBase<
44- KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>, T, CONVERT_TYPE, IS_DESCEND,
45- INDEX_TYPE>;
46- friend Base;
47- 
48- __aicore__ inline KthValueMergeSortMoreCore() {}
49- __aicore__ inline void Init(GM_ADDR inputValue, GM_ADDR value, GM_ADDR indices, GM_ADDR workSpace,
50- const KthValueTilingData* tilingData, TPipe* pipe);
51- __aicore__ inline void InitMergeBuffers();
52- __aicore__ inline void ExtractAndCopyOut();
53- __aicore__ inline void OnInputLoaded(LocalTensor<T> inputLocal, uint32_t tileNum);
54- __aicore__ inline void PrepareInputForSort(LocalTensor<T> inputLocal, uint32_t alignedCount);
55- __aicore__ inline uint32_t ResolveRowK();
56- 
57- // KthValue-specific member
58- uint32_t kthIndex_ = 0;
59- uint32_t medianMode_ = 0;
60- GlobalTensor<uint32_t> medianCountGm_;
61-};
62- 
63-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian>
64-__aicore__ inline void KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>::Init(
65- GM_ADDR inputValue, GM_ADDR value, GM_ADDR indices, GM_ADDR workSpace, const KthValueTilingData* tilingData,
66- TPipe* pipe)
67-{
68- if (tilingData == nullptr || pipe == nullptr) {
69- return;
70- }
71- this->blockIdx_ = GetBlockIdx();
72- this->pipe_ = pipe;
73- this->outputLastDimValue_ = tilingData->lastDimTileNum;
74- this->numTileData_ = tilingData->numTileDataSize;
75- this->frontCoreNum_ = tilingData->lastDimNeedCore;
76- kthIndex_ = tilingData->kthIndex;
77- medianMode_ = tilingData->medianMode;
78- if (this->frontCoreNum_ == 0U) {
79- return;
80- }
81- uint32_t sortBufferSize = 8;
82- this->rowIdx_ = this->blockIdx_ / this->frontCoreNum_;
83- this->rowCoreIdx_ = this->blockIdx_ % this->frontCoreNum_;
84- this->rowDataOffset_ = static_cast<int64_t>(this->rowIdx_) * static_cast<int64_t>(this->outputLastDimValue_);
85- // Per-row workspace stores Sort API sort-struct data. This capacity uses sortBufferSize bytes per
86- // original element and UB-block byte alignment; it must cover later GetSortLen-based accesses.
87- uint64_t rowWorkspaceBytes = ROUND_UP_AGLIN_UINT64(static_cast<uint64_t>(this->outputLastDimValue_) *
88- sortBufferSize);
89- uint64_t rowWorkspaceElements = rowWorkspaceBytes / sizeof(CONVERT_TYPE);
90- this->rowWorkspaceOffset_ = static_cast<int64_t>(this->rowIdx_) * static_cast<int64_t>(rowWorkspaceElements) * 2;
91- this->onceMaxElements_ = tilingData->keyParams0 / DEALING_SORT_NUM_ONCE * DEALING_SORT_NUM_ONCE;
92- 
93- this->inputValueGm_.SetGlobalBuffer((__gm__ T*)(inputValue));
94- this->outValueGm_.SetGlobalBuffer((__gm__ T*)(value));
95- this->outIndexGm_.SetGlobalBuffer((__gm__ INDEX_TYPE*)(indices));
96- this->workspaceGm_[0].SetGlobalBuffer((__gm__ CONVERT_TYPE*)(workSpace) + this->rowWorkspaceOffset_,
97- rowWorkspaceElements);
98- this->workspaceGm_[1].SetGlobalBuffer(
99- (__gm__ CONVERT_TYPE*)(workSpace) + this->rowWorkspaceOffset_ + rowWorkspaceElements, rowWorkspaceElements);
100- uint64_t medianCountOffset = ROUND_UP_AGLIN_UINT64(static_cast<uint64_t>(tilingData->lastAxisNum) *
101- static_cast<uint64_t>(tilingData->unsortedDimNum) *
102- MERGE_SORT_WORKSPACE_PARAM * sizeof(uint32_t));
103- medianCountGm_.SetGlobalBuffer(reinterpret_cast<__gm__ uint32_t*>(workSpace + medianCountOffset),
104- tilingData->lastDimNeedCore * tilingData->unsortedDimNum);
105- 
106- uint32_t tailNum = this->outputLastDimValue_ - (this->frontCoreNum_ - 1) * this->numTileData_;
107- uint32_t alignTile = ROUND_UP_AGLIN(tailNum);
108- this->pipe_->InitBuffer(this->inputQueue_, MERGE_MORE_BUFFER_NUM, alignTile * sizeof(T));
109- 
110- this->pipe_->InitBuffer(this->sortedValueUb_, alignTile * sortBufferSize);
111- this->pipe_->InitBuffer(this->sortedValueIndexUb_, alignTile * sizeof(uint32_t));
112- this->pipe_->InitBuffer(this->concatTempBuf_, alignTile * sortBufferSize);
113- this->pipe_->InitBuffer(this->sortTempBuf_, alignTile * sortBufferSize);
114- this->pipe_->InitBuffer(this->sortedValueLocalCastTbuf_, alignTile * sortBufferSize);
115-}
116- 
117-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian>
118-__aicore__ inline void KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>::OnInputLoaded(
119- LocalTensor<T> inputLocal, uint32_t tileNum)
120-{
121- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
122- LocalTensor<float> scratch = this->concatTempBuf_.template Get<float>();
123- uint32_t nonNanCount = CountNonNan(inputLocal, tileNum, scratch, this->pipe_);
124- LocalTensor<uint32_t> countLocal = scratch.template ReinterpretCast<uint32_t>();
125- countLocal.SetValue(0, nonNanCount);
126- event_t eventId = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::S_MTE3));
127- SetFlag<HardEvent::S_MTE3>(eventId);
128- WaitFlag<HardEvent::S_MTE3>(eventId);
129- DataCopyExtParams copyParams{1, static_cast<uint32_t>(sizeof(uint32_t)), 0, 0, 0};
130- DataCopyPad(medianCountGm_[this->blockIdx_], countLocal, copyParams);
131- }
132-}
133- 
134-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian>
135-__aicore__ inline void KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE,
136- EnableMedian>::PrepareInputForSort(LocalTensor<T> inputLocal,
137- uint32_t alignedCount)
138-{
139- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
140- if (medianMode_ != MEDIAN_MODE_STATIC) {
141- CanonicalizeNanValues(inputLocal, alignedCount);
142- }
143- }
144-}
145- 
146-// Resolves the effective k for the current row: gathers the per-core non-NaN counts
147-// published to the median count workspace by all frontCoreNum_ cores of this row, sums
148-// them up, then maps the row total to a sorted index via ResolveMedianK.
149-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian>
150-__aicore__ inline uint32_t
151-KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>::ResolveRowK()
152-{
153- LocalTensor<uint32_t> countLocal = this->concatTempBuf_.template Get<uint32_t>();
154- uint32_t countOffset = this->rowIdx_ * this->frontCoreNum_;
155- DataCopyExtParams copyParams{1, this->frontCoreNum_ * static_cast<uint32_t>(sizeof(uint32_t)), 0, 0, 0};
156- DataCopyPad(countLocal, medianCountGm_[countOffset], copyParams, {false, 0, 0, 0});
157- event_t eventId = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::MTE2_S));
158- SetFlag<HardEvent::MTE2_S>(eventId);
159- WaitFlag<HardEvent::MTE2_S>(eventId);
160- uint32_t nonNanCount = 0U;
161- for (uint32_t core = 0U; core < this->frontCoreNum_; ++core) {
162- nonNanCount += countLocal.GetValue(core);
163- }
164- return ResolveMedianK(kthIndex_, this->outputLastDimValue_, nonNanCount, medianMode_);
165-}
166- 
167-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian>
168-__aicore__ inline void
169-KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>::InitMergeBuffers()
170-{
171- uint32_t sortBufferSize = 8;
172- this->pipe_->InitBuffer(this->sortedQueue_, MERGE_MORE_BUFFER_NUM,
173- MERGE_LIST_MAX_NUM * this->onceMaxElements_ * sortBufferSize);
174- this->pipe_->InitBuffer(this->copyInQueue_, MERGE_MORE_BUFFER_NUM,
175- MERGE_LIST_MAX_NUM * this->onceMaxElements_ * sortBufferSize);
176- this->pipe_->InitBuffer(this->castValueQueue_, MERGE_MORE_BUFFER_NUM,
177- MERGE_LIST_MAX_NUM * this->onceMaxElements_ * sizeof(CONVERT_TYPE));
178- this->pipe_->InitBuffer(this->castIndexQueue_, MERGE_MORE_BUFFER_NUM,
179- MERGE_LIST_MAX_NUM * this->onceMaxElements_ * sizeof(uint32_t));
180- this->pipe_->InitBuffer(this->outValueQueue_, 1, UB_BLOCK_BYTES);
181- this->pipe_->InitBuffer(this->outIndexQueue_, 1, UB_BLOCK_BYTES);
182-}
183- 
184-template <typename T, typename CONVERT_TYPE, bool IS_DESCEND, typename INDEX_TYPE, bool EnableMedian>
185-__aicore__ inline void
186-KthValueMergeSortMoreCore<T, CONVERT_TYPE, IS_DESCEND, INDEX_TYPE, EnableMedian>::ExtractAndCopyOut()
187-{
188- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
189- if (this->outOffset_ == 0) {
190- kthIndex_ = ResolveRowK();
191- }
192- }
193- LocalTensor<CONVERT_TYPE> sortTempBuffer = this->sortedQueue_.template DeQue<CONVERT_TYPE>();
194- LocalTensor<CONVERT_TYPE> castValue = this->castValueQueue_.template AllocTensor<CONVERT_TYPE>();
195- LocalTensor<uint32_t> castIndex = this->castIndexQueue_.template AllocTensor<uint32_t>();
196- AscendC::Extract(castValue, castIndex, sortTempBuffer,
197- ((this->curLoopSortedNum_ + DEALING_EXTRACT_NUM_ONCE - 1) / DEALING_EXTRACT_NUM_ONCE));
198- if constexpr (!IS_DESCEND) {
199- this->FlipSignBit(castValue, ROUND_UP_AGLIN(this->curLoopSortedNum_));
200- }
201- this->castValueQueue_.EnQue(castValue);
202- this->castIndexQueue_.EnQue(castIndex);
203- castValue = this->castValueQueue_.template DeQue<CONVERT_TYPE>();
204- castIndex = this->castIndexQueue_.template DeQue<uint32_t>();
205- 
206- uint32_t sortedBase = static_cast<uint32_t>(this->outOffset_);
207- uint32_t sortedEnd = sortedBase + static_cast<uint32_t>(this->curLoopSortedNum_);
208- // Extract chunks are scanned in sorted order. Only the chunk covering kthIndex_ writes GM output;
209- // other chunks are merge intermediates and are discarded for KthValue.
210- if (kthIndex_ >= sortedBase && kthIndex_ < sortedEnd) {
211- uint32_t localK = kthIndex_ - sortedBase;
212- event_t eventIdVToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_S));
213- SetFlag<HardEvent::V_S>(eventIdVToS);
214- WaitFlag<HardEvent::V_S>(eventIdVToS);
215- 
216- LocalTensor<T> outValue = this->outValueQueue_.template AllocTensor<T>();
217- LocalTensor<INDEX_TYPE> outIndex = this->outIndexQueue_.template AllocTensor<INDEX_TYPE>();
218- outValue.SetValue(0, castValue.GetValue(localK));
219- outIndex.SetValue(0, static_cast<INDEX_TYPE>(castIndex.GetValue(localK)));
220- this->outValueQueue_.EnQue(outValue);
221- this->outIndexQueue_.EnQue(outIndex);
222- 
223- outValue = this->outValueQueue_.template DeQue<T>();
224- outIndex = this->outIndexQueue_.template DeQue<INDEX_TYPE>();
225- event_t eventIdSToMte3 = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::S_MTE3));
226- SetFlag<HardEvent::S_MTE3>(eventIdSToMte3);
227- WaitFlag<HardEvent::S_MTE3>(eventIdSToMte3);
228- DataCopyExtParams valueCopyParam{1, static_cast<uint32_t>(sizeof(T)), 0, 0, 0};
229- DataCopyPad(this->outValueGm_[this->rowIdx_], outValue, valueCopyParam);
230- DataCopyExtParams indexCopyParam{1, static_cast<uint32_t>(sizeof(INDEX_TYPE)), 0, 0, 0};
231- DataCopyPad(this->outIndexGm_[this->rowIdx_], outIndex, indexCopyParam);
232- event_t eventIdMte3ToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::MTE3_S));
233- SetFlag<HardEvent::MTE3_S>(eventIdMte3ToS);
234- WaitFlag<HardEvent::MTE3_S>(eventIdMte3ToS);
235- this->outValueQueue_.FreeTensor(outValue);
236- this->outIndexQueue_.FreeTensor(outIndex);
237- this->allRemainElements_ = 0;
238- }
239- this->castIndexQueue_.FreeTensor(castIndex);
240- this->castValueQueue_.FreeTensor(castValue);
241- this->sortedQueue_.FreeTensor(sortTempBuffer);
242- this->outOffset_ += this->curLoopSortedNum_;
243-}
244-} // namespace KthValue
245-#endif // KTH_VALUE_MERGE_SORT_MORE_CORE_H
@@ -1,308 +0,0 @@
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-#ifndef KTH_VALUE_MERGE_SORT_ONE_CORE_H
12-#define KTH_VALUE_MERGE_SORT_ONE_CORE_H
13- 
14-#include <cmath>
15-#include "kernel_operator.h"
16-#include "op_kernel/platform_util.h"
17-#include "kth_value_median_utils.h"
18-#include "kth_value_tiling_data.h"
19-#include "../../sort/arch35/common/merge_sort_constants.h"
20-#include "../../sort/arch35/common/util_type_simd.h"
21- 
22-namespace KthValue {
23-using namespace AscendC;
24- 
25-using MergeSortConstants::DEALING_CONCAT_NUM_ONCE;
26-using MergeSortConstants::DEALING_SORT_NUM_ONCE;
27-using MergeSortConstants::XOR_OP_VALUE_FP;
28-using MergeSortConstants::XOR_OP_VALUE_HALF;
29- 
30-constexpr uint32_t UB_BLOCK_SIZE = Ops::Base::GetUbBlockSize();
31- 
32-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis = 0, bool EnableMedian = false>
33-class KthValueMergeSortOneCore {
34-public:
35- __aicore__ inline void Init(GM_ADDR x, GM_ADDR values, GM_ADDR indices, const KthValueTilingData* tiling,
36- TPipe* pipe);
37- __aicore__ inline void Process();
38- 
39-private:
40- __aicore__ inline void ParseTilingData();
41- __aicore__ inline void InitIndexLocal();
42- __aicore__ inline void CopyDataIn(uint64_t tileOffset, uint32_t currTileSize, uint32_t rowNum);
43- __aicore__ inline void FlipSignBit(LocalTensor<CONVERT_TYPE> xLocal, uint32_t offset, uint32_t count);
44- __aicore__ inline void SortRows(LocalTensor<T> xLocal, LocalTensor<T> sortedValueLocal,
45- LocalTensor<uint32_t> sortedIndexLocal, uint32_t rowNum);
46- __aicore__ inline void CopyKthToGm(uint64_t outputOffset, uint32_t rowNum);
47- __aicore__ inline void ProcessSingleRound(uint32_t round);
48- 
49- static constexpr uint32_t SORT_STRUCT_BYTES = 8;
50- 
51- GlobalTensor<T> xGm_;
52- GlobalTensor<T> valuesGm_;
53- GlobalTensor<int64_t> indicesGm_;
54- 
55- TPipe* pipe_{nullptr};
56- const KthValueTilingData* tiling_{nullptr};
57- TQue<QuePosition::VECIN, 1> inQueueX_;
58- TQue<QuePosition::VECOUT, 1> outValueQueue_;
59- TQue<QuePosition::VECOUT, 1> outIndexQueue_;
60- TQue<QuePosition::VECOUT, 1> compactValueQueue_;
61- TQue<QuePosition::VECOUT, 1> compactIndexQueue_;
62- TBuf<TPosition::VECCALC> concatTmpBuf_;
63- TBuf<TPosition::VECCALC> sortTmpBuf_;
64- TBuf<TPosition::VECCALC> sortedLocalBuf_;
65- TBuf<TPosition::VECCALC> xCastBuf_;
66- TBuf<TPosition::VECCALC> sortedValueCastBuf_;
67- TBuf<TPosition::VECCALC> indexLocalBuf_;
68- LocalTensor<uint32_t> indexLocal_;
69- 
70- uint32_t blockIdx_ = 0;
71- uint32_t oneCoreRowNum_ = 0;
72- uint32_t numTileData_ = 0;
73- uint32_t alignSize_ = 0;
74- uint32_t sortLoopTimes_ = 0;
75- uint32_t unsortedDimParallel_ = 0;
76- uint32_t kthIndex_ = 0;
77- uint32_t medianMode_ = 0;
78- int64_t unsortedDimNum_ = 0;
79-};
80- 
81-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
82-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::Init(
83- GM_ADDR x, GM_ADDR values, GM_ADDR indices, const KthValueTilingData* tiling, TPipe* pipe)
84-{
85- if (tiling == nullptr || pipe == nullptr) {
86- return;
87- }
88- blockIdx_ = GetBlockIdx();
89- pipe_ = pipe;
90- tiling_ = tiling;
91- ParseTilingData();
92- 
93- xGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(x));
94- valuesGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(values));
95- indicesGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int64_t*>(indices));
96- 
97- uint32_t bufferNum = tiling_->keyParams4 == 0U ? 1U : tiling_->keyParams4;
98- pipe_->InitBuffer(inQueueX_, bufferNum, tiling_->keyParams1);
99- pipe_->InitBuffer(outValueQueue_, bufferNum, tiling_->keyParams1);
100- pipe_->InitBuffer(outIndexQueue_, bufferNum, tiling_->keyParams2);
101- pipe_->InitBuffer(compactValueQueue_, bufferNum, ROUND_UP_AGLIN(oneCoreRowNum_ * sizeof(T)));
102- pipe_->InitBuffer(compactIndexQueue_, bufferNum, ROUND_UP_AGLIN(oneCoreRowNum_ * sizeof(int64_t)));
103- 
104- uint32_t sortStructElems = alignSize_ * SORT_STRUCT_BYTES;
105- pipe_->InitBuffer(indexLocalBuf_, alignSize_ * sizeof(uint32_t));
106- pipe_->InitBuffer(concatTmpBuf_, tiling_->tmpUbSize);
107- pipe_->InitBuffer(sortTmpBuf_, sortStructElems * sizeof(CONVERT_TYPE));
108- pipe_->InitBuffer(sortedLocalBuf_, sortStructElems * sizeof(CONVERT_TYPE));
109- if constexpr (!IsSameType<T, CONVERT_TYPE>::value) {
110- pipe_->InitBuffer(xCastBuf_, alignSize_ * oneCoreRowNum_ * sizeof(CONVERT_TYPE));
111- pipe_->InitBuffer(sortedValueCastBuf_, alignSize_ * oneCoreRowNum_ * sizeof(CONVERT_TYPE));
112- }
113- indexLocal_ = indexLocalBuf_.AllocTensor<uint32_t>();
114- InitIndexLocal();
115-}
116- 
117-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
118-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::ParseTilingData()
119-{
120- oneCoreRowNum_ = tiling_->keyParams0;
121- numTileData_ = tiling_->numTileDataSize;
122- alignSize_ = tiling_->keyParams3;
123- sortLoopTimes_ = tiling_->sortLoopTimes;
124- unsortedDimParallel_ = tiling_->unsortedDimParallel;
125- kthIndex_ = tiling_->kthIndex;
126- medianMode_ = tiling_->medianMode;
127- unsortedDimNum_ = tiling_->unsortedDimNum;
128-}
129- 
130-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
131-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::InitIndexLocal()
132-{
133- __ubuf__ int32_t* indexValuePtr = reinterpret_cast<__ubuf__ int32_t*>(indexLocal_.GetPhyAddr());
134- uint32_t vfLenB32 = Ops::Base::GetVRegSize() / sizeof(int32_t);
135- uint16_t repeatTime = Ops::Base::CeilDiv(alignSize_, vfLenB32);
136- uint32_t alignSizeCopy = alignSize_;
137- __VEC_SCOPE__
138- {
139- Reg::RegTensor<int32_t> vciTensor;
140- Reg::RegTensor<int32_t> indexTensor;
141- Reg::Arange(vciTensor, 0);
142- for (uint16_t i = 0; i < repeatTime; ++i) {
143- Reg::MaskReg mask = Reg::UpdateMask<uint32_t>(alignSizeCopy);
144- Reg::Adds(indexTensor, vciTensor, i * vfLenB32, mask);
145- Reg::StoreAlign<int32_t, Reg::PostLiteral::POST_MODE_UPDATE>(indexValuePtr, indexTensor, vfLenB32, mask);
146- }
147- }
148-}
149- 
150-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
151-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::CopyDataIn(
152- uint64_t tileOffset, uint32_t currTileSize, uint32_t rowNum)
153-{
154- LocalTensor<T> xLocal = inQueueX_.AllocTensor<T>();
155- Duplicate(xLocal, static_cast<T>(NAN), alignSize_ * rowNum);
156- event_t eventId = static_cast<event_t>(pipe_->FetchEventID(HardEvent::V_MTE2));
157- SetFlag<HardEvent::V_MTE2>(eventId);
158- WaitFlag<HardEvent::V_MTE2>(eventId);
159- 
160- uint32_t currTileSizeAlign = (currTileSize * sizeof(T) + UB_BLOCK_SIZE - 1U) / UB_BLOCK_SIZE * UB_BLOCK_SIZE /
161- sizeof(T);
162- uint32_t dstStride = ((alignSize_ - currTileSizeAlign) * sizeof(T)) / UB_BLOCK_SIZE;
163- DataCopyPadExtParams<T> padParams{true, 0, static_cast<uint8_t>(currTileSizeAlign - currTileSize),
164- static_cast<T>(NAN)};
165- DataCopyExtParams copyParam{static_cast<uint16_t>(rowNum), static_cast<uint32_t>(currTileSize * sizeof(T)), 0,
166- dstStride, 0};
167- DataCopyPad(xLocal, xGm_[tileOffset], copyParam, padParams);
168- inQueueX_.EnQue<T>(xLocal);
169-}
170- 
171-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
172-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::FlipSignBit(
173- LocalTensor<CONVERT_TYPE> xLocal, uint32_t offset, uint32_t count)
174-{
175- if constexpr (IsSameType<float, CONVERT_TYPE>::value) {
176- LocalTensor<int32_t> castTensor = xLocal[offset].template ReinterpretCast<int32_t>();
177- Adds(castTensor, castTensor, XOR_OP_VALUE_FP, count);
178- } else if constexpr (IsSameType<half, CONVERT_TYPE>::value) {
179- LocalTensor<int16_t> castTensor = xLocal[offset].template ReinterpretCast<int16_t>();
180- Adds(castTensor, castTensor, XOR_OP_VALUE_HALF, count);
181- }
182-}
183- 
184-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
185-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::SortRows(
186- LocalTensor<T> xLocal, LocalTensor<T> sortedValueLocal, LocalTensor<uint32_t> sortedIndexLocal, uint32_t rowNum)
187-{
188- uint32_t sortRepeatTimes = alignSize_ / DEALING_SORT_NUM_ONCE;
189- uint32_t concatRepeatTimes = alignSize_ / DEALING_CONCAT_NUM_ONCE;
190- LocalTensor<CONVERT_TYPE> concatTmp = concatTmpBuf_.Get<CONVERT_TYPE>();
191- LocalTensor<CONVERT_TYPE> sortedLocal = sortedLocalBuf_.Get<CONVERT_TYPE>();
192- LocalTensor<CONVERT_TYPE> sortTmp = sortTmpBuf_.Get<CONVERT_TYPE>();
193- LocalTensor<CONVERT_TYPE> xSortLocal;
194- LocalTensor<CONVERT_TYPE> sortedValueCast;
195- if constexpr (!IsSameType<T, CONVERT_TYPE>::value) {
196- xSortLocal = xCastBuf_.Get<CONVERT_TYPE>();
197- sortedValueCast = sortedValueCastBuf_.Get<CONVERT_TYPE>();
198- Cast(xSortLocal, xLocal, RoundMode::CAST_NONE, alignSize_ * rowNum);
199- } else {
200- xSortLocal = xLocal;
201- sortedValueCast = sortedValueLocal;
202- }
203- for (uint32_t row = 0; row < rowNum; ++row) {
204- uint32_t offset = row * alignSize_;
205- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<CONVERT_TYPE>) {
206- if (medianMode_ != MEDIAN_MODE_STATIC) {
207- CanonicalizeNanValues(xSortLocal[offset], alignSize_);
208- }
209- }
210- // MergeSort sorts sign-flipped ascending keys. Flip before Sort/Sort32 and flip back after Extract.
211- FlipSignBit(xSortLocal, offset, alignSize_);
212- if constexpr (isSort32SmallAxis == 1) {
213- Sort32<CONVERT_TYPE>(sortedLocal, xSortLocal[offset], indexLocal_, 1);
214- } else {
215- LocalTensor<CONVERT_TYPE> concatLocal;
216- Concat(concatLocal, xSortLocal[offset], concatTmp, concatRepeatTimes);
217- AscendC::Sort<CONVERT_TYPE, true>(sortedLocal, concatLocal, indexLocal_, sortTmp, sortRepeatTimes);
218- }
219- Extract(sortedValueCast[offset], sortedIndexLocal[offset], sortedLocal,
220- isSort32SmallAxis == 1 ? 1 : sortRepeatTimes);
221- FlipSignBit(sortedValueCast, offset, alignSize_);
222- }
223- if constexpr (!IsSameType<T, CONVERT_TYPE>::value) {
224- Cast(sortedValueLocal, sortedValueCast, RoundMode::CAST_RINT, alignSize_ * rowNum);
225- }
226-}
227- 
228-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
229-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::CopyKthToGm(
230- uint64_t outputOffset, uint32_t rowNum)
231-{
232- LocalTensor<T> sortedValueLocal = outValueQueue_.DeQue<T>();
233- LocalTensor<uint32_t> sortedIndexLocal = outIndexQueue_.DeQue<uint32_t>();
234- 
235- event_t eventIdVToS = static_cast<event_t>(pipe_->FetchEventID(HardEvent::V_S));
236- SetFlag<HardEvent::V_S>(eventIdVToS);
237- WaitFlag<HardEvent::V_S>(eventIdVToS);
238- 
239- LocalTensor<T> compactValue = compactValueQueue_.AllocTensor<T>();
240- LocalTensor<int64_t> compactIndex = compactIndexQueue_.AllocTensor<int64_t>();
241- for (uint32_t row = 0; row < rowNum; ++row) {
242- uint32_t rowOffset = row * alignSize_;
243- uint32_t selectedK = kthIndex_;
244- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
245- uint32_t nonNanCount = CountNonNan(sortedValueLocal[rowOffset], numTileData_, concatTmpBuf_.Get<float>(),
246- pipe_);
247- selectedK = ResolveMedianK(kthIndex_, numTileData_, nonNanCount, medianMode_);
248- }
249- uint32_t srcOffset = rowOffset + selectedK;
250- compactValue.SetValue(row, sortedValueLocal.GetValue(srcOffset));
251- compactIndex.SetValue(row, static_cast<int64_t>(sortedIndexLocal.GetValue(srcOffset)));
252- }
253- compactValueQueue_.EnQue<T>(compactValue);
254- compactIndexQueue_.EnQue<int64_t>(compactIndex);
255- compactValue = compactValueQueue_.DeQue<T>();
256- compactIndex = compactIndexQueue_.DeQue<int64_t>();
257- event_t eventIdSToMte3 = static_cast<event_t>(pipe_->FetchEventID(HardEvent::S_MTE3));
258- SetFlag<HardEvent::S_MTE3>(eventIdSToMte3);
259- WaitFlag<HardEvent::S_MTE3>(eventIdSToMte3);
260- DataCopyExtParams valueCopyParam{1, static_cast<uint32_t>(rowNum * sizeof(T)), 0, 0, 0};
261- DataCopyPad(valuesGm_[outputOffset], compactValue, valueCopyParam);
262- DataCopyExtParams indexCopyParam{1, static_cast<uint32_t>(rowNum * sizeof(int64_t)), 0, 0, 0};
263- DataCopyPad(indicesGm_[outputOffset], compactIndex, indexCopyParam);
264- compactValueQueue_.FreeTensor(compactValue);
265- compactIndexQueue_.FreeTensor(compactIndex);
266- outValueQueue_.FreeTensor(sortedValueLocal);
267- outIndexQueue_.FreeTensor(sortedIndexLocal);
268-}
269- 
270-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
271-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::ProcessSingleRound(
272- uint32_t round)
273-{
274- int64_t rowStart = (static_cast<int64_t>(blockIdx_) + static_cast<int64_t>(round) * unsortedDimParallel_) *
275- oneCoreRowNum_;
276- if (rowStart >= unsortedDimNum_) {
277- return;
278- }
279- uint32_t rowNum = oneCoreRowNum_;
280- int64_t remain = unsortedDimNum_ - rowStart;
281- if (remain < static_cast<int64_t>(rowNum)) {
282- rowNum = static_cast<uint32_t>(remain);
283- }
284- uint64_t inputOffset = static_cast<uint64_t>(rowStart) * numTileData_;
285- CopyDataIn(inputOffset, numTileData_, rowNum);
286- LocalTensor<T> xLocal = inQueueX_.DeQue<T>();
287- LocalTensor<T> sortedValueLocal = outValueQueue_.AllocTensor<T>();
288- LocalTensor<uint32_t> sortedIndexLocal = outIndexQueue_.AllocTensor<uint32_t>();
289- SortRows(xLocal, sortedValueLocal, sortedIndexLocal, rowNum);
290- outValueQueue_.EnQue<T>(sortedValueLocal);
291- outIndexQueue_.EnQue<uint32_t>(sortedIndexLocal);
292- inQueueX_.FreeTensor(xLocal);
293- CopyKthToGm(static_cast<uint64_t>(rowStart), rowNum);
294-}
295- 
296-template <typename T, typename CONVERT_TYPE, uint64_t isSort32SmallAxis, bool EnableMedian>
297-__aicore__ inline void KthValueMergeSortOneCore<T, CONVERT_TYPE, isSort32SmallAxis, EnableMedian>::Process()
298-{
299- if (blockIdx_ >= GetBlockNum()) {
300- return;
301- }
302- for (uint32_t round = 0; round < sortLoopTimes_; ++round) {
303- ProcessSingleRound(round);
304- }
305-}
306-} // namespace KthValue
307- 
308-#endif
@@ -1,332 +0,0 @@
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-#ifndef KTH_VALUE_NON_LAST_SMALL_AXIS_H
12-#define KTH_VALUE_NON_LAST_SMALL_AXIS_H
13- 
14-#include <type_traits>
15- 
16-#include "kernel_operator.h"
17-#include "kernel_tiling/kernel_tiling.h"
18-#include "op_kernel/platform_util.h"
19-#include "simt_api/asc_simt.h"
20-#include "kth_value_median_utils.h"
21-#include "kth_value_tiling_data.h"
22-#include "../../sort/arch35/common/non_last_small_axis_base.h"
23- 
24-namespace KthValue {
25-using namespace AscendC;
26- 
27-// Median hook (pre-sort): canonicalizes NaN/signed-zero in place over the transposed
28-// sort-major layout, which is strided (valueAxisElems), hence a scalar SIMT pass.
29-template <typename T>
30-__simt_vf__ LAUNCH_BOUND(SmallAxisCommon::NON_LAST_TRANSPOSE_THREAD_NUM) __aicore__
31- void SimtCanonicalizeNonLastMedianRows(uint32_t validElems, uint32_t axisLen, uint32_t valueAxisElems,
32- __ubuf__ T* sortInput)
33-{
34- for (uint32_t idx = static_cast<uint32_t>(threadIdx.x); idx < validElems;
35- idx += SmallAxisCommon::NON_LAST_TRANSPOSE_THREAD_NUM) {
36- uint32_t inner = idx / axisLen;
37- uint32_t axis = idx - inner * axisLen;
38- uint32_t offset = inner * valueAxisElems + axis;
39- sortInput[offset] = CanonicalizeMedianSortValue(sortInput[offset]);
40- }
41-}
42- 
43-// Median hook (post-sort, per inner lane): binary-searches the NaN boundary on the sorted
44-// row, resolves the effective k per medianMode, then traces back the selected element to the
45-// original input tile (raw T layout), preserving precision and the original NaN payload.
46-template <typename T, typename SortT>
47-__simt_vf__ LAUNCH_BOUND(SmallAxisCommon::NON_LAST_TRANSPOSE_THREAD_NUM) __aicore__
48- void SimtStoreNonLastMedian(uint32_t curInnerChunk, uint32_t axisLen, uint32_t kthIndex, uint32_t medianMode,
49- uint32_t inputRowElems, uint32_t valueAxisElems, uint32_t indexAxisElems,
50- __ubuf__ T* inputTile, __ubuf__ SortT* sortedValue, __ubuf__ uint32_t* sortedIndex,
51- __ubuf__ T* compactValue, __ubuf__ int64_t* compactIndex)
52-{
53- for (uint32_t inner = static_cast<uint32_t>(threadIdx.x); inner < curInnerChunk;
54- inner += SmallAxisCommon::NON_LAST_TRANSPOSE_THREAD_NUM) {
55- uint32_t rowOffset = inner * valueAxisElems;
56- uint32_t left = 0U;
57- uint32_t right = axisLen;
58- while (left < right) {
59- uint32_t middle = left + (right - left) / 2U;
60- if (IsNanValue(sortedValue[rowOffset + middle])) {
61- right = middle;
62- } else {
63- left = middle + 1U;
64- }
65- }
66- uint32_t selectedK = kthIndex;
67- if (medianMode == MEDIAN_MODE_PROPAGATE_NAN && left < axisLen) {
68- selectedK = left;
69- } else if (medianMode == MEDIAN_MODE_IGNORE_NAN) {
70- selectedK = left == 0U ? 0U : (left - 1U) / 2U;
71- }
72- uint32_t selectedIndex = sortedIndex[inner * indexAxisElems + selectedK];
73- compactValue[inner] = inputTile[selectedIndex * inputRowElems + inner];
74- compactIndex[inner] = static_cast<int64_t>(selectedIndex);
75- }
76-}
77- 
78-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian = false>
79-class KthValueNonLastSmallAxis
80- : public SmallAxisCommon::NonLastSmallAxisBase<
81- KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>, T,
82- std::conditional_t<UseMergeSort && std::is_same_v<T, bfloat16_t>, float, T>,
83- std::conditional_t<sizeof(T) <= sizeof(int16_t), int16_t, int32_t>,
84- std::conditional_t<sizeof(T) <= sizeof(int16_t), uint16_t, uint32_t>,
85- std::conditional_t<sizeof(T) == 1, std::conditional_t<std::is_same_v<T, uint8_t>, uint16_t, int16_t>, T>,
86- IsDescend, UseMergeSort, UseMergeSort && std::is_same_v<T, bfloat16_t>,
87- !UseMergeSort && SignedZeroSortCommon::IS_FLOATING_POINT_V<T>> {
88- using Base = SmallAxisCommon::NonLastSmallAxisBase<
89- KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>, T,
90- std::conditional_t<UseMergeSort && std::is_same_v<T, bfloat16_t>, float, T>,
91- std::conditional_t<sizeof(T) <= sizeof(int16_t), int16_t, int32_t>,
92- std::conditional_t<sizeof(T) <= sizeof(int16_t), uint16_t, uint32_t>,
93- std::conditional_t<sizeof(T) == 1, std::conditional_t<std::is_same_v<T, uint8_t>, uint16_t, int16_t>, T>,
94- IsDescend, UseMergeSort, UseMergeSort && std::is_same_v<T, bfloat16_t>,
95- !UseMergeSort && SignedZeroSortCommon::IS_FLOATING_POINT_V<T>>;
96- 
97- using SortT = std::conditional_t<UseMergeSort && std::is_same_v<T, bfloat16_t>, float, T>;
98- static constexpr bool IS_BF16_MERGE = UseMergeSort && std::is_same_v<T, bfloat16_t>;
99- static constexpr bool NORMALIZE_SIGNED_ZERO = !UseMergeSort && SignedZeroSortCommon::IS_FLOATING_POINT_V<T>;
100- 
101-public:
102- __aicore__ inline void Init(GM_ADDR x, GM_ADDR values, GM_ADDR indices, GM_ADDR workspace,
103- const KthValueTilingData* tilingData, TPipe* pipe);
104- 
105- __aicore__ inline void Process();
106- __aicore__ inline void StoreTile(int64_t inputOffset, int64_t outputOffset, uint32_t curInnerChunk);
107- 
108-private:
109- __aicore__ inline void ParseTilingData();
110- __aicore__ inline void PrepareSortRows(uint32_t curInnerChunk);
111- __aicore__ inline void CopyKthToOutput(uint32_t curInnerChunk, int64_t outputOffset);
112- 
113- const KthValueTilingData* tilingData_ = nullptr;
114- GlobalTensor<T> valueGm_;
115- GlobalTensor<int64_t> indexGm_;
116- 
117- TBuf<TPosition::VECCALC> compactValueBuf_;
118- TBuf<TPosition::VECCALC> compactCastValueBuf_;
119- TBuf<TPosition::VECCALC> compactIndexBuf_;
120- TBuf<TPosition::VECCALC> sourceIndexBuf_;
121- 
122- LocalTensor<T> compactValue_;
123- LocalTensor<SortT> compactCastValue_;
124- LocalTensor<int64_t> compactIndex_;
125- 
126- uint32_t kthIndex_ = 0;
127- uint32_t medianMode_ = MEDIAN_MODE_STATIC;
128-};
129- 
130-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian>
131-__aicore__ inline void KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>::Init(
132- GM_ADDR x, GM_ADDR values, GM_ADDR indices, GM_ADDR workspace, const KthValueTilingData* tilingData, TPipe* pipe)
133-{
134- (void)workspace;
135- if (tilingData == nullptr || pipe == nullptr) {
136- return;
137- }
138- this->pipe_ = pipe;
139- tilingData_ = tilingData;
140- this->blockIdx_ = GetBlockIdx();
141- this->blockDim_ = GetBlockNum();
142- ParseTilingData();
143- 
144- this->inputGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(x));
145- valueGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(values));
146- indexGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int64_t*>(indices));
147- 
148- if (this->axisLen_ == 0 || this->innerChunk_ == 0 || this->innerLoopNum_ == 0 || this->outerSize_ <= 0 ||
149- this->innerSize_ <= 0) {
150- return;
151- }
152- this->pipe_->InitBuffer(this->inputTileBuf_, this->axisLen_ * this->inputRowBytes_);
153- if constexpr (IS_BF16_MERGE) {
154- this->pipe_->InitBuffer(this->inputCastBuf_, this->innerChunk_ * this->inputValueAxisBytes_);
155- this->inputCast_ = this->inputCastBuf_.template Get<T>();
156- }
157- this->pipe_->InitBuffer(this->sortInputBuf_, this->innerChunk_ * this->valueAxisBytes_);
158- this->pipe_->InitBuffer(this->sortedValueBuf_, this->innerChunk_ * this->valueAxisBytes_);
159- this->pipe_->InitBuffer(this->sortedIndexBuf_, this->innerChunk_ * this->indexAxisBytes_);
160- this->pipe_->InitBuffer(compactValueBuf_, ROUND_UP_AGLIN(this->innerChunk_ * sizeof(T)));
161- if constexpr (IS_BF16_MERGE) {
162- this->pipe_->InitBuffer(compactCastValueBuf_, ROUND_UP_AGLIN(this->innerChunk_ * sizeof(SortT)));
163- compactCastValue_ = compactCastValueBuf_.template Get<SortT>();
164- }
165- this->pipe_->InitBuffer(compactIndexBuf_, ROUND_UP_AGLIN(this->innerChunk_ * sizeof(int64_t)));
166- if constexpr (NORMALIZE_SIGNED_ZERO) {
167- this->pipe_->InitBuffer(sourceIndexBuf_, this->indexAxisBytes_);
168- }
169- this->pipe_->InitBuffer(this->tmpBuf_, this->tmpUbSize_);
170- 
171- this->inputTile_ = this->inputTileBuf_.template Get<T>();
172- this->sortInput_ = this->sortInputBuf_.template Get<SortT>();
173- this->sortedValue_ = this->sortedValueBuf_.template Get<SortT>();
174- this->sortedIndex_ = this->sortedIndexBuf_.template Get<uint32_t>();
175- compactValue_ = compactValueBuf_.template Get<T>();
176- compactIndex_ = compactIndexBuf_.template Get<int64_t>();
177- this->tmp_ = this->tmpBuf_.template Get<uint8_t>();
178- if constexpr (NORMALIZE_SIGNED_ZERO) {
179- this->sourceIndex_ = sourceIndexBuf_.template Get<uint32_t>();
180- }
181-}
182- 
183-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian>
184-__aicore__ inline void KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>::Process()
185-{
186- if (this->blockIdx_ >= this->blockDim_ || this->axisLen_ == 0 || this->innerChunk_ == 0 ||
187- this->innerLoopNum_ == 0) {
188- return;
189- }
190- uint64_t tileCount = static_cast<uint64_t>(this->outerSize_) * static_cast<uint64_t>(this->innerLoopNum_);
191- uint64_t tilesPerCore = Ops::Base::CeilDiv(tileCount, static_cast<uint64_t>(this->blockDim_));
192- uint64_t startTile = static_cast<uint64_t>(this->blockIdx_) * tilesPerCore;
193- uint64_t endTile = startTile + tilesPerCore;
194- if (endTile > tileCount) {
195- endTile = tileCount;
196- }
197- for (uint64_t tileId = startTile; tileId < endTile; ++tileId) {
198- uint64_t outerId = tileId / this->innerLoopNum_;
199- uint32_t innerTileId = static_cast<uint32_t>(tileId - outerId * static_cast<uint64_t>(this->innerLoopNum_));
200- uint32_t curInnerChunk = this->GetCurrentInnerChunk(innerTileId);
201- if (curInnerChunk == 0U) {
202- continue;
203- }
204- int64_t innerStart = static_cast<int64_t>(innerTileId) * static_cast<int64_t>(this->innerChunk_);
205- int64_t inputOffset = static_cast<int64_t>(outerId) * static_cast<int64_t>(this->axisLen_) * this->innerSize_ +
206- innerStart;
207- int64_t outputOffset = static_cast<int64_t>(outerId) * this->innerSize_ + innerStart;
208- this->LoadTile(inputOffset, curInnerChunk);
209- this->TransposeToSortMajor(curInnerChunk);
210- PrepareSortRows(curInnerChunk);
211- this->SortRows(curInnerChunk);
212- StoreTile(inputOffset, outputOffset, curInnerChunk);
213- }
214-}
215- 
216-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian>
217-__aicore__ inline void KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>::ParseTilingData()
218-{
219- this->axisLen_ = static_cast<uint32_t>(tilingData_->lastAxisNum);
220- kthIndex_ = static_cast<uint32_t>(tilingData_->kthIndex);
221- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
222- medianMode_ = tilingData_->medianMode;
223- }
224- this->outerSize_ = tilingData_->outerSize;
225- this->innerSize_ = tilingData_->innerSize;
226- this->innerLoopNum_ = tilingData_->innerLoopNum;
227- this->innerChunk_ = tilingData_->innerChunk;
228- this->inputRowBytes_ = tilingData_->inputRowBytes;
229- this->valueAxisBytes_ = tilingData_->valueAxisBytes;
230- this->indexAxisBytes_ = tilingData_->indexAxisBytes;
231- this->inputRowElems_ = this->inputRowBytes_ / sizeof(T);
232- this->valueAxisElems_ = this->valueAxisBytes_ / sizeof(SortT);
233- this->indexAxisElems_ = this->indexAxisBytes_ / sizeof(uint32_t);
234- this->sortCount_ = tilingData_->keyParams0 == 0U ? this->axisLen_ : tilingData_->keyParams0;
235- if constexpr (IS_BF16_MERGE) {
236- this->inputValueAxisBytes_ = tilingData_->keyParams1;
237- this->inputValueAxisElems_ = this->inputValueAxisBytes_ / sizeof(T);
238- }
239- this->tmpUbSize_ = tilingData_->tmpUbSize;
240-}
241- 
242-// Median hook point: runs after transpose, before SortRows.
243-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian>
244-__aicore__ inline void KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>::PrepareSortRows(
245- uint32_t curInnerChunk)
246-{
247- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
248- if (medianMode_ != MEDIAN_MODE_STATIC) {
249- asc_vf_call<SimtCanonicalizeNonLastMedianRows<SortT>>(
250- dim3(SmallAxisCommon::NON_LAST_TRANSPOSE_THREAD_NUM), curInnerChunk * this->axisLen_, this->axisLen_,
251- this->valueAxisElems_, reinterpret_cast<__ubuf__ SortT*>(this->sortInput_.GetPhyAddr()));
252- event_t eventIdVToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_S));
253- SetFlag<HardEvent::V_S>(eventIdVToS);
254- WaitFlag<HardEvent::V_S>(eventIdVToS);
255- }
256- }
257-}
258- 
259-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian>
260-__aicore__ inline void KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>::CopyKthToOutput(
261- uint32_t curInnerChunk, int64_t outputOffset)
262-{
263- event_t eventIdVToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_S));
264- SetFlag<HardEvent::V_S>(eventIdVToS);
265- WaitFlag<HardEvent::V_S>(eventIdVToS);
266- uint32_t compactCastElems = 0U;
267- if constexpr (IS_BF16_MERGE && !EnableMedian) {
268- compactCastElems = ROUND_UP_AGLIN(curInnerChunk * sizeof(SortT)) / sizeof(SortT);
269- Duplicate(compactCastValue_, static_cast<SortT>(0), compactCastElems);
270- event_t eventIdVToSForInit = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_S));
271- SetFlag<HardEvent::V_S>(eventIdVToSForInit);
272- WaitFlag<HardEvent::V_S>(eventIdVToSForInit);
273- }
274- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
275- // Median hook point: k resolution fused with value extraction, tracing back to the raw input tile.
276- asc_vf_call<SimtStoreNonLastMedian<T, SortT>>(
277- dim3(SmallAxisCommon::NON_LAST_TRANSPOSE_THREAD_NUM), curInnerChunk, this->axisLen_, kthIndex_, medianMode_,
278- this->inputRowElems_, this->valueAxisElems_, this->indexAxisElems_,
279- reinterpret_cast<__ubuf__ T*>(this->inputTile_.GetPhyAddr()),
280- reinterpret_cast<__ubuf__ SortT*>(this->sortedValue_.GetPhyAddr()),
281- reinterpret_cast<__ubuf__ uint32_t*>(this->sortedIndex_.GetPhyAddr()),
282- reinterpret_cast<__ubuf__ T*>(compactValue_.GetPhyAddr()),
283- reinterpret_cast<__ubuf__ int64_t*>(compactIndex_.GetPhyAddr()));
284- eventIdVToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_S));
285- SetFlag<HardEvent::V_S>(eventIdVToS);
286- WaitFlag<HardEvent::V_S>(eventIdVToS);
287- } else {
288- // Compact one kth element per inner lane. The GM output is [outer, 1, inner], so outputOffset
289- // points to a contiguous inner chunk.
290- for (uint32_t inner = 0; inner < curInnerChunk; ++inner) {
291- uint32_t srcOffset = inner * this->valueAxisElems_ + kthIndex_;
292- uint32_t idxOffset = inner * this->indexAxisElems_ + kthIndex_;
293- if constexpr (IS_BF16_MERGE) {
294- compactCastValue_.SetValue(inner, this->sortedValue_.GetValue(srcOffset));
295- } else {
296- compactValue_.SetValue(inner, this->sortedValue_.GetValue(srcOffset));
297- }
298- compactIndex_.SetValue(inner, static_cast<int64_t>(this->sortedIndex_.GetValue(idxOffset)));
299- }
300- }
301- if constexpr (IS_BF16_MERGE && !EnableMedian) {
302- event_t eventIdSToV = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::S_V));
303- SetFlag<HardEvent::S_V>(eventIdSToV);
304- WaitFlag<HardEvent::S_V>(eventIdSToV);
305- Cast(compactValue_, compactCastValue_, RoundMode::CAST_RINT, compactCastElems);
306- event_t eventIdVToMte3 = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::V_MTE3));
307- SetFlag<HardEvent::V_MTE3>(eventIdVToMte3);
308- WaitFlag<HardEvent::V_MTE3>(eventIdVToMte3);
309- }
310- event_t eventIdSToMte3 = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::S_MTE3));
311- SetFlag<HardEvent::S_MTE3>(eventIdSToMte3);
312- WaitFlag<HardEvent::S_MTE3>(eventIdSToMte3);
313- DataCopyExtParams valueCopyParam{1, static_cast<uint32_t>(curInnerChunk * sizeof(T)), 0, 0, 0};
314- DataCopyPad(valueGm_[outputOffset], compactValue_, valueCopyParam);
315- DataCopyExtParams indexCopyParam{1, static_cast<uint32_t>(curInnerChunk * sizeof(int64_t)), 0, 0, 0};
316- DataCopyPad(indexGm_[outputOffset], compactIndex_, indexCopyParam);
317- event_t eventIdMte3ToS = static_cast<event_t>(this->pipe_->FetchEventID(HardEvent::MTE3_S));
318- SetFlag<HardEvent::MTE3_S>(eventIdMte3ToS);
319- WaitFlag<HardEvent::MTE3_S>(eventIdMte3ToS);
320-}
321- 
322-template <typename T, bool IsDescend, bool UseMergeSort, bool EnableMedian>
323-__aicore__ inline void KthValueNonLastSmallAxis<T, IsDescend, UseMergeSort, EnableMedian>::StoreTile(
324- int64_t inputOffset, int64_t outputOffset, uint32_t curInnerChunk)
325-{
326- (void)inputOffset;
327- CopyKthToOutput(curInnerChunk, outputOffset);
328-}
329- 
330-} // namespace KthValue
331- 
332-#endif
@@ -1,237 +0,0 @@
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-#ifndef KTH_VALUE_RADIX_ONE_CORE_H
12-#define KTH_VALUE_RADIX_ONE_CORE_H
13- 
14-#include "kernel_tiling/kernel_tiling.h"
15-#include "kernel_operator.h"
16-#include "op_kernel/platform_util.h"
17-#include "kth_value_median_utils.h"
18-#include "kth_value_tiling_data.h"
19-#include "../../sort/arch35/common/signed_zero_sort_utils.h"
20-#include "../../sort/arch35/common/util_type_simd.h"
21- 
22-namespace KthValue {
23-using namespace AscendC;
24- 
25-template <typename T, bool EnableMedian = false>
26-class KthValueRadixOneCore {
27-public:
28- __aicore__ inline void Init(GM_ADDR x, GM_ADDR values, GM_ADDR indices, const KthValueTilingData* tiling,
29- TPipe* pipe);
30- __aicore__ inline void Process();
31- 
32-private:
33- __aicore__ inline void ParseTilingData();
34- __aicore__ inline void CopyInputToUb(LocalTensor<T>& xLocal, int64_t row);
35- __aicore__ inline void ProcessOneRow(int64_t row, uint32_t localOffset, LocalTensor<T>& compactValue,
36- LocalTensor<int64_t>& compactIndex);
37- __aicore__ inline void CopyOutputToGm(int64_t rowStart, uint32_t rowCount, LocalTensor<T>& compactValue,
38- LocalTensor<int64_t>& compactIndex);
39- __aicore__ inline void ProcessRows(int64_t rowStart, uint32_t rowCount);
40- 
41- static constexpr SortConfig SORT_CONFIG{SortType::RADIX_SORT, false};
42- static constexpr uint32_t UB_BLOCK_SIZE = Ops::Base::GetUbBlockSize();
43- 
44- GlobalTensor<T> xGm_;
45- GlobalTensor<T> valuesGm_;
46- GlobalTensor<int64_t> indicesGm_;
47- 
48- TPipe* pipe_{nullptr};
49- const KthValueTilingData* tiling_{nullptr};
50- TQue<QuePosition::VECIN, 1> inQueueX_;
51- TQue<QuePosition::VECOUT, 1> outValueQueue_;
52- TQue<QuePosition::VECOUT, 1> outIndexQueue_;
53- TQue<QuePosition::VECOUT, 1> compactValueQueue_;
54- TQue<QuePosition::VECOUT, 1> compactIndexQueue_;
55- TBuf<TPosition::VECCALC> tmpUb_;
56- TBuf<TPosition::VECCALC> sourceOrderBuf_;
57- LocalTensor<uint32_t> sourceOrder_;
58- 
59- uint32_t blockIdx_ = 0;
60- uint32_t blockNum_ = 0;
61- uint32_t numTileData_ = 0;
62- uint32_t kthIndex_ = 0;
63- uint32_t medianMode_ = 0;
64- uint32_t unsortedDimParallel_ = 0;
65- uint32_t xUbSize_ = 0;
66- uint32_t idxUbSize_ = 0;
67- uint32_t bufferNum_ = 1;
68- uint32_t tmpUbSize_ = 0;
69- uint32_t outputRowsPerLoop_ = 1;
70- int64_t lastAxisNum_ = 0;
71- int64_t unsortedDimNum_ = 0;
72-};
73- 
74-template <typename T, bool EnableMedian>
75-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::Init(GM_ADDR x, GM_ADDR values, GM_ADDR indices,
76- const KthValueTilingData* tiling, TPipe* pipe)
77-{
78- if (tiling == nullptr || pipe == nullptr) {
79- return;
80- }
81- blockIdx_ = GetBlockIdx();
82- blockNum_ = GetBlockNum();
83- pipe_ = pipe;
84- tiling_ = tiling;
85- ParseTilingData();
86- 
87- xGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(x));
88- valuesGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(values));
89- indicesGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int64_t*>(indices));
90- 
91- pipe_->InitBuffer(inQueueX_, bufferNum_, xUbSize_);
92- pipe_->InitBuffer(outValueQueue_, bufferNum_, xUbSize_);
93- pipe_->InitBuffer(outIndexQueue_, bufferNum_, idxUbSize_);
94- pipe_->InitBuffer(compactValueQueue_, bufferNum_, ROUND_UP_AGLIN(outputRowsPerLoop_ * sizeof(T)));
95- pipe_->InitBuffer(compactIndexQueue_, bufferNum_, ROUND_UP_AGLIN(outputRowsPerLoop_ * sizeof(int64_t)));
96- pipe_->InitBuffer(tmpUb_, tmpUbSize_);
97- if constexpr (SignedZeroSortCommon::IS_FLOATING_POINT_V<T>) {
98- pipe_->InitBuffer(sourceOrderBuf_, idxUbSize_);
99- sourceOrder_ = sourceOrderBuf_.Get<uint32_t>();
100- }
101-}
102- 
103-template <typename T, bool EnableMedian>
104-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::ParseTilingData()
105-{
106- numTileData_ = tiling_->numTileDataSize;
107- kthIndex_ = tiling_->kthIndex;
108- medianMode_ = tiling_->medianMode;
109- unsortedDimParallel_ = tiling_->unsortedDimParallel;
110- xUbSize_ = tiling_->keyParams0;
111- idxUbSize_ = tiling_->keyParams1;
112- bufferNum_ = tiling_->keyParams3 == 2U ? 2U : 1U;
113- outputRowsPerLoop_ = tiling_->keyParams4 == 0U ? 1U : tiling_->keyParams4;
114- tmpUbSize_ = tiling_->tmpUbSize;
115- lastAxisNum_ = tiling_->lastAxisNum;
116- unsortedDimNum_ = tiling_->unsortedDimNum;
117-}
118- 
119-template <typename T, bool EnableMedian>
120-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::CopyInputToUb(LocalTensor<T>& xLocal, int64_t row)
121-{
122- int64_t inputOffset = row * lastAxisNum_;
123- uint32_t copyBytes = static_cast<uint32_t>(numTileData_ * sizeof(T));
124- 
125- DataCopyExtParams inputCopyParam{1, copyBytes, 0, 0, 0};
126- DataCopyPadExtParams<T> inputPadParam{true, 0, 0, 0};
127- DataCopyPad(xLocal, xGm_[inputOffset], inputCopyParam, inputPadParam);
128-}
129- 
130-template <typename T, bool EnableMedian>
131-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::ProcessOneRow(int64_t row, uint32_t localOffset,
132- LocalTensor<T>& compactValue,
133- LocalTensor<int64_t>& compactIndex)
134-{
135- LocalTensor<T> xLocal = inQueueX_.AllocTensor<T>();
136- CopyInputToUb(xLocal, row);
137- inQueueX_.EnQue<T>(xLocal);
138- xLocal = inQueueX_.DeQue<T>();
139- LocalTensor<T> sortedValue = outValueQueue_.AllocTensor<T>();
140- LocalTensor<uint32_t> sortedIndex = outIndexQueue_.AllocTensor<uint32_t>();
141- LocalTensor<uint8_t> tmpUb = tmpUb_.Get<uint8_t>();
142- uint32_t selectedK = kthIndex_;
143- if constexpr (EnableMedian && IS_MEDIAN_FLOAT_TYPE<T>) {
144- if (medianMode_ != MEDIAN_MODE_STATIC) {
145- CanonicalizeNanValues(xLocal, numTileData_);
146- }
147- uint32_t nonNanCount = CountNonNan(xLocal, numTileData_, tmpUb_.Get<float>(), pipe_);
148- selectedK = ResolveMedianK(kthIndex_, numTileData_, nonNanCount, medianMode_);
149- }
150- 
151- bool hasNegativeZero = false;
152- if constexpr (SignedZeroSortCommon::IS_FLOATING_POINT_V<T>) {
153- hasNegativeZero = SignedZeroSortCommon::HasNegativeZeroVec(xLocal, sourceOrder_, numTileData_);
154- if (hasNegativeZero) {
155- SignedZeroSortCommon::PrepareSignedZeroKeysVec(xLocal, sourceOrder_, numTileData_);
156- }
157- AscendC::Sort<T, false, SORT_CONFIG>(sortedValue, sortedIndex, xLocal, tmpUb, numTileData_);
158- if (hasNegativeZero) {
159- SignedZeroSortCommon::RestoreSignedZeroValuesByIndexVec(sortedValue, sortedIndex, sourceOrder_,
160- numTileData_);
161- }
162- } else {
163- AscendC::Sort<T, false, SORT_CONFIG>(sortedValue, sortedIndex, xLocal, tmpUb, numTileData_);
164- }
165- inQueueX_.FreeTensor(xLocal);
166- outValueQueue_.EnQue<T>(sortedValue);
167- outIndexQueue_.EnQue<uint32_t>(sortedIndex);
168- sortedValue = outValueQueue_.DeQue<T>();
169- sortedIndex = outIndexQueue_.DeQue<uint32_t>();
170- event_t eventIdVToS = static_cast<event_t>(pipe_->FetchEventID(HardEvent::V_S));
171- SetFlag<HardEvent::V_S>(eventIdVToS);
172- WaitFlag<HardEvent::V_S>(eventIdVToS);
173- 
174- compactValue.SetValue(localOffset, sortedValue.GetValue(selectedK));
175- compactIndex.SetValue(localOffset, static_cast<int64_t>(sortedIndex.GetValue(selectedK)));
176- 
177- outValueQueue_.FreeTensor(sortedValue);
178- outIndexQueue_.FreeTensor(sortedIndex);
179-}
180- 
181-template <typename T, bool EnableMedian>
182-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::CopyOutputToGm(int64_t rowStart, uint32_t rowCount,
183- LocalTensor<T>& compactValue,
184- LocalTensor<int64_t>& compactIndex)
185-{
186- event_t eventIdSToMte3 = static_cast<event_t>(pipe_->FetchEventID(HardEvent::S_MTE3));
187- SetFlag<HardEvent::S_MTE3>(eventIdSToMte3);
188- WaitFlag<HardEvent::S_MTE3>(eventIdSToMte3);
189- 
190- DataCopyExtParams valueCopyParam{1, static_cast<uint32_t>(rowCount * sizeof(T)), 0, 0, 0};
191- DataCopyPad(valuesGm_[rowStart], compactValue, valueCopyParam);
192- DataCopyExtParams indexCopyParam{1, static_cast<uint32_t>(rowCount * sizeof(int64_t)), 0, 0, 0};
193- DataCopyPad(indicesGm_[rowStart], compactIndex, indexCopyParam);
194-}
195- 
196-template <typename T, bool EnableMedian>
197-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::ProcessRows(int64_t rowStart, uint32_t rowCount)
198-{
199- LocalTensor<T> compactValue = compactValueQueue_.AllocTensor<T>();
200- LocalTensor<int64_t> compactIndex = compactIndexQueue_.AllocTensor<int64_t>();
201- for (uint32_t i = 0; i < rowCount; ++i) {
202- ProcessOneRow(rowStart + i, i, compactValue, compactIndex);
203- }
204- compactValueQueue_.EnQue<T>(compactValue);
205- compactIndexQueue_.EnQue<int64_t>(compactIndex);
206- compactValue = compactValueQueue_.DeQue<T>();
207- compactIndex = compactIndexQueue_.DeQue<int64_t>();
208- CopyOutputToGm(rowStart, rowCount, compactValue, compactIndex);
209- compactValueQueue_.FreeTensor(compactValue);
210- compactIndexQueue_.FreeTensor(compactIndex);
211-}
212- 
213-template <typename T, bool EnableMedian>
214-__aicore__ inline void KthValueRadixOneCore<T, EnableMedian>::Process()
215-{
216- if (blockIdx_ >= blockNum_ || blockIdx_ >= unsortedDimParallel_) {
217- return;
218- }
219- int64_t rowsPerCore = unsortedDimNum_ / static_cast<int64_t>(unsortedDimParallel_);
220- uint32_t tailRows = static_cast<uint32_t>(unsortedDimNum_ % static_cast<int64_t>(unsortedDimParallel_));
221- int64_t blockRows = rowsPerCore + (blockIdx_ < tailRows ? 1 : 0);
222- uint32_t priorTailRows = blockIdx_ < tailRows ? blockIdx_ : tailRows;
223- int64_t blockOffset = static_cast<int64_t>(blockIdx_) * rowsPerCore + static_cast<int64_t>(priorTailRows);
224- 
225- uint64_t blockRows64 = static_cast<uint64_t>(blockRows);
226- uint64_t outputRowsPerLoop64 = static_cast<uint64_t>(outputRowsPerLoop_);
227- uint64_t loopTimes = Ops::Base::CeilDiv(blockRows64, outputRowsPerLoop64);
228- for (uint64_t loopIdx = 0; loopIdx < loopTimes; ++loopIdx) {
229- uint64_t localOffset = loopIdx * outputRowsPerLoop64;
230- uint64_t remainRows = blockRows64 - localOffset;
231- uint32_t rowCount = remainRows < outputRowsPerLoop64 ? static_cast<uint32_t>(remainRows) : outputRowsPerLoop_;
232- ProcessRows(blockOffset + static_cast<int64_t>(localOffset), rowCount);
233- }
234-}
235-} // namespace KthValue
236- 
237-#endif