已合并
IFA算子新增sparsemode类型9,支持树形mask输入 #2045
IFA算子新增sparsemode类型9,支持树形mask输入 #2045
已合并
jiazhixi创建于 2月26日
27 个文件变更+722-73
@@ -137,8 +137,9 @@ bool FiaTilingNonQuantMla::IsCapable()
137 }137 }
138 138 
139 // 支持的sparse_mode值139 // 支持的sparse_mode值
140- if ((fiaInfo_->sparseMode != SPARSE_MODE_NO_MASK) && (fiaInfo_->sparseMode != SPARSE_MODE_RIGHT_DOWN) &&140+ 
141- (fiaInfo_->sparseMode != SPARSE_MODE_BAND)) {141+ if ((fiaInfo_->sparseMode != SPARSE_MODE_NO_MASK) && (fiaInfo_->sparseMode != SPARSE_MODE_RIGHT_DOWN) &&
142+ (fiaInfo_->sparseMode != SPARSE_MODE_BAND) && (fiaInfo_->sparseMode != SPARSE_MODE_TREE)) {
142 return false;143 return false;
143 }144 }
144 145 
Mattention/common/op_host/fia_tiling_info.h+2-0文件内容审核中,请稍后刷新重试
@@ -39,6 +39,7 @@ static const std::map<FiaLayout, std::vector<FiaAxis>> FIA_LAYOUT_AXIS_MAP = {
39 {FiaLayout::B1S1S2, {FiaAxis::B, FiaAxis::CONST, FiaAxis::S1, FiaAxis::S2}},39 {FiaLayout::B1S1S2, {FiaAxis::B, FiaAxis::CONST, FiaAxis::S1, FiaAxis::S2}},
40 {FiaLayout::IS1S2, {FiaAxis::CONST, FiaAxis::S1, FiaAxis::S2}},40 {FiaLayout::IS1S2, {FiaAxis::CONST, FiaAxis::S1, FiaAxis::S2}},
41 {FiaLayout::I1S1S2, {FiaAxis::CONST, FiaAxis::CONST, FiaAxis::S1, FiaAxis::S2}},41 {FiaLayout::I1S1S2, {FiaAxis::CONST, FiaAxis::CONST, FiaAxis::S1, FiaAxis::S2}},
42+ {FiaLayout::S1S1, {FiaAxis::S1}},
42};43};
43 44 
44static bool equal_to(const int64_t& a, const int64_t& b)45static bool equal_to(const int64_t& a, const int64_t& b)
@@ -243,6 +244,8 @@ ge::graphStatus FiaTilingShapeCompare::GetExpectedShapeSpecial(gert::Shape &shap
243 shapeExpected = gert::Shape({param.CONST, param.S1, param.S2});244 shapeExpected = gert::Shape({param.CONST, param.S1, param.S2});
244 } else if (layout_ == FiaLayout::I1S1S2) {245 } else if (layout_ == FiaLayout::I1S1S2) {
245 shapeExpected = gert::Shape({param.CONST, param.CONST, param.S1, param.S2});246 shapeExpected = gert::Shape({param.CONST, param.CONST, param.S1, param.S2});
247+ } else if (layout_ == FiaLayout::S1S1) {
248+ shapeExpected = gert::Shape({param.S1});
246 } else {249 } else {
247 OP_LOGE(opName_, "[%s] layout %s is unsupported", funcName.c_str(), LayoutToSerialString(layout_).c_str());250 OP_LOGE(opName_, "[%s] layout %s is unsupported", funcName.c_str(), LayoutToSerialString(layout_).c_str());
248 return ge::GRAPH_FAILED;251 return ge::GRAPH_FAILED;
Mattention/common/op_host/split_core.cpp+2-0文件内容审核中,请稍后刷新重试
Mattention/common/op_host/split_core.h+1-0文件内容审核中,请稍后刷新重试
@@ -579,7 +579,6 @@ __aicore__ inline void FiaBlockVecNonQuant<FIAT>::ElewiseCompute(
579 maskInfo.maskValue = negativeIntScalar;579 maskInfo.maskValue = negativeIntScalar;
580 maskInfo.s1LeftPaddingSize = info.qPaddingBeginOffset;580 maskInfo.s1LeftPaddingSize = info.qPaddingBeginOffset;
581 maskInfo.s2LeftPaddingSize = info.kvPaddingBeginOffset;581 maskInfo.s2LeftPaddingSize = info.kvPaddingBeginOffset;
582- 
583 if (constInfo.qSeqSize == 1) {582 if (constInfo.qSeqSize == 1) {
584 maskInfo.layout = fa_base_vector::S1_EQUAL1;583 maskInfo.layout = fa_base_vector::S1_EQUAL1;
585 } else if constexpr (LAYOUT_T == FIA_LAYOUT::TND || LAYOUT_T == FIA_LAYOUT::BSH) {584 } else if constexpr (LAYOUT_T == FIA_LAYOUT::TND || LAYOUT_T == FIA_LAYOUT::BSH) {
@@ -587,25 +586,49 @@ __aicore__ inline void FiaBlockVecNonQuant<FIAT>::ElewiseCompute(
587 } else {586 } else {
588 maskInfo.layout = fa_base_vector::GS;587 maskInfo.layout = fa_base_vector::GS;
589 }588 }
589+ 
590 maskInfo.attenMaskType = fa_base_vector::MASK_BOOL; // compatible with int8/uint8590 maskInfo.attenMaskType = fa_base_vector::MASK_BOOL; // compatible with int8/uint8
591- LocalTensor<bool> maskUb;591+ 
592- LocalTensor<bool> attenMaskTmpUb;592+ // 添加Sparse9的处理,由于sparse9的mask拷贝只占最小块的一部分,所以需要对UB空间赋初值0,表示不被掩码覆盖
593+ // TND场景下mask传入∑s1²,其余场景传入[B,S1,S1]
594+ LocalTensor<bool> maskUb = inputQue2.AllocTensor<bool>();
595+ LocalTensor<bool> attenMaskTmpUb = maskUb[BUFFER_SIZE_BYTE_16K / 2];
593 LocalTensor<uint8_t> ubWorkSpace = tmpBuf.Get<uint8_t>();596 LocalTensor<uint8_t> ubWorkSpace = tmpBuf.Get<uint8_t>();
597+ event_t eventIdVMte2 = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::V_MTE2));
598+ if (maskInfo.sparseMode == fa_base_vector::TREE) {
599+ LocalTensor<int16_t> mask16 = maskUb.template ReinterpretCast<int16_t>();
600+ uint32_t zeroCount = BUFFER_SIZE_BYTE_8K / sizeof(int16_t);
601+ Duplicate(mask16, static_cast<int16_t>(0), zeroCount);
602+ maskUb = mask16.template ReinterpretCast<bool>();
603+ SetFlag<HardEvent::V_MTE2>(eventIdVMte2);
604+ WaitFlag<HardEvent::V_MTE2>(eventIdVMte2);
605+ // 修改attenMaskStride、attenMaskBatchStride值
606+ maskInfo.attenMaskBatchStride = maskInfo.attenMaskBatchStride * maskInfo.batchIdx;
607+ if (LAYOUT_T == FIA_LAYOUT::TND || LAYOUT_T == FIA_LAYOUT::NTD) {
608+ maskInfo.attenMaskStride = info.actS1Size;
609+ maskInfo.attenMaskBatchStride = 0;
610+ for (int32_t i = 0; i < maskInfo.batchIdx; i++) {
611+ maskInfo.attenMaskBatchStride += qActSeqLensParser.GetActualSeqLength(i) * qActSeqLensParser.GetActualSeqLength(i);
612+ }
613+ }
614+ }
615+ 
594 if (!fa_base_vector::IsSkipAttentionmask(maskInfo)) {616 if (!fa_base_vector::IsSkipAttentionmask(maskInfo)) {
595- maskUb = inputQue2.AllocTensor<bool>();617+ if (maskInfo.sparseMode == fa_base_vector::TREE) {
596- attenMaskTmpUb = maskUb[BUFFER_SIZE_BYTE_16K / 2];618+ fa_base_vector::AttentionmaskCopyIn<bool, bool, true>(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo);
597- fa_base_vector::AttentionmaskCopyIn(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo);619+ } else {
620+ fa_base_vector::AttentionmaskCopyIn(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo);
621+ }
598 AscendC::PipeBarrier<PIPE_V>();622 AscendC::PipeBarrier<PIPE_V>();
599 fa_base_vector::AttentionMaskCompute<MM1_OUT_T>(mmResUb, mmResUb, maskUb, ubWorkSpace, maskInfo);623 fa_base_vector::AttentionMaskCompute<MM1_OUT_T>(mmResUb, mmResUb, maskUb, ubWorkSpace, maskInfo);
600- inputQue2.FreeTensor(maskUb);
601 }624 }
602 if (!fa_base_vector::IsSkipAttentionmaskForPre(maskInfo)) {625 if (!fa_base_vector::IsSkipAttentionmaskForPre(maskInfo)) {
603- maskUb = inputQue2.AllocTensor<bool>();626+ SetFlag<HardEvent::V_MTE2>(eventIdVMte2);
604- attenMaskTmpUb = maskUb[BUFFER_SIZE_BYTE_16K / 2]; 627+ WaitFlag<HardEvent::V_MTE2>(eventIdVMte2);
605 fa_base_vector::AttentionmaskCopyIn(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo, true);628 fa_base_vector::AttentionmaskCopyIn(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo, true);
606 fa_base_vector::AttentionMaskCompute<MM1_OUT_T>(mmResUb, mmResUb, maskUb, ubWorkSpace, maskInfo, true);629 fa_base_vector::AttentionMaskCompute<MM1_OUT_T>(mmResUb, mmResUb, maskUb, ubWorkSpace, maskInfo, true);
607- inputQue2.FreeTensor(maskUb);
608 }630 }
631+ inputQue2.FreeTensor(maskUb);
609 }632 }
610}633}
611 634 
@@ -454,13 +454,36 @@ __aicore__ inline void FiaBlockVecNonQuantMla<FIAT>::ElewiseCompute(
454 }454 }
455 455 
456 maskInfo.attenMaskType = fa_base_vector::MASK_BOOL; // compatible with int8/uint8456 maskInfo.attenMaskType = fa_base_vector::MASK_BOOL; // compatible with int8/uint8
457+ 
458+ // 添加Sparse9的处理,由于sparse9的mask拷贝只占最小块的一部分,所以需要对UB空间赋初值0,表示不被掩码覆盖
459+ // TND场景下mask传入∑s1²,其余场景传入[B,S1,S1]
457 LocalTensor<bool> maskUb = inputBuff2.Get<bool>();460 LocalTensor<bool> maskUb = inputBuff2.Get<bool>();
458 maskUb = maskUb[pingpongFlag * INPUT2_BUFFER_OFFSET / sizeof(bool)];461 maskUb = maskUb[pingpongFlag * INPUT2_BUFFER_OFFSET / sizeof(bool)];
462+ if (maskInfo.sparseMode == fa_base_vector::TREE) {
463+ LocalTensor<int16_t> mask16 = maskUb.template ReinterpretCast<int16_t>();
464+ AscendC::Duplicate(mask16, static_cast<int16_t>(0), INPUT2_BUFFER_OFFSET / sizeof(int16_t));
465+ maskUb = mask16.template ReinterpretCast<bool>();
466+ WaitFlag<AscendC::HardEvent::V_MTE2>(SYNC_INPUT_BUF2_FLAG + pingpongFlag);
467+ SetFlag<AscendC::HardEvent::V_MTE2>(SYNC_INPUT_BUF2_FLAG + pingpongFlag);
468+ // 修改attenMaskStride、attenMaskBatchStride值
469+ maskInfo.attenMaskBatchStride = maskInfo.attenMaskBatchStride * maskInfo.batchIdx;
470+ if (LAYOUT_T == FIA_LAYOUT::TND || LAYOUT_T == FIA_LAYOUT::NTD) {
471+ maskInfo.attenMaskStride = info.actS1Size;
472+ maskInfo.attenMaskBatchStride = 0;
473+ for (int32_t i = 0; i < maskInfo.batchIdx; i++) {
474+ maskInfo.attenMaskBatchStride += qActSeqLensParser.GetActualSeqLength(i) * qActSeqLensParser.GetActualSeqLength(i);
475+ }
476+ }
477+ }
459 LocalTensor<bool> attenMaskTmpUb = attenMaskTmpBuff.Get<bool>();478 LocalTensor<bool> attenMaskTmpUb = attenMaskTmpBuff.Get<bool>();
460 LocalTensor<uint8_t> ubWorkSpace = tmpBuf.Get<uint8_t>();479 LocalTensor<uint8_t> ubWorkSpace = tmpBuf.Get<uint8_t>();
461 if (!fa_base_vector::IsSkipAttentionmask(maskInfo)) {480 if (!fa_base_vector::IsSkipAttentionmask(maskInfo)) {
462 WaitFlag<AscendC::HardEvent::V_MTE2>(SYNC_INPUT_BUF2_FLAG + pingpongFlag);481 WaitFlag<AscendC::HardEvent::V_MTE2>(SYNC_INPUT_BUF2_FLAG + pingpongFlag);
463- fa_base_vector::AttentionmaskCopyIn(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo);482+ if (maskInfo.sparseMode == fa_base_vector::TREE) {
483+ fa_base_vector::AttentionmaskCopyIn<bool, bool, true>(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo);
484+ } else {
485+ fa_base_vector::AttentionmaskCopyIn(maskUb, attenMaskBoolGm, attenMaskTmpUb, maskInfo);
486+ }
464 AscendC::PipeBarrier<PIPE_V>();487 AscendC::PipeBarrier<PIPE_V>();
465 fa_base_vector::AttentionMaskCompute<MM1_OUT_T>(mmResUb, mmResUb, maskUb, ubWorkSpace, maskInfo);488 fa_base_vector::AttentionMaskCompute<MM1_OUT_T>(mmResUb, mmResUb, maskUb, ubWorkSpace, maskInfo);
466 SetFlag<AscendC::HardEvent::V_MTE2>(SYNC_INPUT_BUF2_FLAG + pingpongFlag);489 SetFlag<AscendC::HardEvent::V_MTE2>(SYNC_INPUT_BUF2_FLAG + pingpongFlag);
Mattention/common/op_kernel/arch32/fia_kernel_nonquant.h+1-1文件内容审核中,请稍后刷新重试
Mattention/common/op_kernel/arch32/fia_kernel_nonquant_mla.h+1-1文件内容审核中,请稍后刷新重试
@@ -47,6 +47,7 @@ enum SparseMode : uint8_t {
47 LEFT_UP_CAUSAL,47 LEFT_UP_CAUSAL,
48 RIGHT_DOWN_CAUSAL,48 RIGHT_DOWN_CAUSAL,
49 BAND,49 BAND,
50+ TREE = 9,
50};51};
51 52
52__aicore__ inline bool IsExistInvalidRows(int64_t nextTokensPerBatch, int64_t preTokensPerBatch, uint32_t mode,53__aicore__ inline bool IsExistInvalidRows(int64_t nextTokensPerBatch, int64_t preTokensPerBatch, uint32_t mode,
@@ -853,6 +854,14 @@ __aicore__ inline uint64_t ComputeAttenMaskOffsetNoCompress(MaskInfo &info, uint
853 return bOffset + s1Offset + s2Offset;854 return bOffset + s1Offset + s2Offset;
854}855}
855 856 
857+__aicore__ inline uint64_t ComputeAttenMaskOffsetTree(MaskInfo &info, uint32_t s1StartIdx, uint64_t treeMaskStart)
858+{
859+ uint64_t bOffset = info.attenMaskBatchStride;
860+ uint64_t s1Offset = (s1StartIdx % info.s1Size) * info.attenMaskStride;
861+ uint64_t s2Offset = info.s2StartIdx > treeMaskStart ? info.s2StartIdx - treeMaskStart : 0;
862+ return bOffset + s1Offset + s2Offset;
863+}
864+ 
856__aicore__ inline uint64_t ComputeAttenMaskOffsetCompress(MaskInfo &info, uint32_t s1StartIdx)865__aicore__ inline uint64_t ComputeAttenMaskOffsetCompress(MaskInfo &info, uint32_t s1StartIdx)
857{866{
858 int64_t nextToken = 0; // sparse2 本身原点就是左上角867 int64_t nextToken = 0; // sparse2 本身原点就是左上角
@@ -886,24 +895,58 @@ __aicore__ inline uint64_t ComputeAttenMaskOffsetCompressPre(MaskInfo &info, uin
886 return offset;895 return offset;
887}896}
888 897 
889-__aicore__ inline uint64_t ComputeAttenMaskOffset(MaskInfo &info, uint32_t s1StartIdx = 0, bool isPre = false)898+template <bool ENABLE_TREE = false>
899+__aicore__ inline uint64_t ComputeAttenMaskOffset(MaskInfo &info, uint32_t s1StartIdx = 0, uint64_t treeMaskStart = 0, bool isPre = false)
890{900{
891 if (isPre) {901 if (isPre) {
892 return ComputeAttenMaskOffsetCompressPre(info, s1StartIdx);902 return ComputeAttenMaskOffsetCompressPre(info, s1StartIdx);
893 } else {903 } else {
894 if (info.sparseMode == DEFAULT_MASK || info.sparseMode == ALL_MASK) {904 if (info.sparseMode == DEFAULT_MASK || info.sparseMode == ALL_MASK) {
895 return ComputeAttenMaskOffsetNoCompress(info, s1StartIdx);905 return ComputeAttenMaskOffsetNoCompress(info, s1StartIdx);
896- } else {
897- return ComputeAttenMaskOffsetCompress(info, s1StartIdx);
898 }906 }
907+ if constexpr (ENABLE_TREE) {
908+ if (info.sparseMode == TREE) {
909+ return ComputeAttenMaskOffsetTree(info, s1StartIdx, treeMaskStart);
910+ }
911+ }
912+ return ComputeAttenMaskOffsetCompress(info, s1StartIdx);
899 }913 }
900}914}
901 915 
902-template <typename T>916+template <typename T, bool ENABLE_TREE = false>
903__aicore__ inline void AttentionmaskDataCopy(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, MaskInfo &info, uint32_t s1StartIdx, uint32_t s1EndIdx, bool isPre = false)917__aicore__ inline void AttentionmaskDataCopy(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, MaskInfo &info, uint32_t s1StartIdx, uint32_t s1EndIdx, bool isPre = false)
904{918{
919+ uint32_t treeMaskStart = 0;
920+ if constexpr (ENABLE_TREE) {
921+ treeMaskStart = info.s2Size - info.s1Size;
922+ uint32_t curS2EnsPos = info.s2StartIdx + info.s2dealNum;
923+ // 只有info.s2StartIdx + info.s2dealNum > treeMaskStart时,才会进入此流程;
924+ // 当info.s2StartIdx > treeMaskStart,mask拷贝也是全量拷贝,和其余sparse过程相同
925+ if (info.s2StartIdx < treeMaskStart) {
926+ if (curS2EnsPos <= treeMaskStart) {
927+ return; // 整个 tile 在零区,UB 已初始化为 0,无需拷贝
928+ }
929+ // 由于sparse9 mask只有一部分,不能合并处理
930+ uint32_t attenMaskSize = curS2EnsPos - treeMaskStart;
931+ uint32_t attenMaskSizeAlign = Align(static_cast<uint32_t>(attenMaskSize + treeMaskStart % 32), 32U);
932+ uint64_t maskOffset = ComputeAttenMaskOffset<ENABLE_TREE>(info, s1StartIdx, treeMaskStart, isPre);
933+ DataCopyExtParams dataCopyParams;
934+ dataCopyParams.blockCount = s1EndIdx - s1StartIdx;
935+ dataCopyParams.blockLen = curS2EnsPos - treeMaskStart;
936+ dataCopyParams.srcStride = info.attenMaskStride - (curS2EnsPos - treeMaskStart);
937+ dataCopyParams.dstStride = (treeMaskStart - info.s2StartIdx) / 32; // dst在UB上,单位为32字节,同时左侧不对齐场景会进行左padding
938+ DataCopyPadExtParams<bool> padParams;
939+ padParams.isPad = true;
940+ padParams.leftPadding = static_cast<uint8_t>(treeMaskStart % 32);
941+ padParams.rightPadding = static_cast<uint8_t>(attenMaskSizeAlign - (attenMaskSize + treeMaskStart % 32));
942+ padParams.paddingValue = 0;
943+ DataCopyPad(attenMaskUb[(treeMaskStart - info.s2StartIdx) / 32 * 32], srcGmAddr[maskOffset], dataCopyParams, padParams);
944+ return;
945+ }
946+ }
947+ // 标准拷贝路径(非TREE模式,或TREE模式但s2StartIdx >= treeMaskStart时)
905 uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);948 uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);
906- uint64_t maskOffset = ComputeAttenMaskOffset(info, s1StartIdx, isPre);949+ uint64_t maskOffset = ComputeAttenMaskOffset<ENABLE_TREE>(info, s1StartIdx, treeMaskStart, isPre);
907 DataCopyExtParams dataCopyParams;950 DataCopyExtParams dataCopyParams;
908 dataCopyParams.blockCount = s1EndIdx - s1StartIdx;951 dataCopyParams.blockCount = s1EndIdx - s1StartIdx;
909 dataCopyParams.blockLen = info.s2dealNum;952 dataCopyParams.blockLen = info.s2dealNum;
@@ -914,7 +957,7 @@ __aicore__ inline void AttentionmaskDataCopy(LocalTensor<T> &attenMaskUb, Global
914 DataCopyPad(attenMaskUb, srcGmAddr[maskOffset], dataCopyParams, padParams);957 DataCopyPad(attenMaskUb, srcGmAddr[maskOffset], dataCopyParams, padParams);
915}958}
916 959 
917-template <typename T, typename U>960+template <typename T, typename U, bool ENABLE_TREE = false>
918__aicore__ inline void AttentionmaskCopyInForGsLayout(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, LocalTensor<U> &tmpBuf, MaskInfo &info, bool isPre = false)961__aicore__ inline void AttentionmaskCopyInForGsLayout(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, LocalTensor<U> &tmpBuf, MaskInfo &info, bool isPre = false)
919{962{
920 int32_t s1StartIdx = info.gs1StartIdx % info.s1Size;963 int32_t s1StartIdx = info.gs1StartIdx % info.s1Size;
@@ -922,17 +965,17 @@ __aicore__ inline void AttentionmaskCopyInForGsLayout(LocalTensor<T> &attenMaskU
922 uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);965 uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);
923 if (info.gs1dealNum <= info.s1Size) {966 if (info.gs1dealNum <= info.s1Size) {
924 if (s1StartIdx + info.gs1dealNum > info.s1Size) {967 if (s1StartIdx + info.gs1dealNum > info.s1Size) {
925- AttentionmaskDataCopy(attenMaskUb, srcGmAddr, info, s1StartIdx, info.s1Size, isPre);968+ AttentionmaskDataCopy<T, ENABLE_TREE>(attenMaskUb, srcGmAddr, info, s1StartIdx, info.s1Size, isPre);
926 LocalTensor<T> attenMaskSecUb = attenMaskUb[(info.s1Size - s1StartIdx) * attenMaskSizeAlign];969 LocalTensor<T> attenMaskSecUb = attenMaskUb[(info.s1Size - s1StartIdx) * attenMaskSizeAlign];
927- AttentionmaskDataCopy(attenMaskSecUb, srcGmAddr, info, 0, s1EndIdx, isPre);970+ AttentionmaskDataCopy<T, ENABLE_TREE>(attenMaskSecUb, srcGmAddr, info, 0, s1EndIdx, isPre);
928 } else {971 } else {
929- AttentionmaskDataCopy(attenMaskUb, srcGmAddr, info, s1StartIdx, s1EndIdx, isPre);972+ AttentionmaskDataCopy<T, ENABLE_TREE>(attenMaskUb, srcGmAddr, info, s1StartIdx, s1EndIdx, isPre);
930 }973 }
931 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));974 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));
932 SetFlag<HardEvent::MTE2_V>(enQueEvtID);975 SetFlag<HardEvent::MTE2_V>(enQueEvtID);
933 WaitFlag<HardEvent::MTE2_V>(enQueEvtID);976 WaitFlag<HardEvent::MTE2_V>(enQueEvtID);
934 } else {977 } else {
935- AttentionmaskDataCopy(attenMaskUb, srcGmAddr, info, 0, info.s1Size, isPre);978+ AttentionmaskDataCopy<T, ENABLE_TREE>(attenMaskUb, srcGmAddr, info, 0, info.s1Size, isPre);
936 LocalTensor<T> attenMaskUbDst = tmpBuf.template ReinterpretCast<T>();979 LocalTensor<T> attenMaskUbDst = tmpBuf.template ReinterpretCast<T>();
937 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));980 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));
938 SetFlag<HardEvent::MTE2_V>(enQueEvtID);981 SetFlag<HardEvent::MTE2_V>(enQueEvtID);
@@ -964,7 +1007,7 @@ __aicore__ inline void AttentionmaskCopyInForGsLayout(LocalTensor<T> &attenMaskU
964 }1007 }
965}1008}
966 1009 
967-template <typename T, typename U>1010+template <typename T, typename U, bool ENABLE_TREE = false>
968__aicore__ inline void AttentionmaskCopyInForSgLayout(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, LocalTensor<U> &tmpBuf, MaskInfo &info, bool isPre = false)1011__aicore__ inline void AttentionmaskCopyInForSgLayout(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, LocalTensor<U> &tmpBuf, MaskInfo &info, bool isPre = false)
969{1012{
970 uint32_t s1StartIdx = info.gs1StartIdx / info.gSize;1013 uint32_t s1StartIdx = info.gs1StartIdx / info.gSize;
@@ -972,7 +1015,7 @@ __aicore__ inline void AttentionmaskCopyInForSgLayout(LocalTensor<T> &attenMaskU
972 uint32_t s1Count = s1EndIdx - s1StartIdx + 1;1015 uint32_t s1Count = s1EndIdx - s1StartIdx + 1;
973 uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);1016 uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);
974 1017 
975- AttentionmaskDataCopy(attenMaskUb, srcGmAddr, info, s1StartIdx, s1EndIdx + 1, isPre);1018+ AttentionmaskDataCopy<T, ENABLE_TREE>(attenMaskUb, srcGmAddr, info, s1StartIdx, s1EndIdx + 1, isPre);
976 1019 
977 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));1020 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));
978 SetFlag<HardEvent::MTE2_V>(enQueEvtID);1021 SetFlag<HardEvent::MTE2_V>(enQueEvtID);
@@ -1012,12 +1055,25 @@ __aicore__ inline void AttentionmaskCopyInForSgLayout(LocalTensor<T> &attenMaskU
1012 attenMaskUb = attenMaskUbDst.template ReinterpretCast<bool>();1055 attenMaskUb = attenMaskUbDst.template ReinterpretCast<bool>();
1013}1056}
1014 1057 
1058+template <bool ENABLE_TREE = false>
1015__aicore__ inline bool IsSkipAttentionmask(MaskInfo &info)1059__aicore__ inline bool IsSkipAttentionmask(MaskInfo &info)
1016{1060{
1017 if (info.sparseMode == DEFAULT_MASK || info.sparseMode == ALL_MASK) {1061 if (info.sparseMode == DEFAULT_MASK || info.sparseMode == ALL_MASK) {
1018 return false;1062 return false;
1019 }1063 }
1020 1064 
1065+ // 增加sparse = 9的处理
1066+ if constexpr (ENABLE_TREE) {
1067+ if (info.sparseMode == TREE) {
1068+ // 由于分核时按照Batch进行划分,sparse9在每个batch的所有 S 跳过的范围固定,所以不区分跨g轴的情况
1069+ if (static_cast<int64_t>(info.s2StartIdx + info.s2dealNum) > static_cast<int64_t>(info.s2Size - info.s1Size)) {
1070+ return false;
1071+ } else {
1072+ return true;
1073+ }
1074+ }
1075+ }
1076+ 
1021 int32_t s1StartIdx = info.layout == GS ? info.gs1StartIdx % info.s1Size : info.gs1StartIdx / info.gSize;1077 int32_t s1StartIdx = info.layout == GS ? info.gs1StartIdx % info.s1Size : info.gs1StartIdx / info.gSize;
1022 if (info.layout == GS && s1StartIdx + info.gs1dealNum > info.s1Size) { // 当跨多个s1时,不再支持跳过计算1078 if (info.layout == GS && s1StartIdx + info.gs1dealNum > info.s1Size) { // 当跨多个s1时,不再支持跳过计算
1023 return false;1079 return false;
@@ -1056,30 +1112,69 @@ __aicore__ inline bool IsSkipAttentionmaskForPre(MaskInfo &info)
1056 return false;1112 return false;
1057}1113}
1058 1114 
1059-template <typename T, typename U>1115+template <typename T, typename U, bool ENABLE_TREE = false>
1060__aicore__ inline void AttentionmaskCopyIn(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, LocalTensor<U> &tmpBuf, MaskInfo &info, bool isPre = false)1116__aicore__ inline void AttentionmaskCopyIn(LocalTensor<T> &attenMaskUb, GlobalTensor<T> &srcGmAddr, LocalTensor<U> &tmpBuf, MaskInfo &info, bool isPre = false)
1061{1117{
1062 if (info.layout == GS) {1118 if (info.layout == GS) {
1063- AttentionmaskCopyInForGsLayout(attenMaskUb, srcGmAddr, tmpBuf, info, isPre);1119+ AttentionmaskCopyInForGsLayout<T, U, ENABLE_TREE>(attenMaskUb, srcGmAddr, tmpBuf, info, isPre);
1064 } else if(info.layout == SG) { // sg1120 } else if(info.layout == SG) { // sg
1065- AttentionmaskCopyInForSgLayout(attenMaskUb, srcGmAddr, tmpBuf, info, isPre);1121+ AttentionmaskCopyInForSgLayout<T, U, ENABLE_TREE>(attenMaskUb, srcGmAddr, tmpBuf, info, isPre);
1066 } else if (info.layout == S1_EQUAL1) {1122 } else if (info.layout == S1_EQUAL1) {
1067- uint64_t maskOffset = ComputeAttenMaskOffset(info, 0, isPre);1123+ uint32_t treeMaskStart = 0;
1068- uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);1124+ if constexpr (ENABLE_TREE) {
1069- DataCopyExtParams dataCopyParams;1125+ treeMaskStart = info.s2Size - info.s1Size;
1070- dataCopyParams.blockCount = 1;1126+ uint32_t curS2EndPos = info.s2StartIdx + info.s2dealNum;
1071- dataCopyParams.blockLen = info.s2dealNum;1127+ if (info.s2StartIdx >= treeMaskStart) {
1072- dataCopyParams.srcStride = info.attenMaskStride - info.s2dealNum ;1128+ // TREE s2StartIdx >= treeMaskStart:全量拷贝
1073- dataCopyParams.dstStride = 0;1129+ uint64_t maskOffset = ComputeAttenMaskOffset<ENABLE_TREE>(info, 0, treeMaskStart, isPre);
1074- DataCopyPadExtParams<bool> padParams{true, 0, static_cast<uint8_t>(attenMaskSizeAlign - info.s2dealNum), 0};1130+ uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);
1075- DataCopyPad(attenMaskUb, srcGmAddr[maskOffset], dataCopyParams, padParams);1131+ DataCopyExtParams dataCopyParams;
1132+ dataCopyParams.blockCount = 1;
1133+ dataCopyParams.blockLen = info.s2dealNum;
1134+ dataCopyParams.srcStride = info.attenMaskStride - info.s2dealNum;
1135+ dataCopyParams.dstStride = 0;
1136+ DataCopyPadExtParams<bool> padParams{true, 0,
1137+ static_cast<uint8_t>(attenMaskSizeAlign - info.s2dealNum), 0};
1138+ DataCopyPad(attenMaskUb, srcGmAddr[maskOffset], dataCopyParams, padParams);
1139+ } else if (curS2EndPos > treeMaskStart) {
1140+ // 部分拷贝:只拷贝 [treeMaskStart, curS2EndPos) 区域
1141+ uint32_t attenMaskSize = curS2EndPos - treeMaskStart;
1142+ uint32_t attenMaskSizeAlign = Align(static_cast<uint32_t>(attenMaskSize + treeMaskStart % 32), 32U);
1143+ uint64_t maskOffset = ComputeAttenMaskOffset<ENABLE_TREE>(info, 0, treeMaskStart, isPre);
1144+ DataCopyExtParams dataCopyParams;
1145+ dataCopyParams.blockCount = 1;
1146+ dataCopyParams.blockLen = curS2EndPos - treeMaskStart;
1147+ dataCopyParams.srcStride = 0;
1148+ dataCopyParams.dstStride = 0;
1149+ DataCopyPadExtParams<bool> padParams;
1150+ padParams.isPad = true;
1151+ padParams.leftPadding = static_cast<uint8_t>(treeMaskStart % 32);
1152+ padParams.rightPadding = static_cast<uint8_t>(attenMaskSizeAlign - (attenMaskSize + treeMaskStart % 32));
1153+ padParams.paddingValue = 0;
1154+ DataCopyPad(attenMaskUb[(treeMaskStart - info.s2StartIdx) / 32 * 32],
1155+ srcGmAddr[maskOffset], dataCopyParams, padParams);
1156+ }
1157+ // else: curS2EndPos <= treeMaskStart,整个 tile 在零区,UB 已初始化为 0,无需拷贝
1158+ } else {
1159+ // 非 TREE:全量拷贝
1160+ uint64_t maskOffset = ComputeAttenMaskOffset<ENABLE_TREE>(info, 0, 0, isPre);
1161+ uint32_t attenMaskSizeAlign = Align(info.s2dealNum, 32U);
1162+ DataCopyExtParams dataCopyParams;
1163+ dataCopyParams.blockCount = 1;
1164+ dataCopyParams.blockLen = info.s2dealNum;
1165+ dataCopyParams.srcStride = info.attenMaskStride - info.s2dealNum;
1166+ dataCopyParams.dstStride = 0;
1167+ DataCopyPadExtParams<bool> padParams{true, 0,
1168+ static_cast<uint8_t>(attenMaskSizeAlign - info.s2dealNum), 0};
1169+ DataCopyPad(attenMaskUb, srcGmAddr[maskOffset], dataCopyParams, padParams);
1170+ }
1076 1171 
1077 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));1172 event_t enQueEvtID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_V));
1078 SetFlag<HardEvent::MTE2_V>(enQueEvtID);1173 SetFlag<HardEvent::MTE2_V>(enQueEvtID);
1079 WaitFlag<HardEvent::MTE2_V>(enQueEvtID);1174 WaitFlag<HardEvent::MTE2_V>(enQueEvtID);
1080 for (uint32_t i = 1; i < info.gs1dealNum; i++) {1175 for (uint32_t i = 1; i < info.gs1dealNum; i++) {
1081- uint32_t offset = i * attenMaskSizeAlign;1176+ uint32_t offset = i * Align(info.s2dealNum, 32U);
1082- DataCopy(attenMaskUb[offset], attenMaskUb, attenMaskSizeAlign);1177+ DataCopy(attenMaskUb[offset], attenMaskUb, Align(info.s2dealNum, 32U));
1083 }1178 }
1084 }1179 }
1085}1180}
@@ -210,6 +210,12 @@ aclnnStatus aclnnFusedInferAttentionScoreV4(
210 </ul>210 </ul>
211 </li>211 </li>
212 <li>sparseMode = 2、3、4时,attenMaskOptional的shape输入支持(2048, 2048)或(1,2048,2048)或(1,1,2048,2048)</li>212 <li>sparseMode = 2、3、4时,attenMaskOptional的shape输入支持(2048, 2048)或(1,2048,2048)或(1,1,2048,2048)</li>
213+ <li>sparseMode = 9时:
214+ <ul>
215+ <li>inputLayout为BSH、BSND、BNSD时,attenMaskOptional的shape输入支持(B, Q_S, Q_S)</li>
216+ <li>inputLayout为TND时,attenMaskOptional的shape输入支持(∑Q_Si²,),即每个batch的Q_Si×Q_Si mask拼接为1D tensor</li>
217+ </ul>
218+ </li>
213 </ul>219 </ul>
214 </td>220 </td>
215 <td>×</td>221 <td>×</td>
@@ -955,6 +961,11 @@ aclnnStatus aclnnFusedInferAttentionScoreV4(
955 <td>block_local</td>961 <td>block_local</td>
956 <td>不支持</td>962 <td>不支持</td>
957 </tr>963 </tr>
964+ <tr>
965+ <td>9</td>
966+ <td>treeMask模式,用于推测解码场景的树形注意力掩码。需传入自定义的tree mask。</td>
967+ <td>非量化支持GQA和MLA场景,全量化仅支持MLA场景。不支持左padding、pseShift、sharedPrefix。输出dtype不支持INT8。每个batch需满足Q_S≤KV_S。inputLayout为BSH/BSND/BNSD时mask shape为(B,Q_S,Q_S);inputLayout为TND时mask shape为(∑Q_Si²,)。</td>
968+ </tr>
958 </tbody>969 </tbody>
959 </table>970 </table>
960 971 
@@ -1492,7 +1503,7 @@ BFLOAT16和INT8不区分高精度和高性能,行无效修正对FLOAT16、BFLO
1492 </tr>1503 </tr>
1493 <tr>1504 <tr>
1494 <td>sparseMode</td>1505 <td>sparseMode</td>
1495- <td>支持0, 3, 4</td>1506+ <td>支持0, 3, 4, 9</td>
1496 </tr>1507 </tr>
1497 <tr>1508 <tr>
1498 <td rowspan="2">PagedAttention</td>1509 <td rowspan="2">PagedAttention</td>
@@ -1541,6 +1552,7 @@ BFLOAT16和INT8不区分高精度和高性能,行无效修正对FLOAT16、BFLO
1541 <li>sparse=0(attenMask为nullptr)</li>1552 <li>sparse=0(attenMask为nullptr)</li>
1542 <li>sparse=3(传优化后的attenMask)</li>1553 <li>sparse=3(传优化后的attenMask)</li>
1543 <li>sparse=4(传优化后的attenMask,需满足:Q_D=K_D=V_D≤256 或 Q_D=K_D=192且V_D=128/192;同时preTokens≥-actualSeqLengths、nextTokens≥-actualSeqLengthsKv、preTokens+nextTokens≥0)</li>1554 <li>sparse=4(传优化后的attenMask,需满足:Q_D=K_D=V_D≤256 或 Q_D=K_D=192且V_D=128/192;同时preTokens≥-actualSeqLengths、nextTokens≥-actualSeqLengthsKv、preTokens+nextTokens≥0)</li>
1555+ <li>sparse=9(传入tree mask,inputLayout为BSH/BSND/BNSD时shape为(B,Q_S,Q_S),inputLayout为TND时shape为(∑Q_Si²,))</li>
1544 </ul>1556 </ul>
1545 </li>1557 </li>
1546 <li>innerPrecise:仅支持0(不带行无效的高精度模式)</li>1558 <li>innerPrecise:仅支持0(不带行无效的高精度模式)</li>
@@ -1641,7 +1653,7 @@ BFLOAT16和INT8不区分高精度和高性能,行无效修正对FLOAT16、BFLO
1641 <tr>1653 <tr>
1642 <td>MASK</td>1654 <td>MASK</td>
1643 <td>sparseMode</td>1655 <td>sparseMode</td>
1644- <td>sparseMode支持0, 3, 4</td>1656+ <td>sparseMode支持0, 3, 4, 9</td>
1645 <td>-</td>1657 <td>-</td>
1646 </tr>1658 </tr>
1647 <tr>1659 <tr>
@@ -1692,8 +1704,8 @@ BFLOAT16和INT8不区分高精度和高性能,行无效修正对FLOAT16、BFLO
1692 </tr>1704 </tr>
1693 <tr>1705 <tr>
1694 <td>sparseMode</td>1706 <td>sparseMode</td>
1695- <td>全量化场景sparseMode仅支持0,3</td>1707+ <td>全量化场景sparseMode仅支持0,3,9</td>
1696- <td>qs=1时,仅支持sparseMode=0,且attenMask为nullptr; qs>1时,支持sparseMode=3,且attenMask的shape为[2048,2048]</td>1708+ <td>qs=1时,仅支持sparseMode=0,且attenMask为nullptr; qs>1时,支持sparseMode=3attenMask的shape为[2048,2048])或sparseMode=9(attenMask的shape见Mask章节)</td>
1697 </tr>1709 </tr>
1698 <tr>1710 <tr>
1699 <td>blockSize</td>1711 <td>blockSize</td>
@@ -1806,7 +1818,7 @@ BFLOAT16和INT8不区分高精度和高性能,行无效修正对FLOAT16、BFLO
1806 </tr>1818 </tr>
1807 <tr>1819 <tr>
1808 <td colspan="2">Mask</td>1820 <td colspan="2">Mask</td>
1809- <td colspan="3">当MTP等于0时,支持sparseMode=0且attenMask为nullptr;当MTP大于0、小于16时,支持sparseMode=3传入优化后的attenMask矩阵,attenMask矩阵shape必须传入(2048*2048);</td>1821+ <td colspan="3">当MTP等于0时,支持sparseMode=0且attenMask为nullptr;当MTP大于0、小于16时,支持sparseMode=3传入优化后的attenMask矩阵,shape2048*2048)或sparseMode=9(传入tree mask,inputLayout为BSH/BSND时shape为(B,Q_S,Q_S),inputLayout为TND时shape为(∑Q_Si²,));</td>
1810 </tr>1822 </tr>
1811 <tr>1823 <tr>
1812 <td rowspan="9">伪量化</td>1824 <td rowspan="9">伪量化</td>
@@ -1977,6 +1989,7 @@ BFLOAT16和INT8不区分高精度和高性能,行无效修正对FLOAT16、BFLO
1977 <li>sparseMode = 0时,attenMaskOptional如果为空指针,或者在左padding场景传入attenMaskOptional,则忽略入参preTokens、nextTokens。</li>1989 <li>sparseMode = 0时,attenMaskOptional如果为空指针,或者在左padding场景传入attenMaskOptional,则忽略入参preTokens、nextTokens。</li>
1978 <li>sparseMode = 2、3、4时,attenMaskOptional的shape需要为(2048,2048)或(1,2048,2048)或(1,1,2048,2048),且需要用户保证传入的attenMaskOptional为下三角,attenMaskOptional为nullptre或者传入的shape不正确报错。</li>1990 <li>sparseMode = 2、3、4时,attenMaskOptional的shape需要为(2048,2048)或(1,2048,2048)或(1,1,2048,2048),且需要用户保证传入的attenMaskOptional为下三角,attenMaskOptional为nullptre或者传入的shape不正确报错。</li>
1979 <li>sparseMode = 1、2、3的场景忽略入参preTokens、nextTokens并按照相关规则赋值。</li>1991 <li>sparseMode = 1、2、3的场景忽略入参preTokens、nextTokens并按照相关规则赋值。</li>
1992+ <li>sparseMode = 9时,非量化支持GQA和MLA场景,全量化仅支持MLA场景。attenMaskOptional不能为空。inputLayout为BSH/BSND/BNSD时shape为(B,Q_S,Q_S);inputLayout为TND时shape为(∑Q_Si²,)。不支持左padding、pseShift、sharedPrefix。输出dtype不支持INT8。每个batch需满足Q_S≤KV_S。</li>
1980 <li>sparseMode取其它值时会报错</li>1993 <li>sparseMode取其它值时会报错</li>
1981 </ul>1994 </ul>
1982 </td>1995 </td>
@@ -160,8 +160,71 @@ ge::graphStatus FiaTilingCheck::CheckFeatureMla()
160 return ge::GRAPH_SUCCESS;160 return ge::GRAPH_SUCCESS;
161}161}
162 162 
163+// 补充sparse9特性的拦截校验
164+ge::graphStatus FiaTilingCheck::CheckFeatureSparseMode() const
165+{
166+ int32_t sparseMode = fiaInfo_.sparseMode;
167+ // sparse9 仅在rope分离场景下存在,不支持左padding、PSE、公共前缀、后量化等特性 拦截s2 >= s1
168+ if (sparseMode == SPARSE_MODE_TREE) {
169+ // 特性校验
170+ OP_CHECK_IF(ropeMode_ != RopeMode::ROPE_SPLIT,
171+ OP_LOGE(opName_,
172+ "In %s situation, when query_rope and key_rope not exist, %s does not support sparse(%d).",
173+ QuantModeToSerialString(quantMode_).c_str(), SPARSE_MODE_NAME.c_str(), sparseMode),
174+ return ge::GRAPH_FAILED);
175+ 
176+ OP_CHECK_IF(fiaInfo_.qPaddingSizeFlag || fiaInfo_.kvPaddingSizeFlag,
177+ OP_LOGE(opName_,
178+ "In %s situation, when sparse is %d, query_padding_size or kv_padding_size should be not exist.",
179+ QuantModeToSerialString(quantMode_).c_str(), sparseMode),
180+ return ge::GRAPH_FAILED);
181+ 
182+ OP_CHECK_IF(fiaInfo_.pseShiftFlag,
183+ OP_LOGE(opName_,
184+ "In %s situation, when sparse is %d, pse_shift should be not exist.",
185+ QuantModeToSerialString(quantMode_).c_str(), sparseMode),
186+ return ge::GRAPH_FAILED);
187+ 
188+ OP_CHECK_IF(fiaInfo_.sysPrefixFlag,
189+ OP_LOGE(opName_,
190+ "In %s situation, when sparse is %d, key_shared_prefix and key_shared_prefix should be not exist.",
191+ QuantModeToSerialString(quantMode_).c_str(), sparseMode),
192+ return ge::GRAPH_FAILED);
193+ 
194+ OP_CHECK_IF(outputType_ == ge::DT_INT8,
195+ OP_LOGE(opName_,
196+ "In %s situation, when sparse is %d, output dtype %d is not currently supported.",
197+ QuantModeToSerialString(quantMode_).c_str(), sparseMode, static_cast<int32_t>(outputType_)),
198+ return ge::GRAPH_FAILED);
199+ 
200+ // s2 >= s1拦截
201+ // tiling下沉场景 由于actualSeqlen得不到,所以不进行校验
202+ if (fiaInfo_.isMaxWorkspace) {
203+ return ge::GRAPH_SUCCESS;
204+ }
205+ // s2=0 的 batch(入图padding或空tensor场景)不校验 s1<=s2
206+ int32_t actualSeqSize = std::min(qSize.size(), kvSize.size());
207+ for (int32_t i = 0; i < actualSeqSize; i++) {
208+ if (kvSize[i] == 0) {
209+ continue;
210+ }
211+ OP_CHECK_IF(qSize[i] > kvSize[i],
212+ OP_LOGE(opName_,
213+ "In %s situation, when sparse is %d, qSize[%d] should less than or equal to kvSize[%d],"
214+ "but got qSize %d and kvSize %d.",
215+ QuantModeToSerialString(quantMode_).c_str(), sparseMode, i, i, qSize[i], kvSize[i]),
216+ return ge::GRAPH_FAILED);
217+ }
218+ }
219+ return ge::GRAPH_SUCCESS;
220+}
221+ 
163ge::graphStatus FiaTilingCheck::CheckFeatureMask() const222ge::graphStatus FiaTilingCheck::CheckFeatureMask() const
164{223{
224+ if (ge::GRAPH_SUCCESS != CheckFeatureSparseMode()) {
225+ return ge::GRAPH_FAILED;
226+ }
227+ 
165 if ((!attenMaskFlag_) && (fiaInfo_.sparseMode != SPARSE_MODE_NO_MASK)) {228 if ((!attenMaskFlag_) && (fiaInfo_.sparseMode != SPARSE_MODE_NO_MASK)) {
166 OP_LOGE(opName_, "when %s is %d, it not 0, %s should not be null.",229 OP_LOGE(opName_, "when %s is %d, it not 0, %s should not be null.",
167 SPARSE_MODE_NAME.c_str(), fiaInfo_.sparseMode, ATTEN_MASK_NAME.c_str());230 SPARSE_MODE_NAME.c_str(), fiaInfo_.sparseMode, ATTEN_MASK_NAME.c_str());
@@ -194,10 +257,11 @@ ge::graphStatus FiaTilingCheck::CheckFeatureMask() const
194 257 
195 if (ropeMode_ == RopeMode::ROPE_SPLIT && vHeadDim_ == 512U) {258 if (ropeMode_ == RopeMode::ROPE_SPLIT && vHeadDim_ == 512U) {
196 int32_t sparseMode = fiaInfo_.sparseMode;259 int32_t sparseMode = fiaInfo_.sparseMode;
197- if (sparseMode != SPARSE_MODE_NO_MASK && sparseMode != SPARSE_MODE_RIGHT_DOWN && sparseMode != SPARSE_MODE_BAND) {260+ if (sparseMode != SPARSE_MODE_NO_MASK && sparseMode != SPARSE_MODE_RIGHT_DOWN &&
261+ sparseMode != SPARSE_MODE_BAND && sparseMode != SPARSE_MODE_TREE) {
198 OP_LOGE(opName_,262 OP_LOGE(opName_,
199 "In %s situation, when query_rope and key_rope exsists and the head dim of value is %u, %s only "263 "In %s situation, when query_rope and key_rope exsists and the head dim of value is %u, %s only "
200- "support 0/3/4, but got %d.",264+ "support 0/3/4/9, but got %d.",
201 QuantModeToSerialString(quantMode_).c_str(), vHeadDim_, SPARSE_MODE_NAME.c_str(), sparseMode);265 QuantModeToSerialString(quantMode_).c_str(), vHeadDim_, SPARSE_MODE_NAME.c_str(), sparseMode);
202 return ge::GRAPH_FAILED;266 return ge::GRAPH_FAILED;
203 }267 }
@@ -229,13 +293,13 @@ ge::graphStatus FiaTilingCheck::CheckFeatureLeftPadding() const
229 293 
230 OP_CHECK_IF(kvStorageMode_ == KvStorageMode::TENSOR_LIST,294 OP_CHECK_IF(kvStorageMode_ == KvStorageMode::TENSOR_LIST,
231 OP_LOGE(opName_,295 OP_LOGE(opName_,
232- "when query_padding_size or kv_padding_size exists, key/value tensorlist is not suppoprted; in this "296+ "when query_padding_size or kv_padding_size exists, key/value tensorlist is not supported; in this "
233 "case, the tensor number of key/value should be 1"),297 "case, the tensor number of key/value should be 1"),
234 return ge::GRAPH_FAILED);298 return ge::GRAPH_FAILED);
235 299 
236 OP_CHECK_IF(kvStorageMode_ == KvStorageMode::PAGE_ATTENTION,300 OP_CHECK_IF(kvStorageMode_ == KvStorageMode::PAGE_ATTENTION,
237 OP_LOGE(opName_,301 OP_LOGE(opName_,
238- "when query_padding_size or kv_padding_size exists, page attention is not suppoprted; in this case, "302+ "when query_padding_size or kv_padding_size exists, page attention is not supported; in this case, "
239 "block_table should exist and block_size is not 0"),303 "block_table should exist and block_size is not 0"),
240 return ge::GRAPH_FAILED);304 return ge::GRAPH_FAILED);
241 305 
@@ -633,10 +633,11 @@ ge::graphStatus FiaTilingCheck::CheckSingleParaSparseMode() const
633 SPARSE_MODE_ALL_MASK,633 SPARSE_MODE_ALL_MASK,
634 SPARSE_MODE_LEFT_UP,634 SPARSE_MODE_LEFT_UP,
635 SPARSE_MODE_RIGHT_DOWN,635 SPARSE_MODE_RIGHT_DOWN,
636- SPARSE_MODE_BAND636+ SPARSE_MODE_BAND,
637+ SPARSE_MODE_TREE
637 };638 };
638 if (ge::GRAPH_SUCCESS != CheckAttrValueSupport(opParamInfo_.sparseMode, sparseModeList, SPARSE_MODE_NAME)) {639 if (ge::GRAPH_SUCCESS != CheckAttrValueSupport(opParamInfo_.sparseMode, sparseModeList, SPARSE_MODE_NAME)) {
639- OP_LOGE(opName_, "sparseMode only supports 0/1/2/3/4, but got %u", *opParamInfo_.sparseMode);640+ OP_LOGE(opName_, "sparseMode only supports 0/1/2/3/4/9, but got %u", *opParamInfo_.sparseMode);
640 return ge::GRAPH_FAILED;641 return ge::GRAPH_FAILED;
641 }642 }
642 return ge::GRAPH_SUCCESS;643 return ge::GRAPH_SUCCESS;
@@ -537,7 +537,7 @@ ge::graphStatus IFATiling::GetRopeAndGqaFlag(const uint32_t sOfQuery, const uint
537 OP_LOGE(ifaContext_->opName, "In case where MLA is not applied, S of Query:%u is invalid. It should be in range [1, 16]", sOfQuery),537 OP_LOGE(ifaContext_->opName, "In case where MLA is not applied, S of Query:%u is invalid. It should be in range [1, 16]", sOfQuery),
538 return ge::GRAPH_FAILED);538 return ge::GRAPH_FAILED);
539 } else if (layout != "TND" && layout != "TND_NTD") {539 } else if (layout != "TND" && layout != "TND_NTD") {
540- OP_CHECK_IF(sOfQuery > 16, OP_LOGE(ifaContext_->opName, "QueryS(%u) should not be bigger than 16 in MLA.", sOfQuery),540+ OP_CHECK_IF(sOfQuery > 32, OP_LOGE(ifaContext_->opName, "QueryS(%u) should not be bigger than 32 in MLA.", sOfQuery),
541 return ge::GRAPH_FAILED);541 return ge::GRAPH_FAILED);
542 }542 }
543 OP_CHECK_IF(layout == "TND" && headDim_ == 512 && !ropeFlag_, OP_LOGE(ifaContext_->opName,543 OP_CHECK_IF(layout == "TND" && headDim_ == 512 && !ropeFlag_, OP_LOGE(ifaContext_->opName,
@@ -588,12 +588,13 @@ ge::graphStatus IFATiling::QKVPreProcess4TND(const std::string layout)
588 std::vector<int64_t> actualSeqQ(actualLenQDims_);588 std::vector<int64_t> actualSeqQ(actualLenQDims_);
589 int64_t tmpQSeqSize = 0;589 int64_t tmpQSeqSize = 0;
590 590 
591- for (int b = 0; b < static_cast<int>(actualLenQDims_); b++) {591+ for (int32_t b = 0; b < static_cast<int>(actualLenQDims_); b++) {
592 actualSeqQ[b] = (b <= 0) ? actualSeqQTnd[0] : (actualSeqQTnd[b] - actualSeqQTnd[b - 1]);592 actualSeqQ[b] = (b <= 0) ? actualSeqQTnd[0] : (actualSeqQTnd[b] - actualSeqQTnd[b - 1]);
593- OP_CHECK_IF((actualSeqQ[b] < 0) || (actualSeqQ[b] > 16), // 16 MTP最大QS593+ OP_CHECK_IF((actualSeqQ[b] < 0) || (actualSeqQ[b] > 32), // 32 MTP最大QS
594- OP_LOGE(ifaContext_->opName, "%s QS(%ld) of batch(%d) computed by the query's actual sequence lengths should be in range [0, 16].", layout.c_str(), actualSeqQ[b], b),594+ OP_LOGE(ifaContext_->opName, "%s QS(%ld) of batch(%d) computed by the query's actual sequence lengths should be in range [0, 32].", layout.c_str(), actualSeqQ[b], b),
595 return ge::GRAPH_FAILED);595 return ge::GRAPH_FAILED);
596 tmpQSeqSize = std::max(tmpQSeqSize, actualSeqQ[b]);596 tmpQSeqSize = std::max(tmpQSeqSize, actualSeqQ[b]);
597+ qSeqSquareSum_ += actualSeqQ[b] * actualSeqQ[b];
597 }598 }
598 599 
599 OP_CHECK_IF((tSeqSize_ != actualSeqQTnd[actualLenQDims_ - 1]),600 OP_CHECK_IF((tSeqSize_ != actualSeqQTnd[actualLenQDims_ - 1]),
@@ -1973,14 +1974,14 @@ void IFATiling::GetActualSeqInfo(const int64_t *actualSeqKv, ActualSeqInfo &actu
1973 // TND格式,actual_seq_q定义为累积长度,这里做转化再分核1974 // TND格式,actual_seq_q定义为累积长度,这里做转化再分核
1974 const int64_t *actualSeqQTnd = ifaContext_->actualSeqLengthsQ.tensor->GetData<int64_t>();1975 const int64_t *actualSeqQTnd = ifaContext_->actualSeqLengthsQ.tensor->GetData<int64_t>();
1975 actualSeqInfo.actualSeqQ[0] = actualSeqQTnd[0];1976 actualSeqInfo.actualSeqQ[0] = actualSeqQTnd[0];
1976- for (int b = 1; b < static_cast<int>(bSize); b++) {1977+ for (int32_t b = 1; b < static_cast<int>(bSize); b++) {
1977 actualSeqInfo.actualSeqQ[b] = actualSeqQTnd[b] - actualSeqQTnd[b - 1];1978 actualSeqInfo.actualSeqQ[b] = actualSeqQTnd[b] - actualSeqQTnd[b - 1];
1978 if (actualLenDims_ != 1U) {1979 if (actualLenDims_ != 1U) {
1979 actualSeqInfo.maxActualseqkv = std::max(actualSeqInfo.maxActualseqkv, actualSeqKv[b]);1980 actualSeqInfo.maxActualseqkv = std::max(actualSeqInfo.maxActualseqkv, actualSeqKv[b]);
1980 }1981 }
1981 }1982 }
1982 } else {1983 } else {
1983- for (int b = 0; b < static_cast<int>(bSize); b++) {1984+ for (int32_t b = 0; b < static_cast<int>(bSize); b++) {
1984 actualSeqInfo.actualSeqQ[b] = qSeqSize_; // 需要检查1985 actualSeqInfo.actualSeqQ[b] = qSeqSize_; // 需要检查
1985 if (actualLenDims_ != 1U) {1986 if (actualLenDims_ != 1U) {
1986 actualSeqInfo.maxActualseqkv = std::max(actualSeqInfo.maxActualseqkv, actualSeqKv[b]);1987 actualSeqInfo.maxActualseqkv = std::max(actualSeqInfo.maxActualseqkv, actualSeqKv[b]);
@@ -3220,8 +3221,10 @@ void IFATiling::FillTilingBaseParamsMla()
3220 tilingDataMla_.baseParams.set_actualLenDims(actualLenDims_);3221 tilingDataMla_.baseParams.set_actualLenDims(actualLenDims_);
3221 tilingDataMla_.baseParams.set_attenMaskFlag(attenMaskFlag_ ? 1 : 0);3222 tilingDataMla_.baseParams.set_attenMaskFlag(attenMaskFlag_ ? 1 : 0);
3222 tilingDataMla_.baseParams.set_attenMaskSize(attenMaskSize_);3223 tilingDataMla_.baseParams.set_attenMaskSize(attenMaskSize_);
3224+ tilingDataMla_.baseParams.set_sparseMode(sparseMode_);
3223 tilingDataMla_.baseParams.set_outputLayout(static_cast<uint32_t>(outputLayout_));3225 tilingDataMla_.baseParams.set_outputLayout(static_cast<uint32_t>(outputLayout_));
3224 tilingDataMla_.baseParams.set_softmaxLseFlag(softmaxLseFlag_ ? 1 : 0);3226 tilingDataMla_.baseParams.set_softmaxLseFlag(softmaxLseFlag_ ? 1 : 0);
3227+ tilingDataMla_.baseParams.set_tSeqSize(tSeqSize_);
3225}3228}
3226 3229 
3227// for flash decode3230// for flash decode
@@ -4107,7 +4110,7 @@ uint32_t IFATiling::GetTotalQBlockNum() const
4107 uint32_t totalQblockSum = 0;4110 uint32_t totalQblockSum = 0;
4108 uint32_t curSeqLenQ = 0;4111 uint32_t curSeqLenQ = 0;
4109 uint32_t preSeqLenQ = 0;4112 uint32_t preSeqLenQ = 0;
4110- for (int bIdx = 0; bIdx < static_cast<int>(actualLenQDims_); bIdx++) {4113+ for (int32_t bIdx = 0; bIdx < static_cast<int>(actualLenQDims_); bIdx++) {
4111 // actualLenDataQ里的值单调递增4114 // actualLenDataQ里的值单调递增
4112 curSeqLenQ = static_cast<uint32_t>(actualLenDataQ[bIdx]);4115 curSeqLenQ = static_cast<uint32_t>(actualLenDataQ[bIdx]);
4113 uint32_t tmpBlkNum = static_cast<uint32_t>(curSeqLenQ - preSeqLenQ + seqStepQ_ - 1) / seqStepQ_;4116 uint32_t tmpBlkNum = static_cast<uint32_t>(curSeqLenQ - preSeqLenQ + seqStepQ_ - 1) / seqStepQ_;
@@ -329,7 +329,7 @@ ge::graphStatus IFATiling::CheckInputQKVTypeMatch() const
329 329 
330 OP_CHECK_IF(330 OP_CHECK_IF(
331 ((!ropeFlag_) && inputQType_ == ge::DT_INT8 && inputKvType_ == ge::DT_INT8),331 ((!ropeFlag_) && inputQType_ == ge::DT_INT8 && inputKvType_ == ge::DT_INT8),
332- OP_LOGE(ifaContext_->opName, "When QueryRope/KeyRope is null and Qs(%u) in [1, 16], not support qkv datatype all int8.", qSeqSize_), return ge::GRAPH_FAILED);332+ OP_LOGE(ifaContext_->opName, "When QueryRope/KeyRope is null and Qs(%u) in [1, 32], not support qkv datatype all int8.", qSeqSize_), return ge::GRAPH_FAILED);
333 333 
334 OP_CHECK_IF(334 OP_CHECK_IF(
335 ((inputQType_ == ge::DT_FLOAT16) && (inputKvType_ != ge::DT_FLOAT16 && inputKvType_ != ge::DT_INT8 && inputKvType_ != ge::DT_INT4)), OP_LOGE(ifaContext_->opName, "when input Q type is fp16, KV type %d should be fp16 or int8 or int4", inputKvType_),335 ((inputQType_ == ge::DT_FLOAT16) && (inputKvType_ != ge::DT_FLOAT16 && inputKvType_ != ge::DT_INT8 && inputKvType_ != ge::DT_INT4)), OP_LOGE(ifaContext_->opName, "when input Q type is fp16, KV type %d should be fp16 or int8 or int4", inputKvType_),
@@ -1345,6 +1345,87 @@ ge::graphStatus IFATiling::ProcessPseShift()
1345 return ge::GRAPH_SUCCESS;1345 return ge::GRAPH_SUCCESS;
1346}1346}
1347 1347 
1348+ge::graphStatus IFATiling::CheckTreeSparseMask()
1349+{
1350+ // 仅支持MLA全量化,且要求s2 >= s1
1351+ OP_CHECK_IF(socVersion_ != IfaSocVersion::SOC_ASCEND_910B,
1352+ OP_LOGE(ifaContext_->opName, "Tree sparse9 is only supported in ASCEND_910B."),
1353+ return ge::GRAPH_FAILED);
1354+ 
1355+ OP_CHECK_IF(!(ropeFlag_ && quantFlag_),
1356+ OP_LOGE(ifaContext_->opName, "Tree Sparse9 is only supported in MLA full quant situation."),
1357+ return ge::GRAPH_FAILED);
1358+ 
1359+
1360+ // tiling下沉场景,获取不到actualseqlen,不进行校验
1361+ if (isWorkspace_) {
1362+ return ge::GRAPH_SUCCESS;
1363+ }
1364+ 
1365+ // 补充s2 >= s1的拦截,在MLA全量化场景,必须开启PA(NZ),所以actualSeqKV不进行累加
1366+ if (inputLayout_ == IfaLayout::TND) {
1367+ const int64_t *actualSeqQTnd = ifaContext_->actualSeqLengthsQ.tensor->GetData<int64_t>();
1368+ const int64_t *actualSeqKVTnd = ifaContext_->actualSeqLengths.tensor->GetData<int64_t>();
1369+ int64_t qActSize = 0;
1370+ int64_t kvActSize = 0;
1371+ 
1372+ // s2=0 的 batch(入图padding或空tensor场景)不校验 s1<=s2
1373+ for (int32_t b = 0; b < actualLenQDims_; b++) {
1374+ kvActSize = actualSeqKVTnd[b];
1375+ if (kvActSize == 0) {
1376+ continue;
1377+ }
1378+ qActSize = (b == 0) ? actualSeqQTnd[0] : (actualSeqQTnd[b] - actualSeqQTnd[b - 1]);
1379+ OP_CHECK_IF(qActSize > kvActSize,
1380+ OP_LOGE(ifaContext_->opName,
1381+ "In MLA full quant situation, when sparse is %d, qSize(%ld) should less than or equal to kvSize(%ld).",
1382+ sparseMode_, qActSize, kvActSize),
1383+ return ge::GRAPH_FAILED);
1384+ }
1385+ } else {
1386+ OP_CHECK_IF(qSeqSize_ > seqSize_,
1387+ OP_LOGE(ifaContext_->opName,
1388+ "In MLA full quant situation, when sparse is %d, qSize(%ld) should less than or equal to kvSize(%ld).",
1389+ sparseMode_, qSeqSize_, seqSize_),
1390+ return ge::GRAPH_FAILED);
1391+ }
1392+ return ge::GRAPH_SUCCESS;
1393+}
1394+ 
1395+ge::graphStatus IFATiling::CheckTreeSparseMaskShape()
1396+{
1397+ // sparse9时需要传入Mask,在TND场景,传入∑s1²,一维矩阵
1398+ // 非TND场景传入[B, S1, S1]
1399+ auto maskShape = ifaContext_->attenMask.tensor;
1400+ if (inputLayout_ == IfaLayout::TND) {
1401+ // sparse9时需要传入Mask,在TND场景,传入∑s1²,一维矩阵
1402+ OP_CHECK_IF(maskShape == nullptr,
1403+ OP_LOGE(ifaContext_->opName, "TND/TND_NTD need input attenMask when sparse = 9."), return ge::GRAPH_FAILED);
1404+ 
1405+ auto shape = ifaContext_->attenMask.tensor->GetStorageShape();
1406+ OP_CHECK_IF(shape.GetDimNum() != 1U,
1407+ OP_LOGE(ifaContext_->opName, "TND/TND_NTD when sparse = 9, atten_mask tensor shape must be 1 dimension."),
1408+ return ge::GRAPH_FAILED);
1409+
1410+ if (!isWorkspace_) {
1411+ OP_CHECK_IF(shape.GetDim(0) != qSeqSquareSum_,
1412+ OP_LOGE(ifaContext_->opName, "TND/TND_NTD when sparse = 9, atten_mask tensor shape must be ∑s1²."),
1413+ return ge::GRAPH_FAILED);
1414+ }
1415+ attenMaskFlag_ = true;
1416+ } else {
1417+ OP_CHECK_IF(maskShape == nullptr,
1418+ OP_LOGE(ifaContext_->opName, "When sparse = 9, atten_mask should not be null."), return ge::GRAPH_FAILED);
1419+ 
1420+ auto shape = ifaContext_->attenMask.tensor->GetStorageShape();
1421+ OP_CHECK_IF(shape.GetDimNum() != 3U || shape.GetDim(0) != batchSize_ || shape.GetDim(1) != qSeqSize_ || shape.GetDim(2) != qSeqSize_ ,
1422+ OP_LOGE(ifaContext_->opName, "BSH when sparse = 9, atten_mask tensor shape must be [B, S1, S1]."),
1423+ return ge::GRAPH_FAILED);
1424+ attenMaskFlag_ = true;
1425+ }
1426+ return ge::GRAPH_SUCCESS;
1427+}
1428+ 
1348ge::graphStatus IFATiling::CheckTndMaskShapeWithSparseMode()1429ge::graphStatus IFATiling::CheckTndMaskShapeWithSparseMode()
1349{1430{
1350 auto maskShape = ifaContext_->attenMask.tensor; // input shape = 41431 auto maskShape = ifaContext_->attenMask.tensor; // input shape = 4
@@ -1361,8 +1442,13 @@ ge::graphStatus IFATiling::CheckTndMaskShapeWithSparseMode()
1361 OP_LOGE(ifaContext_->opName, "TND/TND_NTD when sparse = 3, atten_mask tensor shape must be [2048, 2048]."),1442 OP_LOGE(ifaContext_->opName, "TND/TND_NTD when sparse = 3, atten_mask tensor shape must be [2048, 2048]."),
1362 return ge::GRAPH_FAILED);1443 return ge::GRAPH_FAILED);
1363 attenMaskFlag_ = true;1444 attenMaskFlag_ = true;
1445+ } else if (sparseMode_ == 9U) {
1446+ if (ge::GRAPH_SUCCESS == CheckTreeSparseMask() &&
1447+ ge::GRAPH_SUCCESS == CheckTreeSparseMaskShape()) {
1448+ attenMaskFlag_ = true;
1449+ }
1364 } else {1450 } else {
1365- OP_LOGE(ifaContext_->opName, "TND/TND_NTD only support sparse(%u) = 0 or 3.", sparseMode_);1451+ OP_LOGE(ifaContext_->opName, "TND/TND_NTD only support sparse(%u) = 0/3/9.", sparseMode_);
1366 return ge::GRAPH_FAILED;1452 return ge::GRAPH_FAILED;
1367 }1453 }
1368 return ge::GRAPH_SUCCESS;1454 return ge::GRAPH_SUCCESS;
@@ -1370,14 +1456,23 @@ ge::graphStatus IFATiling::CheckTndMaskShapeWithSparseMode()
1370 1456 
1371ge::graphStatus IFATiling::CheckMaskShapeWithQSeq() const1457ge::graphStatus IFATiling::CheckMaskShapeWithQSeq() const
1372{1458{
1373- if (antiQuantFlag_ || (ifaContext_->dequantScaleQuery.tensor != nullptr && ropeFlag_)) {1459+ if (quantFlag_) {
1374- OP_CHECK_IF((ropeFlag_ && qSeqSize_ > 1U && static_cast<int32_t>(sparseMode_) != 3),1460+ OP_CHECK_IF((ropeFlag_ && qSeqSize_ > 1U && (static_cast<int32_t>(sparseMode_) != 3 && static_cast<int32_t>(sparseMode_) != 9)),
1375- OP_LOGE(ifaContext_->opName, "when queryS > 1, sparseMode(%d) only support 3 "1461+ OP_LOGE(ifaContext_->opName, "when queryS > 1, sparseMode(%d) only support 3/9 "
1376- "in MLA when antiquant or full quant situation.", static_cast<int32_t>(sparseMode_)),1462+ "in MLA when full quant situation.", static_cast<int32_t>(sparseMode_)),
1377- return ge::GRAPH_FAILED);1463+ return ge::GRAPH_FAILED);
1378 OP_CHECK_IF((ropeFlag_ && qSeqSize_ == 1U && static_cast<int32_t>(sparseMode_) != 0),1464 OP_CHECK_IF((ropeFlag_ && qSeqSize_ == 1U && static_cast<int32_t>(sparseMode_) != 0),
1379 OP_LOGE(ifaContext_->opName, "when queryS = 1, sparseMode(%d) only support 0 "1465 OP_LOGE(ifaContext_->opName, "when queryS = 1, sparseMode(%d) only support 0 "
1380- "in MLA when antiquant or full quant situation.", static_cast<int32_t>(sparseMode_)),1466+ "in MLA when full quant situation.", static_cast<int32_t>(sparseMode_)),
1467+ return ge::GRAPH_FAILED);
1468+ } else if (antiQuantFlag_) {
1469+ OP_CHECK_IF((ropeFlag_ && qSeqSize_ > 1U && static_cast<int32_t>(sparseMode_) != 3),
1470+ OP_LOGE(ifaContext_->opName, "when queryS > 1, sparseMode(%d) only support 3 "
1471+ "in MLA when antiquant situation.", static_cast<int32_t>(sparseMode_)),
1472+ return ge::GRAPH_FAILED);
1473+ OP_CHECK_IF((ropeFlag_ && qSeqSize_ == 1U && static_cast<int32_t>(sparseMode_) != 0),
1474+ OP_LOGE(ifaContext_->opName, "when queryS = 1, sparseMode(%d) only support 0 "
1475+ "in MLA when antiquant situation.", static_cast<int32_t>(sparseMode_)),
1381 return ge::GRAPH_FAILED);1476 return ge::GRAPH_FAILED);
1382 } else {1477 } else {
1383 OP_CHECK_IF((ropeFlag_ && qSeqSize_ > 1U &&1478 OP_CHECK_IF((ropeFlag_ && qSeqSize_ > 1U &&
@@ -1414,6 +1509,11 @@ ge::graphStatus IFATiling::CheckAttenMaskShape()
1414 OP_LOGW(ifaContext_->opName, "atten_mask tensor exist, but atten_mask shape size is 0.");1509 OP_LOGW(ifaContext_->opName, "atten_mask tensor exist, but atten_mask shape size is 0.");
1415 return ge::GRAPH_SUCCESS;1510 return ge::GRAPH_SUCCESS;
1416 }1511 }
1512+ 
1513+ if (sparseMode_ == 9U && CheckTreeSparseMaskShape() == ge::GRAPH_SUCCESS) {
1514+ attenMaskFlag_ = true;
1515+ return ge::GRAPH_SUCCESS;
1516+ }
1417 return ge::GRAPH_FAILED;1517 return ge::GRAPH_FAILED;
1418}1518}
1419 1519 
@@ -1430,6 +1530,10 @@ ge::graphStatus IFATiling::ProcessAttenMask()
1430 return ge::GRAPH_FAILED;1530 return ge::GRAPH_FAILED;
1431 }1531 }
1432 1532
1533+ if (sparseMode_ == 9U && CheckTreeSparseMask() != ge::GRAPH_SUCCESS) {
1534+ return ge::GRAPH_FAILED;
1535+ }
1536+ 
1433 if (CheckAttenMaskShape() == ge::GRAPH_SUCCESS) {1537 if (CheckAttenMaskShape() == ge::GRAPH_SUCCESS) {
1434 return ge::GRAPH_SUCCESS;1538 return ge::GRAPH_SUCCESS;
1435 }1539 }
@@ -85,6 +85,8 @@ private:
85 ge::graphStatus ProcessBaseInputs();85 ge::graphStatus ProcessBaseInputs();
86 ge::graphStatus ProcessOptionalTensors();86 ge::graphStatus ProcessOptionalTensors();
87 ge::graphStatus ProcessPseShift();87 ge::graphStatus ProcessPseShift();
88+ ge::graphStatus CheckTreeSparseMask();
89+ ge::graphStatus CheckTreeSparseMaskShape();
88 ge::graphStatus CheckTndMaskShapeWithSparseMode();90 ge::graphStatus CheckTndMaskShapeWithSparseMode();
89 ge::graphStatus CheckMaskShapeWithQSeq() const;91 ge::graphStatus CheckMaskShapeWithQSeq() const;
90 ge::graphStatus CheckAttenMaskShape();92 ge::graphStatus CheckAttenMaskShape();
@@ -360,6 +362,7 @@ private:
360 uint32_t sMax_ = 0;362 uint32_t sMax_ = 0;
361 uint32_t tSeqSize_ = 1; // TND格式T轴长度363 uint32_t tSeqSize_ = 1; // TND格式T轴长度
362 uint32_t qSeqSize_ = 1; // 默认S1 = 1364 uint32_t qSeqSize_ = 1; // 默认S1 = 1
365+ uint64_t qSeqSquareSum_ = 0; // 给Sparse9 TND下mask大小用
363 uint32_t blockTypeSize_ = 0; // 计算中间量大小366 uint32_t blockTypeSize_ = 0; // 计算中间量大小
364 uint32_t kvSplitPart_ = 1;367 uint32_t kvSplitPart_ = 1;
365 368 
@@ -37,8 +37,10 @@ TILING_DATA_FIELD_DEF(uint32_t, actualLenDims)
37TILING_DATA_FIELD_DEF(uint32_t, antiquantMode)37TILING_DATA_FIELD_DEF(uint32_t, antiquantMode)
38TILING_DATA_FIELD_DEF(uint32_t, attenMaskFlag)38TILING_DATA_FIELD_DEF(uint32_t, attenMaskFlag)
39TILING_DATA_FIELD_DEF(uint32_t, attenMaskSize)39TILING_DATA_FIELD_DEF(uint32_t, attenMaskSize)
40+TILING_DATA_FIELD_DEF(uint32_t, sparseMode)
40TILING_DATA_FIELD_DEF(uint32_t, outputLayout) // 输出Layout41TILING_DATA_FIELD_DEF(uint32_t, outputLayout) // 输出Layout
41TILING_DATA_FIELD_DEF(uint32_t, softmaxLseFlag)42TILING_DATA_FIELD_DEF(uint32_t, softmaxLseFlag)
43+TILING_DATA_FIELD_DEF(uint32_t, tSeqSize) // TND T轴总长度(含padding)
42END_TILING_DATA_DEF44END_TILING_DATA_DEF
43REGISTER_TILING_DATA_CLASS(IncreFlashAttentionBaseParamsMlaOp, IncreFlashAttentionBaseParamsMla)45REGISTER_TILING_DATA_CLASS(IncreFlashAttentionBaseParamsMlaOp, IncreFlashAttentionBaseParamsMla)
44 46 
@@ -336,6 +336,7 @@ protected:
336 uint64_t mSizeVStart = 0ULL;336 uint64_t mSizeVStart = 0ULL;
337 uint64_t kvSeqSize = 0ULL;337 uint64_t kvSeqSize = 0ULL;
338 uint64_t qSeqSize = 1ULL;338 uint64_t qSeqSize = 1ULL;
339+ uint64_t tSeqSize = 1ULL; // TND T轴总长度(含padding)
339 340 
340 // pageAttention341 // pageAttention
341 uint32_t kvCacheBlockSize = 0;342 uint32_t kvCacheBlockSize = 0;
@@ -347,6 +348,7 @@ protected:
347 // attention mask348 // attention mask
348 bool attenMaskFlag = false;349 bool attenMaskFlag = false;
349 uint32_t attenMaskSizeAlign = 0U;350 uint32_t attenMaskSizeAlign = 0U;
351+ uint32_t sparseMode = 3U;
350 352 
351 // offset353 // offset
352 uint64_t tensorACoreOffset = 0ULL;354 uint64_t tensorACoreOffset = 0ULL;
@@ -531,6 +533,9 @@ protected:
531 __aicore__ inline bool IsSkipAttenMask(const ExtraInfoMla &info, uint32_t startRow, uint32_t dealRowCount);533 __aicore__ inline bool IsSkipAttenMask(const ExtraInfoMla &info, uint32_t startRow, uint32_t dealRowCount);
532 // 针对BSND/BSH/TND等切G的格式拷贝AttentionMask534 // 针对BSND/BSH/TND等切G的格式拷贝AttentionMask
533 __aicore__ inline void AttenMaskCopyForSplitG(const ExtraInfoMla &info, LocalTensor<bool> &attenMaskUb, uint32_t startRow, uint32_t dealRowCount);535 __aicore__ inline void AttenMaskCopyForSplitG(const ExtraInfoMla &info, LocalTensor<bool> &attenMaskUb, uint32_t startRow, uint32_t dealRowCount);
536+ __aicore__ inline void AttenMaskCopyForTree(const ExtraInfoMla &info, LocalTensor<bool> &attenMaskUb, uint32_t startRow, uint32_t dealRowCount);
537+ __aicore__ inline void AttenMaskCopyNoFull(LocalTensor<bool> &attenMaskUb, const ExtraInfoMla &info, uint32_t s1StartIdx, uint32_t s1EndIdx);
538+ 
534 __aicore__ inline void ElewiseCompute(const ExtraInfoMla &info, LocalTensor<T> &mmResUb, TBuf<> &tmpBuf, uint32_t startRow,539 __aicore__ inline void ElewiseCompute(const ExtraInfoMla &info, LocalTensor<T> &mmResUb, TBuf<> &tmpBuf, uint32_t startRow,
535 uint32_t dealRowCount, uint32_t columnCount, uint32_t actualColumnCount);540 uint32_t dealRowCount, uint32_t columnCount, uint32_t actualColumnCount);
536 541 
@@ -562,6 +567,7 @@ protected:
562 567 
563 __aicore__ inline void InitAllZeroOutput(uint32_t bIdx, uint32_t n2Idx);568 __aicore__ inline void InitAllZeroOutput(uint32_t bIdx, uint32_t n2Idx);
564 __aicore__ inline void InitSoftmaxLseAllInfOutput(uint32_t bIdx, uint32_t n2Idx);569 __aicore__ inline void InitSoftmaxLseAllInfOutput(uint32_t bIdx, uint32_t n2Idx);
570+ __aicore__ inline void InitOutputSingleCore();
565 __aicore__ inline uint64_t SeqLenFromTensorList(uint32_t bIdx);571 __aicore__ inline uint64_t SeqLenFromTensorList(uint32_t bIdx);
566 572 
567 __aicore__ inline void CopyFixedUbToGm(const GlobalTensor<T> &dst, const LocalTensor<T> &src, size_t size);573 __aicore__ inline void CopyFixedUbToGm(const GlobalTensor<T> &dst, const LocalTensor<T> &src, size_t size);
@@ -590,6 +596,7 @@ template <typename IFAT> __aicore__ inline void IncreFlashAttentionAttenPreloadM
590 596 
591 kvSeqSize = tilingData->baseParams.seqSize;597 kvSeqSize = tilingData->baseParams.seqSize;
592 qSeqSize = tilingData->baseParams.qSeqSize;598 qSeqSize = tilingData->baseParams.qSeqSize;
599+ tSeqSize = tilingData->baseParams.tSeqSize;
593 600 
594 s1SizeSub = tilingData->increFlashAttentionSingleCoreParams.s1SplitSize; // 切块大小Si601 s1SizeSub = tilingData->increFlashAttentionSingleCoreParams.s1SplitSize; // 切块大小Si
595 s1Outer = (qSeqSize + s1SizeSub - 1) / s1SizeSub;602 s1Outer = (qSeqSize + s1SizeSub - 1) / s1SizeSub;
@@ -597,6 +604,7 @@ template <typename IFAT> __aicore__ inline void IncreFlashAttentionAttenPreloadM
597 604 
598 attenMaskFlag = (tilingData->baseParams.attenMaskFlag != 0) ? true : false;605 attenMaskFlag = (tilingData->baseParams.attenMaskFlag != 0) ? true : false;
599 attenMaskSize = tilingData->baseParams.attenMaskSize;606 attenMaskSize = tilingData->baseParams.attenMaskSize;
607+ sparseMode = tilingData->baseParams.sparseMode;
600 608 
601 softmaxLseFlag = tilingData->baseParams.softmaxLseFlag;609 softmaxLseFlag = tilingData->baseParams.softmaxLseFlag;
602 610 
@@ -732,6 +740,50 @@ __aicore__ inline void IncreFlashAttentionAttenPreloadMla<IFAT>::InitSoftmaxLseA
732 }740 }
733}741}
734 742 
743+template <typename IFAT>
744+__aicore__ inline void IncreFlashAttentionAttenPreloadMla<IFAT>::InitOutputSingleCore()
745+{
746+ if (usedCoreNum != 0) {
747+ uint32_t initOutputEventId = 0U;
748+ SetFlag<AscendC::HardEvent::MTE3_V>(initOutputEventId);
749+ // TND 场景:sparse9 有 s2=0 的 batch,其 Q token 不会被计算路径写入,必须清零
750+ if constexpr (LAYOUT_T == LAYOUT::TND) {
751+ if (sparseMode == 9U && actualLenDims > 1U) {
752+ WaitFlag<AscendC::HardEvent::MTE3_V>(initOutputEventId);
753+ // 按 batch 轮询分配,跳过 s2≠0 的 batch
754+ for (uint64_t bIdx = (uint64_t)tmpBlockIdx; bIdx < batchSize;
755+ bIdx += (uint64_t)usedCoreNum) {
756+ uint64_t s2 = actualSeqLengthsGm.GetValue(bIdx);
757+ if (s2 != 0) {
758+ continue;
759+ }
760+ uint64_t tBase = (bIdx == 0) ? 0 : actualSeqLengthsGmQ.GetValue(bIdx - 1);
761+ uint64_t s1 = actualSeqLengthsGmQ.GetValue(bIdx) - tBase;
762+ if (s1 == 0) {
763+ continue;
764+ }
765+ // TND: [T, N, D],batch 对应的 T 区间连续,一次清零
766+ uint64_t outputOffset = tBase * qHeadNum * headDim;
767+ uint64_t outputSize = s1 * qHeadNum * headDim;
768+ if constexpr (IsSameType<OUT_T, int8_t>::value) {
769+ GlobalTensor<half> attentionOutTmpGm;
770+ attentionOutTmpGm.SetGlobalBuffer(
771+ reinterpret_cast<__gm__ half *>(attentionOutGm.GetPhyAddr(0)));
772+ matmul::InitOutput<half>(
773+ attentionOutTmpGm[outputOffset / 2], outputSize / 2, 0);
774+ } else {
775+ matmul::InitOutput<OUT_T>(
776+ attentionOutGm[outputOffset], outputSize, 0);
777+ }
778+ }
779+ SetFlag<AscendC::HardEvent::MTE3_V>(initOutputEventId);
780+ }
781+ }
782+ WaitFlag<AscendC::HardEvent::MTE3_V>(initOutputEventId);
783+ SyncAll();
784+ }
785+}
786+ 
735template <typename IFAT>787template <typename IFAT>
736__aicore__ inline void IncreFlashAttentionAttenPreloadMla<IFAT>::GetActualSeqLen(uint32_t bIdx, uint32_t s1Idx)788__aicore__ inline void IncreFlashAttentionAttenPreloadMla<IFAT>::GetActualSeqLen(uint32_t bIdx, uint32_t s1Idx)
737{789{
@@ -895,6 +947,11 @@ __aicore__ inline void IncreFlashAttentionAttenPreloadMla<IFAT>::Init(
895 947 
896 InitActualSeqLen(actualSeqLengthsQ, actualSeqLengths);948 InitActualSeqLen(actualSeqLengthsQ, actualSeqLengths);
897 949 
950+ // sparse9 TND padding 场景初始化 output 为 0
951+ if ASCEND_IS_AIV {
952+ InitOutputSingleCore();
953+ }
954+ 
898 if constexpr (PAGE_ATTENTION) {955 if constexpr (PAGE_ATTENTION) {
899 blockTableGm.SetGlobalBuffer((__gm__ int32_t *)blockTable);956 blockTableGm.SetGlobalBuffer((__gm__ int32_t *)blockTable);
900 }957 }
@@ -1880,6 +1937,144 @@ IncreFlashAttentionAttenPreloadMla<IFAT>::AttenMaskCopyForSplitG(const ExtraInfo
1880 attenMaskUb = attenMaskUbDst.template ReinterpretCast<bool>();1937 attenMaskUb = attenMaskUbDst.template ReinterpretCast<bool>();
1881}1938}
1882 1939 
1940+// 新增sparse9的处理
1941+template <typename IFAT>
1942+__aicore__ inline void
1943+IncreFlashAttentionAttenPreloadMla<IFAT>::AttenMaskCopyNoFull(LocalTensor<bool> &attenMaskUb, const ExtraInfoMla &info, uint32_t s1StartIdx, uint32_t s1EndIdx)
1944+{
1945+ uint32_t actualSeqQ = qSeqSize;
1946+ if constexpr (LAYOUT_T == LAYOUT::TND) {
1947+ actualSeqQ = info.actS1Size;
1948+ }
1949+ 
1950+ uint32_t treeMaskStart = info.s2Size - actualSeqQ;
1951+ uint32_t curS2EndPos = info.s2Idx * singleProcessSInnerSize + info.actualSingleProcessSInnerSize;
1952+ uint32_t curS2StartPos = info.s2Idx * singleProcessSInnerSize;
1953+ 
1954+ uint64_t attenMaskBatchStride = actualSeqQ * actualSeqQ;
1955+ uint64_t attenMaskStride = actualSeqQ;
1956+ 
1957+ uint64_t bOffset;
1958+ uint64_t s1Offset;
1959+ if constexpr (LAYOUT_T == LAYOUT::TND) {
1960+ // TND格式下mask是一维张量∑s1²,batch偏移是前面所有batch的s1²累积和
1961+ uint64_t batchOffset = 0;
1962+ for (uint32_t i = 0; i < info.bIdx; i++) {
1963+ uint64_t s1i = GetBalanceActualSeqLengths(actualSeqLengthsGmQ, i);
1964+ batchOffset += s1i * s1i;
1965+ }
1966+ bOffset = batchOffset;
1967+ // TND格式下s1StartIdx是当前S1块内的局部偏移,完整局部S1索引需加上s1Idx*s1SizeSub
1968+ s1Offset = (static_cast<uint64_t>(info.s1Idx) * s1SizeSub + s1StartIdx) * attenMaskStride;
1969+ } else {
1970+ bOffset = static_cast<uint64_t>(info.bIdx) * static_cast<uint64_t>(attenMaskBatchStride);
1971+ s1Offset = (s1StartIdx % actualSeqQ) * attenMaskStride;
1972+ }
1973+ uint64_t s2Offset = curS2StartPos > treeMaskStart ? curS2StartPos - treeMaskStart : 0;
1974+ 
1975+ uint64_t maskOffset = bOffset + s1Offset + s2Offset;
1976+ 
1977+ if (curS2StartPos < treeMaskStart) { //mask占不满UB空间
1978+ // 不对齐场景下的搬运
1979+ DataCopyExtParams dataCopyParams;
1980+ dataCopyParams.blockCount = s1EndIdx - s1StartIdx;
1981+ dataCopyParams.blockLen = curS2EndPos - treeMaskStart;
1982+ dataCopyParams.srcStride = attenMaskStride - (curS2EndPos - treeMaskStart);
1983+ dataCopyParams.dstStride = (treeMaskStart - curS2StartPos) / 32;
1984+ 
1985+ DataCopyPadExtParams<bool> padParams;
1986+ padParams.isPad = true;
1987+ padParams.leftPadding = static_cast<uint8_t>(treeMaskStart % 32);
1988+ padParams.rightPadding = static_cast<uint8_t>(Align(static_cast<uint32_t>(curS2EndPos - treeMaskStart + treeMaskStart % 32), 32U) - \
1989+ (curS2EndPos - treeMaskStart + treeMaskStart % 32));
1990+ padParams.paddingValue = 0;
1991+ DataCopyPad(attenMaskUb[(treeMaskStart - curS2StartPos) / 32 * 32], attenMaskBoolGm[maskOffset], dataCopyParams, padParams);
1992+ } else { // mask 占满UB空间
1993+ DataCopyExtParams dataCopyParams;
1994+ dataCopyParams.blockCount = s1EndIdx - s1StartIdx;
1995+ dataCopyParams.blockLen = info.actualSingleProcessSInnerSize;
1996+ dataCopyParams.srcStride = attenMaskStride - info.actualSingleProcessSInnerSize;
1997+ dataCopyParams.dstStride = 0;
1998+ DataCopyPadExtParams<bool> padParams{true, 0, static_cast<uint8_t>(info.actualSingleProcessSInnerSizeAlign - info.actualSingleProcessSInnerSize), 0};
1999+ DataCopyPad(attenMaskUb, attenMaskBoolGm[maskOffset], dataCopyParams, padParams);
2000+ }
2001+}
2002+ 
2003+template <typename IFAT>
2004+__aicore__ inline void IncreFlashAttentionAttenPreloadMla<IFAT>::AttenMaskCopyForTree(const ExtraInfoMla &info,
2005+ LocalTensor<bool> &attenMaskUb, uint32_t startRow, uint32_t dealRowCount)
2006+{
2007+ uint32_t s1StartIdx = (mSizeVStart + startRow) / info.gSize;
2008+ uint32_t s1EndIdx = (mSizeVStart + startRow + dealRowCount - 1) / info.gSize;
2009+ uint32_t s1Count = s1EndIdx - s1StartIdx + 1;
2010+ 
2011+ uint32_t actualSeqQ = qSeqSize;
2012+ if constexpr (LAYOUT_T == LAYOUT::TND) {
2013+ actualSeqQ = info.actS1Size;
2014+ }
2015+ 
2016+ 
2017+ attenMaskSizeAlign = Align(info.actualSingleProcessSInnerSize, 32U);
2018+ // 第一步,将mask的 ub空间赋值为0
2019+ // 申请的16k空间,前8k给从GM拷贝到Ub时使用,后8k用来在拓展G轴时使用
2020+ attenMaskUb = inputQue2.AllocTensor<bool>();
2021+ LocalTensor<int16_t> mask16_0 = attenMaskUb.template ReinterpretCast<int16_t>();
2022+ AscendC::Duplicate(mask16_0, static_cast<int16_t>(0), s1Count * attenMaskSizeAlign / sizeof(int16_t));
2023+ attenMaskUb = mask16_0.template ReinterpretCast<bool>();
2024+ // V→MTE2 跨流水线同步:确保 V pipeline 的 Duplicate 完成后,MTE2 的 DataCopyPad 才能写入
2025+ event_t eventIdVMte2 = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::V_MTE2));
2026+ SetFlag<HardEvent::V_MTE2>(eventIdVMte2);
2027+ WaitFlag<HardEvent::V_MTE2>(eventIdVMte2);
2028+ 
2029+ // 第二步,计算偏移 从Gm上把mask拷过来
2030+ AttenMaskCopyNoFull(attenMaskUb, info, s1StartIdx, s1EndIdx + 1);
2031+ 
2032+ // 第三步,添加拷贝同步信号
2033+ inputQue2.template EnQue(attenMaskUb);
2034+ attenMaskUb = inputQue2.DeQue<bool>();
2035+ 
2036+ LocalTensor<int16_t> mask16 = attenMaskUb.template ReinterpretCast<int16_t>();
2037+ LocalTensor<int16_t> attenMaskUbDst = mask16[BUFFER_SIZE_BYTE_16K / 4];
Y
Yyang-binrong2月27日

【提示】4等数组需要加注释,防止引入告警

likedislike
2038+ 
2039+ uint32_t headGCount = 0;
2040+ uint32_t firstGIdx = (mSizeVStart + startRow) % info.gSize;
2041+ if (s1Count > 1) {
2042+ headGCount = info.gSize - firstGIdx;
2043+ } else {
2044+ headGCount = dealRowCount;
2045+ }
2046+ uint64_t dstMaskOffset = 0;
2047+ uint64_t srcMaskBaseOffset = 0;
2048+ // head
2049+ SetMaskCount();
2050+ SetVectorMask<int16_t, MaskMode::COUNTER>(attenMaskSizeAlign / 2);
2051+ Copy<int16_t, false>(attenMaskUbDst[dstMaskOffset], mask16[srcMaskBaseOffset],
2052+ AscendC::MASK_PLACEHOLDER, headGCount,
2053+ {1, 1, static_cast<uint16_t>(attenMaskSizeAlign / 32), 0});
2054+ dstMaskOffset += headGCount * attenMaskSizeAlign / 2;
2055+ srcMaskBaseOffset += attenMaskSizeAlign / 2;
2056+ // mid
2057+ uint32_t reminRowCount = dealRowCount - headGCount;
2058+ uint32_t midS1Count = reminRowCount / info.gSize;
2059+ uint32_t tailGSize = reminRowCount % info.gSize;
2060+ for (uint32_t midIdx = 0; midIdx < midS1Count; midIdx++) {
2061+ Copy<int16_t, false>(attenMaskUbDst[dstMaskOffset], mask16[srcMaskBaseOffset],
2062+ AscendC::MASK_PLACEHOLDER, info.gSize,
2063+ {1, 1, static_cast<uint16_t>(attenMaskSizeAlign / 32), 0});
Y
Yyang-binrong2月27日

【提示】32等数字会引入告警,加注释

likedislike
2064+ dstMaskOffset += info.gSize * attenMaskSizeAlign / 2;
2065+ srcMaskBaseOffset += attenMaskSizeAlign / 2;
2066+ }
2067+ // tail
2068+ if (tailGSize > 0) {
2069+ Copy<int16_t, false>(attenMaskUbDst[dstMaskOffset], mask16[srcMaskBaseOffset],
2070+ AscendC::MASK_PLACEHOLDER, tailGSize,
2071+ {1, 1, static_cast<uint16_t>(attenMaskSizeAlign / 32), 0});
2072+ }
2073+ SetMaskNorm();
2074+ ResetMask();
2075+ attenMaskUb = attenMaskUbDst.template ReinterpretCast<bool>();
2076+}
2077+ 
1883template <typename IFAT>2078template <typename IFAT>
1884__aicore__ inline bool2079__aicore__ inline bool
1885IncreFlashAttentionAttenPreloadMla<IFAT>::IsSkipAttenMask(const ExtraInfoMla &info, uint32_t startRow, uint32_t dealRowCount)2080IncreFlashAttentionAttenPreloadMla<IFAT>::IsSkipAttenMask(const ExtraInfoMla &info, uint32_t startRow, uint32_t dealRowCount)
@@ -1889,13 +2084,22 @@ IncreFlashAttentionAttenPreloadMla<IFAT>::IsSkipAttenMask(const ExtraInfoMla &in
1889 actualSeqQ = info.actS1Size;2084 actualSeqQ = info.actS1Size;
1890 }2085 }
1891 2086 
2087+ uint32_t s2EndPos = info.s2Idx * singleProcessSInnerSize + info.actualSingleProcessSInnerSize;
2088+ // 新增sparse9的处理,sparse9和sparse3类似,但是sparse9是用户自己传入的mask矩阵,算子无法感知具体数值,所以涉及到基本块都要参与计算
2089+ if (sparseMode == 9U) {
2090+ if (s2EndPos > (info.s2Size - actualSeqQ)) {
2091+ return false;
2092+ } else {
2093+ return true;
2094+ }
2095+ }
2096+
1892 // s2<s1时,必然走mask2097 // s2<s1时,必然走mask
1893 if (info.s2Size < actualSeqQ) {2098 if (info.s2Size < actualSeqQ) {
1894 return false;2099 return false;
1895 }2100 }
1896 2101 
1897 // 当前的s2位置不超过需要打标记的位置时,不需要mask2102 // 当前的s2位置不超过需要打标记的位置时,不需要mask
1898- uint32_t s2EndPos = info.s2Idx * singleProcessSInnerSize + info.actualSingleProcessSInnerSize;
1899 if (s2EndPos <= (info.s2Size - actualSeqQ + 1)) {2103 if (s2EndPos <= (info.s2Size - actualSeqQ + 1)) {
1900 return true;2104 return true;
1901 }2105 }
@@ -1966,7 +2170,11 @@ IncreFlashAttentionAttenPreloadMla<IFAT>::ElewiseCompute(const ExtraInfoMla &inf
1966 }2170 }
1967 attenMaskUb = attenMaskUbDst;2171 attenMaskUb = attenMaskUbDst;
1968 } else { // BSH/BSND/TND2172 } else { // BSH/BSND/TND
1969- AttenMaskCopyForSplitG(info, attenMaskUb, startRow, dealRowCount);2173+ if (sparseMode == 9U) {
2174+ AttenMaskCopyForTree(info, attenMaskUb, startRow, dealRowCount);
2175+ } else {
2176+ AttenMaskCopyForSplitG(info, attenMaskUb, startRow, dealRowCount);
2177+ }
1970 }2178 }
1971 }2179 }
1972 2180 
@@ -15,6 +15,7 @@
15| 6 | prefix压缩模式。 | - |15| 6 | prefix压缩模式。 | - |
16| 7 | varlen外切场景,rightDownCausal模式。 | 仅varlen场景支持。 |16| 7 | varlen外切场景,rightDownCausal模式。 | 仅varlen场景支持。 |
17| 8 | varlen外切场景,leftUpCausal模式。 | 仅varlen场景支持。 |17| 8 | varlen外切场景,leftUpCausal模式。 | 仅varlen场景支持。 |
18+| 9 | treeMask模式。 | 非量化支持GQA和MLA场景,全量化仅支持MLA场景。 |
18 19 
19attenMask的工作原理为,在Mask为True的位置遮蔽query(Q)与key(K)的转置矩阵乘积的值,示意如下:20attenMask的工作原理为,在Mask为True的位置遮蔽query(Q)与key(K)的转置矩阵乘积的值,示意如下:
20 21 
@@ -146,4 +147,28 @@ Masked $QK^T$矩阵示意如下,在第二个batch对query进行切分,key和
146- 基于sparseMode=2进行外切产生的band模式的sparse的参数应符合以下条件:147- 基于sparseMode=2进行外切产生的band模式的sparse的参数应符合以下条件:
147 - preTokens >= first_Skv。148 - preTokens >= first_Skv。
148 - nextTokens >= first_Sq - first_Skv,根据实际情况进行配置。149 - nextTokens >= first_Sq - first_Skv,根据实际情况进行配置。
149- - 当前模式下不支持可选输入pse。150+ - 当前模式下不支持可选输入pse。
151+ 
152+## sparseMode=9
153+ 
154+sparseMode为9时,代表treeMask模式,用于推测解码(speculative decoding)场景下的树形注意力掩码。用户需传入自定义的树形mask,mask中值为1的位置会被遮蔽。
155+ 
156+树形mask矩阵特征如下:
157+- 对角线位置(s1==s2):值为0,表示token关注自身。
158+- 上三角位置(s1<s2):值为1,表示不关注未来token。
159+- 下三角位置(s1>s2):值为0或1,由树结构决定部分注意力关系。
160+ 
161+attenMask输入格式:
162+- inputLayout为BSH、BSND或BNSD时:attenMask的shape为(B, S1, S1),每个batch传入S1×S1大小的tree mask。
163+ 
164+ ![原理图](../figures/sparsemode为9_BSND_BNSD示意图.png)
165+ 
166+- inputLayout为TND时:attenMask为1D紧凑格式,shape为(∑S1i²,),即每个batch的S1i×S1i mask拼接传入。
167+ 
168+ ![原理图](../figures/sparsemode为9_TND示意图.png)
169+ 
170+约束说明:
171+- 非量化场景支持GQA和MLA,全量化场景仅支持MLA。
172+- 不支持左padding、pseShift、sharedPrefix。
173+- 输出dtype不支持INT8。
174+- 每个batch需满足Q_S ≤ KV_S。