/**
 * Copyright (c) 2026 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 fixpipe_perf.asc
 * \brief Fixpipe(L0C 搬出)性能测试示例
 *
 * 测试矩阵计算的搬出路径:把 Cube 计算结果从 L0C Buffer 搬出到 L1 Buffer 或 Unified Buffer。

 *
 * 测试场景编号说明:
 * 场景 1-9:  适用于 Atlas A3/A2 训练/推理平台(dav-2201 架构)
 * 场景 11-19:适用于 Ascend 950PR/950DT 平台(dav-3510 架构)
 *
 * 场景 1: L0C->L1  DataCopy float -> half   (随路 F322F16)
 * 场景 2: L0C->L1  DataCopy float -> int8_t (随路 QF322B8_PRE)
 *
 * 场景 11:L0C->L1  DataCopy float -> half   (随路 F322F16)
 * 场景 12:L0C->L1  DataCopy float -> int8_t (随路 QF322B8_PRE)
 * 场景 13:L0C->UB  Fixpipe  float -> float 非双目标模式(dualDstCtl=0b00,仅 dav-3510)
 * 场景 14:L0C->UB  Fixpipe  float -> float 双目标模式按 M 拆分(dualDstCtl=0b01,仅 dav-3510)
 */
#ifdef ASCENDC_CPU_DEBUG
#include "cpu_debug_launch.h"
#endif
#include "acl/acl.h"
#include "kernel_operator.h"
#include <cstdio>
#include <cstdlib>

constexpr uint32_t BLOCK_CUBE = 16;
constexpr uint32_t C0_SIZE = 16;
constexpr uint64_t ADDR_0 = 0;
// 搬出目的矩阵在 L1 上的声明地址
constexpr uint64_t DST_L1_ADDR = 128 * 1024;

// L0C -> L1 搬出性能测试:源矩阵 C 为 float(L0C),目的类型 T 为 half / int8_t(L1)。
template <class T>
class KernelFixpipeL0c2L1 {
public:
    __aicore__ inline KernelFixpipeL0c2L1(uint32_t m, uint32_t k, uint32_t n)
    {
        mSize = m;
        kSize = k;
        nSize = n;

        aSizeAlignL0 = CeilAlign(mSize, BLOCK_CUBE) * CeilAlign(kSize, C0_SIZE);
        bSizeAlignL0 = CeilAlign(kSize, C0_SIZE) * CeilAlign(nSize, BLOCK_CUBE);
        cSizeAlignL0 = CeilAlign(mSize, BLOCK_CUBE) * CeilAlign(nSize, BLOCK_CUBE);
        dstSizeAlignL1 = CeilAlign(mSize, BLOCK_CUBE) * CeilAlign(nSize, BLOCK_CUBE);
    }

    __aicore__ inline void Process()
    {
        AscendC::LocalTensor<half> a2Local(AscendC::TPosition::A2, ADDR_0, aSizeAlignL0);
        AscendC::LocalTensor<half> b2Local(AscendC::TPosition::B2, ADDR_0, bSizeAlignL0);
        AscendC::LocalTensor<float> co1Local(AscendC::TPosition::CO1, ADDR_0, cSizeAlignL0);
        AscendC::LocalTensor<T> c1Local(AscendC::TPosition::C1, DST_L1_ADDR, dstSizeAlignL1);

        AscendC::PipeBarrier<PIPE_ALL>();

        AscendC::MmadParams mmadParams;
        mmadParams.m = mSize;
        mmadParams.n = nSize;
        mmadParams.k = kSize;
        mmadParams.cmatrixInitVal = true;
        AscendC::Mmad(co1Local, a2Local, b2Local, mmadParams);

        AscendC::PipeBarrier<PIPE_ALL>();

        // 搬出:L0C -> L1,随路量化/格式转换
        AscendC::DataCopyCO12DstParams intriParams;
        intriParams.nSize = nSize;
        intriParams.mSize = mSize;
        intriParams.srcStride = CeilAlign(mSize, BLOCK_CUBE);
        intriParams.dstStride = CeilAlign(mSize, BLOCK_CUBE);
        intriParams.reluPre = 0;
        intriParams.channelSplit = false;
        intriParams.nz2ndEn = false;
        if constexpr (AscendC::IsSameType<T, half>::value) {
            // float -> half:非量化 Cast
            intriParams.quantPre = QuantMode_t::F322F16;
        } else {
            // float -> int8_t:scalar 量化,需设置量化参数
            intriParams.quantPre = QuantMode_t::QF322B8_PRE;
            AscendC::SetFixpipePreQuantFlag(1);
        }
        AscendC::DataCopy(c1Local, co1Local, intriParams);

        AscendC::PipeBarrier<PIPE_ALL>();
    }

private:
    // 向上整除
    __aicore__ inline uint32_t CeilDiv(uint32_t numerator, uint32_t denominator)
    {
        return (numerator + denominator - 1) / denominator;
    }
    // 向上对齐
    __aicore__ inline uint32_t CeilAlign(uint32_t numerator, uint32_t denominator)
    {
        return (numerator + denominator - 1) / denominator * denominator;
    }

private:
    uint32_t mSize, kSize, nSize;
    uint32_t aSizeAlignL0, bSizeAlignL0, cSizeAlignL0, dstSizeAlignL1;
};

