* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file data_compare_empty.h
* \brief DataCompare 空 tensor 模板 kernel 类
*
* EMPTY_A:usedCoreNum=0,所有核早退,输出空 tensor
* EMPTY_R:usedCoreNum>0,输出 0.0f(Reducer::empty_r_output_value)
* 不带 isTailR 模板参数(All Reduce 简化)
*/
#ifndef OPS_DATA_COMPARE_EMPTY_H_
#define OPS_DATA_COMPARE_EMPTY_H_
#include "kernel_operator.h"
#include "kernel_tiling/kernel_tiling.h"
#include "data_compare_tiling_data.h"
#include "data_compare_tiling_key.h"
namespace NsDataCompare {
using namespace AscendC;
constexpr uint32_t kEmptyVlBytes = 256;
constexpr uint32_t kEmptyRepF32 = kEmptyVlBytes / sizeof(float);
template <typename DType>
class DataCompareEmpty {
public:
using D_T = DType;
__aicore__ inline DataCompareEmpty() {}
__aicore__ inline void Init(GM_ADDR x1, GM_ADDR x2, GM_ADDR y, const DataCompareTilingData* td);
__aicore__ inline void Process();
private:
int32_t usedCoreNum_ = 0;
int64_t aTotal_ = 0;
int64_t aUbFactor_ = 0;
int32_t aBigCoreCnt_ = 0;
int64_t aBigCoreLoopCnt_ = 0;
int64_t aSmallCoreLoopCnt_ = 0;
GlobalTensor<D_T> x1Gm_;
GlobalTensor<D_T> x2Gm_;
GlobalTensor<float> yGm_;
TPipe pipe_;
TQue<QuePosition::VECOUT, 1> outQue_;
};
template <typename DType>
__aicore__ inline void DataCompareEmpty<DType>::Init(GM_ADDR x1, GM_ADDR x2, GM_ADDR y, const DataCompareTilingData* td)
{
x1Gm_.SetGlobalBuffer(reinterpret_cast<__gm__ D_T*>(x1));
x2Gm_.SetGlobalBuffer(reinterpret_cast<__gm__ D_T*>(x2));
yGm_.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(y));
usedCoreNum_ = td->usedCoreNum;
aTotal_ = td->axisShape[0];
aUbFactor_ = td->aUbFactor;
aBigCoreCnt_ = td->aBigCoreCnt;
aBigCoreLoopCnt_ = td->aBigCoreLoopCnt;
aSmallCoreLoopCnt_ = td->aSmallCoreLoopCnt;
int64_t outBufSize = td->postReduceUbSize;
if (outBufSize <= 0) {
outBufSize = 256;
}
pipe_.InitBuffer(outQue_, 1, outBufSize);
}
template <typename DType>
__aicore__ inline void DataCompareEmpty<DType>::Process()
{
int64_t blockIdx = GetBlockIdx();
if (blockIdx >= usedCoreNum_)
return;
int64_t aStart, aEnd;
if (blockIdx < aBigCoreCnt_) {
aStart = blockIdx * aBigCoreLoopCnt_ * aUbFactor_;
aEnd = aStart + aBigCoreLoopCnt_ * aUbFactor_;
} else {
aStart =
aBigCoreCnt_ * aBigCoreLoopCnt_ * aUbFactor_ + (blockIdx - aBigCoreCnt_) * aSmallCoreLoopCnt_ * aUbFactor_;
aEnd = aStart + aSmallCoreLoopCnt_ * aUbFactor_;
}
if (aEnd > aTotal_)
aEnd = aTotal_;
for (int64_t aOff = aStart; aOff < aEnd; aOff += aUbFactor_) {
int64_t aLen = aUbFactor_;
if (aOff + aLen > aTotal_)
aLen = aTotal_ - aOff;
auto outLocal = outQue_.AllocTensor<float>();
__ubuf__ float* outBase = reinterpret_cast<__ubuf__ float*>(outLocal.GetPhyAddr());
uint32_t count = static_cast<uint32_t>(aLen);
uint16_t repU16 = static_cast<uint16_t>((count + kEmptyRepF32 - 1) / kEmptyRepF32);
__VEC_SCOPE__
{
AscendC::Reg::RegTensor<float> idReg;
AscendC::Reg::Duplicate(idReg, 0.0f);
AscendC::Reg::MaskReg mask;
uint32_t remaining = count;
for (uint16_t i = 0; i < repU16; ++i) {
int32_t off = static_cast<int32_t>(i) * static_cast<int32_t>(kEmptyRepF32);
mask = AscendC::Reg::UpdateMask<float>(remaining);
AscendC::Reg::StoreAlign(outBase + off, idReg, mask);
}
}
outQue_.EnQue(outLocal);
auto outDeq = outQue_.DeQue<float>();
DataCopyExtParams cpExt;
cpExt.blockLen = static_cast<uint32_t>(aLen * sizeof(float));
cpExt.blockCount = 1;
cpExt.srcStride = 0;
cpExt.dstStride = 0;
cpExt.rsv = 0;
DataCopyPad(yGm_[aOff], outDeq, cpExt);
outQue_.FreeTensor(outDeq);
}
}
}
#endif