* 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 dynamic_quant_base.h
* \brief
*/
#ifndef DYNAMIC_QUANT_BASE_H
#define DYNAMIC_QUANT_BASE_H
#include "kernel_tiling/kernel_tiling.h"
#include "kernel_operator.h"
namespace DynamicQuantNDOpt {
using namespace AscendC;
constexpr uint32_t BUFFER_NUM = 1;
constexpr uint32_t DOUBLE = 2;
constexpr uint32_t DOUBLE_BUFFER_NUM = 2;
constexpr uint32_t FIFTEEN = 15;
constexpr uint32_t SIXTEEN = 16;
constexpr uint32_t SEVEN = 7;
constexpr uint32_t EIGHT = 8;
constexpr uint32_t THIRTY_ONE = 31;
constexpr uint32_t THIRTY_TWO = 32;
constexpr uint32_t SIXTY_THREE = 63;
constexpr uint32_t SIXTY_FOUR = 64;
constexpr uint32_t QUANT_TEMP_LEN = 128;
constexpr uint32_t MAX_VALUE_NUM = 8;
constexpr uint32_t MAX_REPEAT = 255;
constexpr uint32_t UB_RESERVED_LENGTH = 1024;
constexpr int32_t ELEM_PER_REP_FP32 = 64;
constexpr int32_t ELEM_PER_REP_HALF = 128;
constexpr float DYNAMIC_QUANT_INT8_SCALE = 255.0;
constexpr float DYNAMIC_QUANT_INT8_SYM_SCALE = 127.0;
constexpr float DYNAMIC_QUANT_INT8_OFFSET = 127.0;
constexpr float DYNAMIC_QUANT_INT4_SCALE = 15.0;
constexpr float DYNAMIC_QUANT_INT4_SYM_SCALE = 7.0;
constexpr float DYNAMIC_QUANT_INT4_OFFSET = 7.0;
constexpr float DYNAMIC_QUANT_EPSINON = 1e-12;
constexpr float MIN_FLOAT_VALUE = -3.4028235e+38f;
constexpr float MAX_FLOAT_VALUE = 3.402823466e+38f;
class DynamicQuantBase
{
public:
__aicore__ inline DynamicQuantBase()
{}
__aicore__ inline void ParseTilingData(const DynamicQuantTilingData* tilingData)
{
tilingData_.rowLen = tilingData->rowLen;
tilingData_.coreNum = tilingData->coreNum;
tilingData_.headCoreNum = tilingData->headCoreNum;
tilingData_.rowPerHeadCore = tilingData->rowPerHeadCore;
tilingData_.rowPerTailCore = tilingData->rowPerTailCore;
tilingData_.multiRowNumTailCore = tilingData->multiRowNumTailCore;
tilingData_.multiRowNumHeadCore = tilingData->multiRowNumHeadCore;
tilingData_.innerLoopEle = tilingData->innerLoopEle;
tilingData_.innerLoopTail = tilingData->innerLoopTail;
tilingData_.innerLoopTimes = tilingData->innerLoopTimes;
tilingData_.alignGroupNum = tilingData->alignGroupNum;
tilingData_.groupNum = tilingData->groupNum;
tilingData_.hasSmooth = tilingData->hasSmooth;
tilingData_.ubSize = tilingData->ubSize;
}
__aicore__ inline void InitBaseBuffer()
{
pPipe->InitBuffer(quantTemp, QUANT_TEMP_LEN * sizeof(float));
pPipe->InitBuffer(tempCastUb, (sizeHalfLen + SIXTY_THREE) / SIXTY_FOUR * SIXTY_FOUR * sizeof(float));
pPipe->InitBuffer(fp32_buf_, sizeHalfLen * sizeof(float));
}
__aicore__ inline void InitLargeShapeBaseBuffer()
{
pPipe->InitBuffer(quantTemp, THIRTY_TWO);
pPipe->InitBuffer(tempCastUb, tilingData_.innerLoopEle * sizeof(float));
pPipe->InitBuffer(fp32_buf_, tilingData_.innerLoopEle * sizeof(float));
}
__aicore__ inline void DuplicateConst()
{
if (!isAsymmetrical) {
LocalTensor<uint8_t> tmp = quantTemp.Get<uint8_t>();
constScale = tmp.ReinterpretCast<float>();
constInvScale = tmp[MAX_VALUE_NUM * sizeof(float)].ReinterpretCast<float>();
scaleTmp = tmp[MAX_VALUE_NUM * sizeof(float) * 2].ReinterpretCast<float>();
quantScaleTmp = tmp[MAX_VALUE_NUM * sizeof(float) * 3].ReinterpretCast<float>();
brcbTmp = tmp[MAX_VALUE_NUM * sizeof(float) * 4].ReinterpretCast<float>();
if constexpr (ORIG_DTYPE_Y == DT_INT4) {
Duplicate<float>(constScale, DYNAMIC_QUANT_INT4_SYM_SCALE, MAX_VALUE_NUM);
Duplicate<float>(constInvScale, float(1) / DYNAMIC_QUANT_INT4_SYM_SCALE, MAX_VALUE_NUM);
} else {
Duplicate<float>(constScale, DYNAMIC_QUANT_INT8_SYM_SCALE, MAX_VALUE_NUM);
Duplicate<float>(constInvScale, float(1) / DYNAMIC_QUANT_INT8_SYM_SCALE, MAX_VALUE_NUM);
}
}
}
__aicore__ inline void InitCommonParams()
{
lenHead = rowPerHeadCore * tilingData_.rowLen;
lenTail = rowPerTailCore * tilingData_.rowLen;
if (ORIG_DTYPE_Y == DT_INT4) {
outLenHead = lenHead >> 1;
outLenTail = lenTail >> 1;
} else {
outLenHead = lenHead;
outLenTail = lenTail;
}
}
__aicore__ inline void InitParams(GM_ADDR offset)
{
blockIdx = GetBlockIdx();
if (offset != nullptr) {
isAsymmetrical = true;
}
rowPerHeadCore = tilingData_.rowPerHeadCore;
rowPerTailCore = tilingData_.rowPerTailCore;
if (blockIdx < tilingData_.headCoreNum) {
multiRowNum = tilingData_.multiRowNumHeadCore;
loopCnt = rowPerHeadCore / multiRowNum;
remainRow = rowPerHeadCore % multiRowNum;
} else if (blockIdx >= tilingData_.headCoreNum && blockIdx < tilingData_.coreNum) {
multiRowNum = tilingData_.multiRowNumTailCore;
loopCnt = rowPerTailCore / multiRowNum;
remainRow = rowPerTailCore % multiRowNum;
} else {
}
sizeHalfLen = (tilingData_.rowLen + FIFTEEN) / SIXTEEN * SIXTEEN;
rightPadding = sizeHalfLen - tilingData_.rowLen;
if (rightPadding > 0) {
isPad = true;
}
if (ORIG_DTYPE_Y == DT_INT4) {
outAlignLen = (tilingData_.rowLen + SIXTY_THREE) / SIXTY_FOUR * SIXTY_FOUR;
} else {
outAlignLen = (tilingData_.rowLen + THIRTY_ONE) / THIRTY_TWO * THIRTY_TWO;
}
sizeFloatLen = (multiRowNum + SEVEN) / EIGHT * EIGHT;
lenMultiRow = multiRowNum * sizeHalfLen;
lenGMMultiRow = multiRowNum * tilingData_.rowLen;
outLen = multiRowNum * outAlignLen;
InitCommonParams();
}
__aicore__ inline void InitLargeShapeParams(GM_ADDR offset)
{
blockIdx = GetBlockIdx();
if (offset != nullptr) {
isAsymmetrical = true;
}
rowPerHeadCore = tilingData_.rowPerHeadCore;
rowPerTailCore = tilingData_.rowPerTailCore;
if (blockIdx < tilingData_.headCoreNum) {
loopCnt = rowPerHeadCore;
} else if (blockIdx >= tilingData_.headCoreNum && blockIdx < tilingData_.coreNum) {
loopCnt = rowPerTailCore;
} else {
}
uint32_t sizeTailLen = (tilingData_.innerLoopTail + FIFTEEN) / SIXTEEN * SIXTEEN;
rightPadding = sizeTailLen - tilingData_.innerLoopTail;
sizeFloatLen = (loopCnt + SEVEN) / EIGHT * EIGHT;
if (ORIG_DTYPE_Y == DT_INT4) {
outAlignLen = (tilingData_.innerLoopEle + SIXTY_THREE) / SIXTY_FOUR * SIXTY_FOUR;
} else {
outAlignLen = tilingData_.innerLoopEle;
}
InitCommonParams();
}
__aicore__ inline void GetScale(float max_value, float& scale)
{
if (ORIG_DTYPE_Y == DT_INT4) {
scale = DYNAMIC_QUANT_INT4_SYM_SCALE / max_value;
} else {
scale = DYNAMIC_QUANT_INT8_SYM_SCALE / max_value;
}
}
__aicore__ inline void GetScaleAndOffset(float max_value, float min_value, float& scale, float& offset)
{
if (ORIG_DTYPE_Y == DT_INT4) {
scale = GetMax((max_value - min_value) / DYNAMIC_QUANT_INT4_SCALE, DYNAMIC_QUANT_EPSINON);
offset = DYNAMIC_QUANT_INT4_OFFSET - max_value / scale;
} else {
scale = GetMax((max_value - min_value) / DYNAMIC_QUANT_INT8_SCALE, DYNAMIC_QUANT_EPSINON);
offset = DYNAMIC_QUANT_INT8_OFFSET - max_value / scale;
}
}
__aicore__ inline void ReduceMaxInplace(const LocalTensor<float>& src_local, uint32_t count)
{
uint64_t repsFp32 = count >> 6;
uint64_t offsetsFp32 = repsFp32 << 6;
uint64_t remsFp32 = count & 0x3f;
if (likely(repsFp32 > 1)) {
if (repsFp32 - 1 > MAX_REPEAT) {
Max(src_local, src_local[ELEM_PER_REP_FP32], src_local, ELEM_PER_REP_FP32, MAX_REPEAT,
{1, 1, 1, 0, 8, 0});
PipeBarrier<PIPE_V>();
Max(src_local, src_local[ELEM_PER_REP_FP32 * MAX_REPEAT], src_local, ELEM_PER_REP_FP32, repsFp32 - MAX_REPEAT - 1,
{1, 1, 1, 0, 8, 0});
} else {
Max(src_local, src_local[ELEM_PER_REP_FP32], src_local, ELEM_PER_REP_FP32, repsFp32 - 1,
{1, 1, 1, 0, 8, 0});
}
PipeBarrier<PIPE_V>();
}
if (unlikely(remsFp32 > 0) && unlikely(offsetsFp32 > 0)) {
Max(src_local, src_local[offsetsFp32], src_local, remsFp32, 1, {1, 1, 1, 0, 8, 0});
PipeBarrier<PIPE_V>();
}
uint32_t mask = repsFp32 > 0 ? ELEM_PER_REP_FP32 : count;
WholeReduceMax(src_local, src_local, mask, 1, 8, 1, 8);
}
__aicore__ inline void ReduceMaxInplace(const LocalTensor<half>& src_local, uint32_t count)
{
uint64_t repsHalf = count >> 7;
uint64_t offsetsHalf = repsHalf << 7;
uint64_t remsHalf = count & 0x7f;
if (likely(repsHalf > 1)) {
Max(src_local, src_local[ELEM_PER_REP_HALF], src_local, ELEM_PER_REP_HALF, repsHalf - 1,
{1, 1, 1, 0, 8, 0});
PipeBarrier<PIPE_V>();
}
if (unlikely(remsHalf > 0) && unlikely(offsetsHalf > 0)) {
Max(src_local, src_local[offsetsHalf], src_local, remsHalf, 1, {1, 1, 1, 0, 8, 0});
PipeBarrier<PIPE_V>();
}
uint32_t mask = repsHalf > 0 ? ELEM_PER_REP_HALF : count;
WholeReduceMax(src_local, src_local, mask, 1, 8, 1, 8);
}
__aicore__ inline float GetMax(float a, float b)
{
return a > b ? a : b;
}
template <typename T1, typename T2>
__aicore__ inline auto GetMin(T1 a, T2 b)
{
return a < b ? a : b;
};
protected:
TPipe* pPipe = nullptr;
DynamicQuantTilingData tilingData_;
uint32_t blockIdx, sizeFloatLen, sizeHalfLen, outAlignLen, multiRowNum;
uint32_t rowPerHeadCore, rowPerTailCore;
uint32_t lenHead, lenTail, lenMultiRow, lenGMMultiRow, outLen;
uint32_t outInt8PaddingLen, outInt4PaddingLen;
uint32_t outLenHead, outLenTail, originOutSize;
uint32_t loopCnt = 0;
uint32_t remainRow = 0;
uint8_t rightPadding = 0;
bool isPad = false;
bool isAsymmetrical = false;
AscendC::TBuf<AscendC::TPosition::VECCALC> quantTemp, fp32_buf_, tempCastUb;
LocalTensor<float> constScale, constInvScale, scaleTmp, brcbTmp, quantScaleTmp;
};
}
#endif