#if defined(__NPU_ARCH__) && (__NPU_ARCH__ == 3510)
// L0C -> UB 搬出性能测试(仅 dav-3510):源矩阵 C 为 float(L0C),输出 float 到 UB(双目标模式仅支持 float)。
// scenario==13:非双目标模式(dualDstCtl=0b00);scenario==14:双目标按 M 拆分(dualDstCtl=0b01)。
// 需 __mix__ 混合编程:AIC 侧 Mmad + Fixpipe,AIV 侧将 UB 数据搬出到 GM。
constexpr AscendC::FixpipeConfig CFG_ROW_MAJOR_UB = {AscendC::CO2Layout::ROW_MAJOR, true};

class KernelFixpipeL0c2UB {
public:
    __aicore__ inline KernelFixpipeL0c2UB(uint32_t scenario, uint32_t m, uint32_t k, uint32_t n)
    {
        scenarioNum = scenario;
        mSize = m;
        kSize = k;
        nSize = n;

        aSizeAlignL0 = CeilAlign(mSize, BLOCK_CUBE) * CeilAlign(kSize, C0_SIZE);
        bSizeAlignL0 = CeilAlign(kSize, C0_SIZE) * CeilAlign(nSize, BLOCK_CUBE);
        cSizeAlignL0 = CeilAlign(mSize, BLOCK_CUBE) * CeilAlign(nSize, BLOCK_CUBE);
        cUBSize = mSize * nSize;
    }

    __aicore__ inline void Init() { cUB = AscendC::LocalTensor<float>(AscendC::TPosition::VECOUT, ADDR_0, cUBSize); }

    __aicore__ inline void Process()
    {
        if ASCEND_IS_AIC {
            AscendC::LocalTensor<half> a2Local(AscendC::TPosition::A2, ADDR_0, aSizeAlignL0);
            AscendC::LocalTensor<half> b2Local(AscendC::TPosition::B2, ADDR_0, bSizeAlignL0);
            AscendC::LocalTensor<float> co1Local(AscendC::TPosition::CO1, ADDR_0, cSizeAlignL0);

            AscendC::PipeBarrier<PIPE_ALL>();

            AscendC::MmadParams mmadParams;
            mmadParams.m = mSize;
            mmadParams.n = nSize;
            mmadParams.k = kSize;
            mmadParams.cmatrixInitVal = true;
            AscendC::Mmad(co1Local, a2Local, b2Local, mmadParams);

            AscendC::PipeBarrier<PIPE_ALL>();

            // 搬出:L0C Buffer -> UB(float -> float,双目标模式仅支持 float 输出,不支持随路转换)
            AscendC::FixpipeParamsArch3510<AscendC::CO2Layout::ROW_MAJOR> fixpipeParams;
            fixpipeParams.srcStride = CeilAlign(mSize, BLOCK_CUBE);
            if (scenarioNum == 14) {
                // 双目标模式按 M 拆分,M 须为 2 的倍数
                fixpipeParams.mSize = CeilAlign(mSize, 2);
                fixpipeParams.nSize = nSize;
                fixpipeParams.dstStride = nSize;
                fixpipeParams.dualDstCtl = 0b01;
            } else {
                // 非双目标模式
                fixpipeParams.mSize = mSize;
                fixpipeParams.nSize = nSize;
                fixpipeParams.dstStride = nSize;
                fixpipeParams.dualDstCtl = 0b00;
            }
            AscendC::Fixpipe<float, float, CFG_ROW_MAJOR_UB>(cUB, co1Local, fixpipeParams);

            AscendC::PipeBarrier<PIPE_ALL>();
        }
    }

private:
    __aicore__ inline uint32_t CeilAlign(uint32_t numerator, uint32_t denominator)
    {
        return (numerator + denominator - 1) / denominator * denominator;
    }

private:
    uint32_t scenarioNum;
    uint32_t mSize, kSize, nSize;
    uint32_t aSizeAlignL0, bSizeAlignL0, cSizeAlignL0, cUBSize;
    AscendC::LocalTensor<float> cUB;
};
#endif

