* 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 flat_quant_tiling.cpp
* \brief
*/
#include "register/tilingdata_base.h"
#include "register/op_impl_registry.h"
#include "log/log.h"
#include "op_host/tiling_base.h"
#include "op_host/tiling_util.h"
#include "op_host/tiling_templates_registry.h"
#include "flat_quant_tiling.h"
namespace optiling {
using namespace Ops::NN::OpTiling;
constexpr uint8_t INPUT_TENSOR_NUM = 3;
constexpr uint8_t INDEX_TWO = 2;
constexpr uint8_t INDEX_ONE = 1;
constexpr uint8_t INDEX_ZERO = 0;
constexpr uint8_t DIM_THREE = 3;
constexpr uint8_t DIM_TWO = 2;
constexpr uint8_t BYTE_LEN_2 = 2;
constexpr uint8_t BYTE_LEN_4 = 4;
constexpr uint8_t CEIL_SIZE = 16;
constexpr int32_t MAX_K_SIZE = 262144;
constexpr int32_t NUM_EIGHT = 8;
constexpr int32_t MAX_MN_SIZE = 256;
constexpr int32_t FACTOR_TWO = 2;
constexpr int32_t K_PER_VEC = 4;
constexpr int32_t BASE_SIZE = 128;
constexpr int32_t L1_SIZE = 512 * 1024;
constexpr float ZERO_FLOAT = 0.0f;
constexpr float ONE_FLOAT = 1.0f;
constexpr uint8_t MM_BASE_MODE = 1;
constexpr uint8_t MM_DOUBLE_MODE = 2;
constexpr uint8_t MM_SPLIT_MODE = 3;
constexpr uint8_t MM_HIGH_MODE = 4;
constexpr uint8_t MM_ONE_MODE = 5;
constexpr uint64_t WORK_SPACE_SIZE = 16 * 1024 * 1024;
constexpr uint64_t WORK_SPACE_SIZE_APT = 48 * 1024 * 1024;
class FlatQuantTiling {
public:
explicit FlatQuantTiling(gert::TilingContext *context) : tilingContext(context){};
ge::graphStatus RunBigKernelTiling();
private:
bool CheckShapes() const;
bool CheckClipRatio() const;
void GetKernelMode(int64_t aivNum);
ge::graphStatus GetTCubeTiling();
template <typename T1, typename T2>
inline auto CeilA2B(T1 a, T2 b) const -> T1;
private:
ge::DataType dataType = ge::DT_UNDEFINED;
gert::TilingContext *tilingContext = nullptr;
gert::Shape xShape;
gert::Shape p1Shape;
gert::Shape p2Shape;
uint8_t mmMode = MM_BASE_MODE;
const float *clipRatio = nullptr;
FlatQuantTilingData tilingData;
};
ge::graphStatus FlatQuantTiling::RunBigKernelTiling()
{
auto xTensor = tilingContext->GetInputTensor(INDEX_ZERO);
auto p1Tensor = tilingContext->GetInputTensor(INDEX_ONE);
auto p2Tensor = tilingContext->GetInputTensor(INDEX_TWO);
const gert::RuntimeAttrs *attrs = tilingContext->GetAttrs();
if (xTensor == nullptr || p1Tensor == nullptr || p2Tensor == nullptr || attrs == nullptr) {
return ge::GRAPH_FAILED;
}
for (int i = 0; i < INPUT_TENSOR_NUM; i++) {
auto temp = tilingContext->GetInputDesc(i);
if (dataType == ge::DT_UNDEFINED) {
dataType = temp->GetDataType();
} else if (dataType != temp->GetDataType()) {
return ge::GRAPH_FAILED;
}
}
xShape = tilingContext->GetInputShape(INDEX_ZERO)->GetOriginShape();
p1Shape = tilingContext->GetInputShape(INDEX_ONE)->GetOriginShape();
p2Shape = tilingContext->GetInputShape(INDEX_TWO)->GetOriginShape();
clipRatio = attrs->GetAttrPointer<float>(0);
if (!CheckShapes() || !CheckClipRatio()) {
return ge::GRAPH_FAILED;
}
tilingData.set_K(xShape.GetDim(INDEX_ZERO));
tilingData.set_M(xShape.GetDim(INDEX_ONE));
tilingData.set_N(xShape.GetDim(INDEX_TWO));
tilingData.set_clipRatio(*clipRatio);
auto compileInfo = tilingContext->GetCompileInfo<FlatQuantCompileInfo>();
int64_t aivNum = FACTOR_TWO * compileInfo->coreNum;
GetKernelMode(aivNum);
if (mmMode == MM_HIGH_MODE && GetTCubeTiling() != ge::GRAPH_SUCCESS) {
return ge::GRAPH_FAILED;
}
tilingContext->SetBlockDim(compileInfo->coreNum);
tilingContext->SetTilingKey(mmMode);
tilingData.SaveToBuffer(
tilingContext->GetRawTilingData()->GetData(), tilingContext->GetRawTilingData()->GetCapacity());
tilingContext->GetRawTilingData()->SetDataSize(tilingData.GetDataSize());
return ge::GRAPH_SUCCESS;
}
void FlatQuantTiling::GetKernelMode(int64_t aivNum)
{
int64_t N = xShape.GetDim(INDEX_TWO);
int64_t M = xShape.GetDim(INDEX_ONE);
int64_t K = xShape.GetDim(INDEX_ZERO);
int64_t Nceil = CeilA2B(N, CEIL_SIZE) * CEIL_SIZE;
int64_t Mceil = CeilA2B(M, CEIL_SIZE) * CEIL_SIZE;
auto outDtype = tilingContext->GetOutputDesc(0)->GetDataType();
auto ascendcPlatform = platform_ascendc::PlatformAscendC(tilingContext->GetPlatformInfo());
if (outDtype == ge::DT_FLOAT4_E2M1) {
if (IsRegbaseSocVersion(tilingContext)) {
mmMode = MM_HIGH_MODE;
size_t* workspaces = tilingContext->GetWorkspaceSizes(1);
int64_t useAivNum = CeilA2B(K, K_PER_VEC) <= aivNum ? CeilA2B(K, K_PER_VEC) : aivNum;
workspaces[0] =
useAivNum * (K_PER_VEC * M * N * BYTE_LEN_2 + FACTOR_TWO * K_PER_VEC * Mceil * N * BYTE_LEN_4) +
WORK_SPACE_SIZE_APT;
}
} else {
if (M == 1 && N % NUM_EIGHT == 0) {
mmMode = MM_ONE_MODE;
} else if (Mceil <= BASE_SIZE / FACTOR_TWO && Nceil <= BASE_SIZE && K > 1) {
mmMode = MM_DOUBLE_MODE;
} else if (Mceil * Mceil + Nceil * Nceil + FACTOR_TWO * FACTOR_TWO * Mceil * Nceil > L1_SIZE / BYTE_LEN_2) {
mmMode = MM_HIGH_MODE;
} else if (Mceil > BASE_SIZE || Nceil > BASE_SIZE) {
mmMode = MM_SPLIT_MODE;
} else {
mmMode = MM_BASE_MODE;
}
size_t* workspaces = tilingContext->GetWorkspaceSizes(1);
if (mmMode == MM_HIGH_MODE) {
int64_t useAivNum = CeilA2B(K, K_PER_VEC) <= aivNum ? CeilA2B(K, K_PER_VEC) : aivNum;
workspaces[0] =
useAivNum * (K_PER_VEC * M * N * BYTE_LEN_2 + FACTOR_TWO * K_PER_VEC * Mceil * N * BYTE_LEN_4) +
WORK_SPACE_SIZE;
} else if (mmMode == MM_DOUBLE_MODE) {
K += (K % FACTOR_TWO);
workspaces[0] = (K * Mceil * N + Mceil * Mceil) * BYTE_LEN_2 + WORK_SPACE_SIZE;
} else if (mmMode == MM_ONE_MODE) {
workspaces[0] = K * Mceil * Nceil * BYTE_LEN_2 + WORK_SPACE_SIZE;
} else {
workspaces[0] = (K * Mceil * N) * BYTE_LEN_2 + WORK_SPACE_SIZE;
}
}
}
ge::graphStatus FlatQuantTiling::GetTCubeTiling()
{
int64_t N = xShape.GetDim(INDEX_TWO);
int64_t M = xShape.GetDim(INDEX_ONE);
int64_t K = xShape.GetDim(INDEX_ZERO);
auto mmDataType = static_cast<matmul_tiling::DataType>(dataType);
matmul_tiling::MatmulApiTiling mmTilingR;
mmTilingR.SetAType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, mmDataType);
mmTilingR.SetBType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, mmDataType);
mmTilingR.SetCType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, mmDataType);
mmTilingR.SetOrgShape(K * M, N, N);
mmTilingR.SetShape(K_PER_VEC * M, N, N);
if (mmTilingR.GetTiling(tilingData.matmulTilingR) == -1) {
return ge::GRAPH_FAILED;
}
matmul_tiling::MatmulApiTiling mmTilingL;
mmTilingL.SetAType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, mmDataType);
mmTilingL.SetBType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, mmDataType);
mmTilingL.SetCType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT);
mmTilingL.SetOrgShape(M, N, M);
mmTilingL.SetShape(M, N, M);
if (mmTilingL.GetTiling(tilingData.matmulTilingL) == -1) {
return ge::GRAPH_FAILED;
}
return ge::GRAPH_SUCCESS;
}
bool FlatQuantTiling::CheckShapes() const
{
if (xShape.GetDimNum() != DIM_THREE || p1Shape.GetDimNum() != DIM_TWO || p2Shape.GetDimNum() != DIM_TWO) {
return false;
}
int64_t K = xShape.GetDim(INDEX_ZERO);
int64_t M = xShape.GetDim(INDEX_ONE);
int64_t N = xShape.GetDim(INDEX_TWO);
if (K > MAX_K_SIZE || M > MAX_MN_SIZE || N > MAX_MN_SIZE) {
return false;
}
if(p1Shape.GetDim(INDEX_ZERO) != M || p1Shape.GetDim(INDEX_ONE) != M || p2Shape.GetDim(INDEX_ZERO) != N || p2Shape.GetDim(INDEX_ONE) != N) {
return false;
}
return true;
}
bool FlatQuantTiling::CheckClipRatio() const
{
return clipRatio != nullptr && *clipRatio > ZERO_FLOAT && *clipRatio <= ONE_FLOAT;
}
template <typename T1, typename T2>
inline auto FlatQuantTiling::CeilA2B(T1 a, T2 b) const -> T1
{
if (b != 0) {
return (a + b - 1) / b;
} else {
return a;
}
}
static ge::graphStatus Tiling4FlatQuantTiling(gert::TilingContext *context)
{
FlatQuantTiling tilingObject(context);
return tilingObject.RunBigKernelTiling();
}
static ge::graphStatus TilingPrepareTiling(gert::TilingParseContext *context)
{
auto compileInfo =context->GetCompiledInfo<FlatQuantCompileInfo>();
OP_CHECK_NULL_WITH_CONTEXT(context, compileInfo);
auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo());
compileInfo->coreNum = ascendcPlatform.GetCoreNumAic();
OP_CHECK_IF(compileInfo->coreNum <= 0,
OP_LOGE(context->GetNodeName(), "FlatQuanrtTiling GetHardwareInfo Failed"),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
IMPL_OP_OPTILING(FlatQuant).Tiling(Tiling4FlatQuantTiling).TilingParse<FlatQuantCompileInfo>(TilingPrepareTiling);
}