__global__ __cube__ void fixpipe_perf_custom(
    __gm__ uint8_t* a, __gm__ uint8_t* b, __gm__ uint8_t* c, uint32_t scenario, uint32_t m, uint32_t k, uint32_t n)
{
    AscendC::InitSocState();
    // L0C -> L1 场景:half(场景 1/11)、int8_t(场景 2/12)
    if (scenario == 1 || scenario == 11) {
        KernelFixpipeL0c2L1<half> op(m, k, n);
        op.Process();
    } else if (scenario == 2 || scenario == 12) {
        KernelFixpipeL0c2L1<int8_t> op(m, k, n);
        op.Process();
    }
    AscendC::PipeBarrier<PIPE_ALL>();
}

#if defined(BUILD_DAV_3510)
// L0C -> UB 核函数(仅 dav-3510):1 个 AIC + 2 个 AIV。
// 场景 13:非双目标模式;场景 14:双目标按 M 拆分。
__global__ __mix__(1, 2) void fixpipe_perf_ub_custom(
    __gm__ uint8_t* c, uint32_t scenario, uint32_t m, uint32_t k, uint32_t n)
{
    AscendC::InitSocState();
    KernelFixpipeL0c2UB op(scenario, m, k, n);
    op.Init();
    op.Process();
    AscendC::PipeBarrier<PIPE_ALL>();
}
#endif

int32_t main(int32_t argc, char* argv[])
{
    if (argc != 5) {
        std::printf("Usage: %s SCENARIO_NUM M K N\n", argv[0]);
        return 1;
    }

    uint32_t scenario = static_cast<uint32_t>(std::strtoul(argv[1], nullptr, 10));
    uint32_t m = static_cast<uint32_t>(std::strtoul(argv[2], nullptr, 10));
    uint32_t k = static_cast<uint32_t>(std::strtoul(argv[3], nullptr, 10));
    uint32_t n = static_cast<uint32_t>(std::strtoul(argv[4], nullptr, 10));

    if (m == 0 || k == 0 || n == 0) {
        std::printf("M, K and N must be positive integers.\n");
        return 1;
    }

    size_t aFileSize = static_cast<size_t>(m) * k * sizeof(half);
    size_t bFileSize = static_cast<size_t>(k) * n * sizeof(half);
    size_t cFileSize = static_cast<size_t>(m) * n * sizeof(float);

    uint32_t numBlocks = 1;

    aclInit(nullptr);
    int32_t deviceId = 0;
    aclrtSetDevice(deviceId);
    aclrtStream stream = nullptr;
    aclrtCreateStream(&stream);

    uint8_t* aDevice = nullptr;
    uint8_t* bDevice = nullptr;
    uint8_t* cDevice = nullptr;
    aclrtMalloc((void**)&aDevice, aFileSize, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMalloc((void**)&bDevice, bFileSize, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMalloc((void**)&cDevice, cFileSize, ACL_MEM_MALLOC_HUGE_FIRST);

    if (scenario == 13 || scenario == 14) {
        // L0C -> UB 场景,仅 dav-3510,使用 __mix__ 核函数
#if defined(BUILD_DAV_3510)
        fixpipe_perf_ub_custom<<<numBlocks, nullptr, stream>>>(cDevice, scenario, m, k, n);
#endif
    } else {
        // L0C -> L1 场景
        fixpipe_perf_custom<<<numBlocks, nullptr, stream>>>(aDevice, bDevice, cDevice, scenario, m, k, n);
    }
    aclrtSynchronizeStream(stream);

    aclrtFree(aDevice);
    aclrtFree(bDevice);
    aclrtFree(cDevice);

    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    return 0;
}