init v0.23.0

Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
This commit is contained in:
2026-08-27 15:11:51 +08:00
parent b582a8e7d1
commit 7f8a1b1f7a
2849 changed files with 712887 additions and 22001 deletions

621
csrc/CMakeLists.txt Normal file
View File

@@ -0,0 +1,621 @@
# -----------------------------------------------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.16)
project(cann_ops-transformer)
option(BUILD_OPEN_PROJECT "Build open ascend ops project." ON)
option(BUILD_OPS_RTY_KERNEL "Build return yellow kernel." OFF)
option(ENABLE_CCACHE "Enable ccache capability" ON)
option(ENABLE_BUILT_IN "Enable built-in package" OFF)
option(ENABLE_STATIC "Enable Static" OFF)
option(ENABLE_EXPERIMENTAL "Enable experimental module" OFF)
option(ENABLE_TEST "Enable test" OFF)
option(ENABLE_UT_EXEC "Enable exec ut" OFF)
option(ENABLE_ASAN "Enable asan" OFF)
option(ENABLE_VALGRIND "Enable valgrind" OFF)
option(OP_HOST_UT "Enable ophost ut" OFF)
option(OP_API_UT "Enable opapi ut" OFF)
option(OP_GRAPH_UT "Enable graph ut" OFF)
option(OP_KERNEL_UT "Enable kernel ut" OFF)
option(OP_KERNEL_AICPU_UT "Enable aicpu kernel ut" OFF)
option(UT_TEST_ALL "Enable all ut" OFF)
option(ENABLE_OOM "Enable kernel oom" OFF)
set(ASCEND_COMPUTE_UNIT "ascend910b" CACHE STRING "soc that need to be compiled")
set(ASCEND_OP_NAME "ALL" CACHE STRING "operators that need to be compiled")
set(ARCH_DIRECTORY "" CACHE STRING "arch directory that need to be compiled")
set(VENDOR_NAME "custom" CACHE STRING "vendor name")
set(ASCEND_ALL_COMPUTE_UNIT "ascend310p;ascend910b;ascend910_93;ascend950;kirinx90" CACHE STRING "all soc list")
set(SOC_VERSION_LIST ascend310p ascend910b ascend910_93 ascend950 kirinx90)
set(ARCH_DIRECTORY_LIST arch22 arch32 arch32 arch35 arch32)
if ("ascend950" IN_LIST ASCEND_COMPUTE_UNIT)
message(STATUS "build with 3~8 packages........")
set(BUILD_WITH_3_8_PACKAGE ON CACHE BOOL "build with 3~8 package and opsbase")
endif()
foreach(SOC_VERSION ${ASCEND_COMPUTE_UNIT})
list(FIND SOC_VERSION_LIST ${SOC_VERSION} INDEX)
if(NOT INDEX EQUAL -1)
list(GET ARCH_DIRECTORY_LIST ${INDEX} VAL)
list(APPEND ARCH_DIRECTORY ${VAL})
else()
message(STATUS "unsupported chip type")
if ((NOT BUILD_OPS_RTY_KERNEL) AND (BUILD_OPEN_PROJECT))
include(cmake/build_empty_package.cmake)
cpack_empty_package()
return()
endif()
endif()
endforeach()
list(FIND ARCH_DIRECTORY "arch32" INDEX)
if(NOT INDEX EQUAL -1)
list(APPEND ARCH_DIRECTORY "arch22")
endif()
if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
message(STATUS "compile project with library")
option(BUILD_WITH_INSTALLED_DEPENDENCY_CANN_PKG "Build ops-transformer with cann pkg" ON)
else()
message(STATUS "compile project with src")
option(BUILD_WITH_INSTALLED_DEPENDENCY_CANN_PKG "Build ops-transformer with cann source" OFF)
endif()
if(UNIX)
set(SYSTEM_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-linux)
endif()
#外部传参
if(NOT ${CMAKE_BUILD_MODE} STREQUAL "FALSE")
if(ENABLE_DEBUG)
set(CMAKE_BUILD_MODE "${CMAKE_BUILD_MODE} -g")
endif()
set(COMPILE_OP_MODE ${CMAKE_BUILD_MODE})
else()
if(ENABLE_TEST)
set(COMPILE_OP_MODE "-O0 -g")
endif()
if(ENABLE_DEBUG)
set(CMAKE_BUILD_MODE "-g")
endif()
endif()
set(PKG_NAME transformer)
set(OPS_TRANSFORMER_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_STANDARD 17 CACHE STRING "c++17 is needed for this project")
set_directory_properties(PROPERTIES
ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_BINARY_DIR}/_CPack_Packages"
)
# Suppress warnings from catlass/tla third-party headers for CANN kernel compilation
set(VLLM_ASCEND_CANN_COMPAT_HEADER "${OPS_TRANSFORMER_DIR}/common/include/cann_compat.h")
list(APPEND OPS_COMPILE_OPTIONS -Wno-ignored-attributes)
list(APPEND OPS_COMPILE_OPTIONS -include${VLLM_ASCEND_CANN_COMPAT_HEADER})
add_compile_options(
$<$<COMPILE_LANGUAGE:CXX>:-include${VLLM_ASCEND_CANN_COMPAT_HEADER}>
)
include(cmake/config.cmake)
include(cmake/func.cmake)
include(cmake/third_party/json.cmake)
if (ENABLE_TEST)
include(${PROJECT_SOURCE_DIR}/cmake/third_party/gtest.cmake)
endif()
include(${OPS_ADV_CMAKE_DIR}/ut.cmake)
if (BUILD_OPEN_PROJECT)
include(cmake/intf.cmake)
add_definitions(-DBUILD_OPEN_PROJECT)
if (BUILD_OPS_RTY_KERNEL)
message(STATUS "Build return yellow kernel.")
include(cmake/rty_obj_func.cmake)
else()
message(STATUS "Start building custom package.")
include(ExternalProject)
include(cmake/dependencies.cmake)
include(cmake/variables.cmake)
include(cmake/obj_func.cmake)
include(cmake/third_party/abseil-cpp.cmake)
include(cmake/third_party/ascend_protobuf.cmake)
include(cmake/third_party/makeself-fetch.cmake)
include(cmake/opbuild.cmake)
include(cmake/custom_build.cmake)
message(STATUS "End building custom package.")
if (ENABLE_OPS_HOST)
gen_aclnn_with_opdef()
endif()
if (ENABLE_STATIC)
include(cmake/static.cmake)
endif()
if (ENABLE_AICPU)
include(cmake/symbol.cmake)
gen_cust_aicpu_json_symbol()
gen_cust_aicpu_kernel_symbol()
endif()
if (ENABLE_BUILT_IN)
message(STATUS "Start building built-in package.")
include(cmake/symbol.cmake)
gen_norm_symbol()
include(cmake/package.cmake)
pack_built_in()
else()
include(cmake/package.cmake)
pack_tiling_sink()
endif()
return()
endif()
else()
include(cmake/dependencies.cmake)
include(cmake/variables.cmake)
include(cmake/opbuild.cmake)
include(cmake/rty_obj_func.cmake)
include(cmake/intf_pub_linux.cmake)
endif()
if (BUILD_OPS_RTY_KERNEL)
set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
${CMAKE_CURRENT_LIST_DIR}/cmake/modules
)
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
${ASCEND_CANN_PACKAGE_PATH}
)
set(_op_host_aclnn_link
$<BUILD_INTERFACE:intf_pub>
exe_graph
register
c_sec
)
find_package(alog MODULE)
if(NOT ${alog_FOUND})
add_definitions(-DALOG_NOT_FOUND)
endif()
add_library(op_host_aclnn SHARED EXCLUDE_FROM_ALL)
target_link_libraries(op_host_aclnn PRIVATE
${_op_host_aclnn_link}
)
target_compile_options(op_host_aclnn PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=gnu++1z>
)
add_library(op_host_aclnnInner SHARED EXCLUDE_FROM_ALL)
target_link_libraries(op_host_aclnnInner PRIVATE
${_op_host_aclnn_link}
)
target_compile_options(op_host_aclnnInner PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=gnu++1z>
)
add_library(op_host_aclnnExc SHARED EXCLUDE_FROM_ALL)
target_link_libraries(op_host_aclnnExc PRIVATE
${_op_host_aclnn_link}
)
target_compile_options(op_host_aclnnExc PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=gnu++1z>
)
# op proto
add_library(opsproto SHARED)
target_compile_options(opsproto PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=c++11>
-fvisibility=hidden
)
target_compile_definitions(opsproto PRIVATE
LOG_CPP
PROCESS_LOG
)
target_link_libraries(opsproto PRIVATE
$<BUILD_INTERFACE:intf_pub>
$<BUILD_INTERFACE:ops_transformer_utils_proto_headers>
$<$<BOOL:${alog_FOUND}>:$<BUILD_INTERFACE:alog_headers>>
-Wl,--whole-archive
rt2_registry
-Wl,--no-whole-archive
-Wl,--no-as-needed
exe_graph
graph
graph_base
register
ascendalog
error_manager
platform
-Wl,--as-needed
c_sec
)
set_target_properties(opsproto PROPERTIES OUTPUT_NAME
cust_opsproto_rt2.0
)
install(TARGETS opsproto
LIBRARY DESTINATION packages/vendors/${VENDOR_NAME}_transformer/op_proto/lib/linux/${CMAKE_SYSTEM_PROCESSOR}
)
add_ops_tiling_keys(
OP_NAME "ALL"
TILING_KEYS ${TILING_KEY}
)
add_opc_config(
OP_NAME "ALL"
CONFIG ${OP_DEBUG_CONFIG}
)
if(ADD_OPS_COMPILE_OPTION_V2)
add_ops_compile_options(
OP_NAME "ALL"
OPTIONS ${OPS_COMPILE_OPTIONS}
)
endif()
endif ()
add_subdirectory(common)
if (NOT BUILD_OPS_RTY_KERNEL)
add_subdirectory(mc2)
add_subdirectory(posembedding)
endif()
if(NOT DEFINED COMPILED_OPS)
set(COMPILED_OPS ${COMPILED_OPS} CACHE STRING "Comp")
set(COMPILED_OPS CACHE STRING "Compiled Ops" FORCE)
set(COMPILED_OP_DIRS CACHE STRING "Compiled Ops Dirs" FORCE)
endif()
if(NOT DEFINED COMPILED_OP_DIRS)
set(COMPILED_OP_DIRS CACHE STRING "Compiled Ops Dirs" FORCE)
endif()
set(OP_LIST)
set(OP_DIR_LIST)
op_add_subdirectory(OP_LIST OP_DIR_LIST)
foreach (OP_DIR ${OP_DIR_LIST})
if (EXISTS "${OP_DIR}/op_host")
add_subdirectory(${OP_DIR}/op_host)
else()
add_subdirectory(${OP_DIR})
endif()
endforeach ()
add_subdirectory(moe)
list(APPEND OP_LIST "moe_init_routing_v2")
list(APPEND OP_LIST "moe_token_unpermute_with_ep_grad")
list(APPEND OP_DIR_LIST ${CMAKE_CURRENT_SOURCE_DIR}/moe/moe_init_routing_v2)
list(APPEND OP_DIR_LIST ${CMAKE_CURRENT_SOURCE_DIR}/moe/moe_token_unpermute_with_ep_grad)
add_subdirectory(ffn)
list(APPEND OP_LIST "ffn")
list(APPEND OP_DIR_LIST ${CMAKE_CURRENT_SOURCE_DIR}/ffn/ffn)
add_subdirectory(attention)
list(APPEND OP_LIST ${COMPILED_OPS})
list(REMOVE_DUPLICATES OP_LIST)
list(APPEND OP_DIR_LIST ${COMPILED_OP_DIRS})
list(REMOVE_DUPLICATES OP_DIR_LIST)
add_subdirectory(gmm)
list(REMOVE_DUPLICATES OP_LIST)
list(APPEND OP_DIR_LIST ${COMPILED_OP_DIRS})
list(REMOVE_DUPLICATES OP_DIR_LIST)
add_subdirectory(mc2)
list(REMOVE_DUPLICATES OP_LIST)
list(APPEND OP_DIR_LIST ${COMPILED_OP_DIRS})
list(REMOVE_DUPLICATES OP_DIR_LIST)
list(APPEND OP_LIST "fused_gdn_gating")
list(APPEND OP_DIR_LIST ${CMAKE_CURRENT_SOURCE_DIR}/attention/fused_gdn_gating)
set(OP_DEPEND_DIR_LIST)
op_add_depend_directory(
OP_LIST ${OP_LIST}
OP_DIR_LIST OP_DEPEND_DIR_LIST
)
foreach (OP_DEPEND_DIR ${OP_DEPEND_DIR_LIST})
if (EXISTS "${OP_DEPEND_DIR}/op_host")
add_subdirectory(${OP_DEPEND_DIR}/op_host)
else()
add_subdirectory(${OP_DEPEND_DIR})
endif()
endforeach ()
install(DIRECTORY ${OPS_ADV_ACT}/
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/common/act
)
install(DIRECTORY ${OPS_GROUPEDMATMUL_ACT}/
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/common/groupedmatmul_act
)
if (BUILD_OPS_RTY_KERNEL)
get_target_property(base_aclnn_srcs op_host_aclnn SOURCES)
get_target_property(base_aclnn_inner_srcs op_host_aclnnInner SOURCES)
get_target_property(base_aclnn_exclude_srcs op_host_aclnnExc SOURCES)
set(base_aclnn_binary_dir ${ASCEND_AUTOGEN_DIR})
set(generate_aclnn_srcs)
set(generate_aclnn_inner_srcs)
set(generate_aclnn_headers)
set(generate_proto_dir ${base_aclnn_binary_dir})
set(generate_exclude_proto_srcs)
set(generate_proto_srcs)
set(generate_proto_headers)
if (base_aclnn_srcs)
foreach (_src ${base_aclnn_srcs})
string(REGEX MATCH "^${CMAKE_CURRENT_SOURCE_DIR}" is_match "${_src}")
if (is_match)
get_filename_component(name_without_ext ${_src} NAME_WE)
string(REGEX REPLACE "_def$" "" _op_name ${name_without_ext})
list(APPEND generate_aclnn_srcs ${base_aclnn_binary_dir}/aclnn_${_op_name}.cpp)
list(APPEND generate_aclnn_headers ${base_aclnn_binary_dir}/aclnn_${_op_name}.h)
list(APPEND generate_proto_srcs ${generate_proto_dir}/${_op_name}_proto.cpp)
list(APPEND generate_proto_headers ${generate_proto_dir}/${_op_name}_proto.h)
endif ()
endforeach ()
else ()
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_stub.cpp
COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_stub.cpp
)
target_sources(op_host_aclnn PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_stub.cpp
)
endif ()
if (base_aclnn_inner_srcs)
foreach (_src ${base_aclnn_inner_srcs})
string(REGEX MATCH "^${CMAKE_CURRENT_SOURCE_DIR}" is_match "${_src}")
if (is_match)
get_filename_component(name_without_ext ${_src} NAME_WE)
string(REGEX REPLACE "_def$" "" _op_name ${name_without_ext})
list(APPEND generate_aclnn_inner_srcs ${base_aclnn_binary_dir}/inner/aclnnInner_${_op_name}.cpp)
list(APPEND generate_proto_srcs ${generate_proto_dir}/inner/${_op_name}_proto.cpp)
list(APPEND generate_proto_headers ${generate_proto_dir}/inner/${_op_name}_proto.h)
endif ()
endforeach ()
else ()
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_inner_stub.cpp
COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_inner_stub.cpp
)
target_sources(op_host_aclnnInner PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_inner_stub.cpp
)
endif ()
if (base_aclnn_exclude_srcs)
foreach (_src ${base_aclnn_exclude_srcs})
string(REGEX MATCH "^${CMAKE_CURRENT_SOURCE_DIR}" is_match "${_src}")
if (is_match)
get_filename_component(name_without_ext ${_src} NAME_WE)
string(REGEX REPLACE "_def$" "" _op_name ${name_without_ext})
list(APPEND generate_exclude_proto_srcs ${generate_proto_dir}/exc/${_op_name}_proto.cpp)
list(APPEND generate_proto_srcs ${generate_proto_dir}/exc/${_op_name}_proto.cpp)
list(APPEND generate_proto_headers ${generate_proto_dir}/exc/${_op_name}_proto.h)
endif ()
endforeach ()
else()
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_exc_stub.cpp
COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_exc_stub.cpp
)
target_sources(op_host_aclnnExc PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/op_host_aclnn_exc_stub.cpp
)
endif ()
if (generate_aclnn_srcs OR generate_aclnn_inner_srcs)
set(ops_aclnn_src ${generate_aclnn_srcs} ${generate_aclnn_inner_srcs})
else ()
set(ops_aclnn_src ${CMAKE_CURRENT_BINARY_DIR}/ops_aclnn_src_stub.cpp)
add_custom_command(OUTPUT ${ops_aclnn_src}
COMMAND touch ${ops_aclnn_src}
)
endif ()
set_source_files_properties(${ops_aclnn_src}
PROPERTIES GENERATED TRUE
)
add_library(ops_aclnn STATIC
${ops_aclnn_src}
)
target_compile_options(ops_aclnn PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=gnu++1z>
)
target_link_libraries(ops_aclnn PRIVATE
$<BUILD_INTERFACE:intf_pub>
)
add_dependencies(ops_aclnn opbuild_gen_default opbuild_gen_inner)
set_source_files_properties(${generate_proto_srcs}
PROPERTIES GENERATED TRUE
)
target_sources(opsproto PRIVATE
${generate_proto_srcs}
)
add_dependencies(opsproto ops_transformer_proto_headers)
install(FILES ${generate_proto_headers}
DESTINATION packages/vendors/${VENDOR_NAME}_transformer/op_proto/inc OPTIONAL
)
add_library(ops_transformer_proto_headers INTERFACE)
target_include_directories(ops_transformer_proto_headers INTERFACE
$<BUILD_INTERFACE:${generate_proto_dir}>
$<BUILD_INTERFACE:${generate_proto_dir}/inner>
$<BUILD_INTERFACE:${generate_proto_dir}/exc>
$<INSTALL_INTERFACE:include/ops_adv/proto>
)
add_dependencies(ops_transformer_proto_headers opbuild_gen_default opbuild_gen_inner opbuild_gen_exc)
if (NOT BUILD_OPEN_PROJECT)
if (generate_proto_srcs)
install_package(
PACKAGE ops_adv
TARGETS ops_proto_headers
FILES ${generate_proto_headers}
DESTINATION include/ops_adv/proto
)
endif ()
endif ()
if (generate_aclnn_srcs)
add_custom_command(OUTPUT ${generate_aclnn_srcs} ${generate_aclnn_headers}
COMMAND mkdir -p ${base_aclnn_binary_dir}
COMMAND OPS_PROTO_SEPARATE=1
OPS_ACLNN_GEN=1
OPS_PROJECT_NAME=aclnn
${OP_BUILD_TOOL}
$<TARGET_FILE:op_host_aclnn>
${base_aclnn_binary_dir}
)
endif ()
add_custom_target(opbuild_gen_default
DEPENDS ${generate_aclnn_srcs} ${generate_aclnn_headers} op_host_aclnn
)
if (generate_aclnn_inner_srcs)
add_custom_command(OUTPUT ${generate_aclnn_inner_srcs}
COMMAND mkdir -p ${base_aclnn_binary_dir}/inner
COMMAND OPS_PROTO_SEPARATE=1
OPS_ACLNN_GEN=1
OPS_PROJECT_NAME=aclnnInner
${OP_BUILD_TOOL}
$<TARGET_FILE:op_host_aclnnInner>
${base_aclnn_binary_dir}/inner
)
endif ()
add_custom_target(opbuild_gen_inner
DEPENDS ${generate_aclnn_inner_srcs} op_host_aclnnInner
)
if (generate_exclude_proto_srcs)
add_custom_command(OUTPUT ${generate_exclude_proto_srcs}
COMMAND mkdir -p ${base_aclnn_binary_dir}/exc
COMMAND OPS_PROTO_SEPARATE=1
OPS_ACLNN_GEN=0
OPS_PROJECT_NAME=aclnnExc
${OP_BUILD_TOOL}
$<TARGET_FILE:op_host_aclnnExc>
${base_aclnn_binary_dir}/exc
)
endif ()
add_custom_target(opbuild_gen_exc
DEPENDS ${generate_exclude_proto_srcs} op_host_aclnnExc
)
add_custom_target(generate_transformer_adapt_py
COMMAND ${HI_PYTHON} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/scripts/util/ascendc_impl_build.py
\"\"
\"\"
\"\"
\"\"
${ASCEND_IMPL_OUT_DIR}
${ASCEND_AUTOGEN_DIR}
--opsinfo-dir ${base_aclnn_binary_dir} ${base_aclnn_binary_dir}/inner ${base_aclnn_binary_dir}/exc
)
add_dependencies(generate_transformer_adapt_py opbuild_gen_default opbuild_gen_inner opbuild_gen_exc)
foreach (_op_name ${OP_LIST})
install(FILES ${ASCEND_IMPL_OUT_DIR}/dynamic/${_op_name}.py
DESTINATION ${IMPL_DYNAMIC_INSTALL_DIR}
OPTIONAL
)
endforeach ()
install(DIRECTORY ${OPS_ADV_UTILS_KERNEL_INC}/
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/common
)
foreach (op_dir ${OP_DIR_LIST})
get_filename_component(_op_name "${op_dir}" NAME)
if (EXISTS "${op_dir}/op_kernel")
file(GLOB KERNEL_FILES
${op_dir}/op_kernel/*.cpp
${op_dir}/op_kernel/*.h
)
else()
file(GLOB KERNEL_FILES
${op_dir}/*.cpp
${op_dir}/*.h
)
endif()
install(FILES ${KERNEL_FILES}
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/${_op_name}
OPTIONAL
)
install(DIRECTORY ${op_dir}/arch32
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/${_op_name}
OPTIONAL
)
install(DIRECTORY ${op_dir}/arch35
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/${_op_name}
OPTIONAL
)
install(DIRECTORY ${op_dir}/arch38
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/${_op_name}
OPTIONAL
)
install(DIRECTORY ${op_dir}/regbase/opkernel
DESTINATION ${IMPL_INSTALL_DIR}/ascendc/${_op_name}/regbase
OPTIONAL
)
endforeach ()
add_custom_target(prepare_build ALL)
add_custom_target(generate_compile_cmd ALL)
add_custom_target(generate_ops_info ALL)
add_dependencies(prepare_build generate_transformer_adapt_py generate_compile_cmd)
foreach (compute_unit ${ASCEND_COMPUTE_UNIT})
add_compile_cmd_target(
COMPUTE_UNIT ${compute_unit}
)
add_ops_info_target(
COMPUTE_UNIT ${compute_unit}
)
endforeach ()
add_custom_target(ops_transformer_kernel ALL)
add_custom_target(ops_transformer_config ALL)
add_dependencies(ops_transformer_kernel ops_transformer_config)
foreach (compute_unit ${ASCEND_COMPUTE_UNIT})
add_bin_compile_target(
COMPUTE_UNIT
${compute_unit}
OP_INFO
${OP_DIR_LIST}
)
endforeach ()
endif ()

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2020, Huawei Technologies Co., Ltd
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
#include "NPUBridge.h"
namespace vllm_ascend
{
NPUStorageImpl *NPUBridge::GetNpuStorageImpl(c10::StorageImpl *storageImpl)
{
return static_cast<NPUStorageImpl *>(storageImpl);
}
NPUStorageImpl *NPUBridge::GetNpuStorageImpl(c10::Storage &&storage)
{
return static_cast<NPUStorageImpl *>(storage.unsafeGetStorageImpl());
}
NPUStorageImpl *NPUBridge::GetNpuStorageImpl(const at::Tensor &tensor)
{
return static_cast<NPUStorageImpl *>(tensor.storage().unsafeGetStorageImpl());
}
NPUStorageDesc &NPUBridge::GetNpuStorageImplDesc(const at::Tensor &tensor)
{
return static_cast<NPUStorageImpl *>(tensor.storage().unsafeGetStorageImpl())->npu_desc_;
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2020, Huawei Technologies Co., Ltd
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
#pragma once
#include <c10/core/StorageImpl.h>
#include "NPUStorageImpl.h"
namespace vllm_ascend
{
class NPUBridge
{
public:
// at::tensor to NPUStorageImpl
static NPUStorageImpl *GetNpuStorageImpl(const at::Tensor &tensor);
// c10::StorageImpl to NPUStorageImpl
static NPUStorageImpl *GetNpuStorageImpl(c10::StorageImpl *storageImpl);
// c10::Storage to NPUStorageImpl
static NPUStorageImpl *GetNpuStorageImpl(c10::Storage &&storage);
// tensor to NPUStorageDesc
static NPUStorageDesc &GetNpuStorageImplDesc(const at::Tensor &tensor);
};
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2020, Huawei Technologies Co., Ltd
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
#include "NPUStorageImpl.h"
namespace vllm_ascend
{
NPUStorageImpl::NPUStorageImpl(
use_byte_size_t use_byte_size,
size_t size_bytes,
at::DataPtr data_ptr,
at::Allocator *allocator,
bool resizable) : c10::StorageImpl(use_byte_size,
size_bytes,
at::DataPtr(std::move(data_ptr)),
allocator,
resizable)
{
}
void NPUStorageImpl::release_resources()
{
StorageImpl::release_resources();
}
c10::intrusive_ptr<c10::StorageImpl> make_npu_storage_impl(
c10::StorageImpl::use_byte_size_t,
c10::SymInt size_bytes,
c10::DataPtr data_ptr,
c10::Allocator *allocator,
bool resizable)
{
if (data_ptr == nullptr)
{
data_ptr = allocator->allocate(size_bytes.as_int_unchecked());
}
// Correctly create NPUStorageImpl object.
c10::intrusive_ptr<c10::StorageImpl> npu_storage_impl = c10::make_intrusive<NPUStorageImpl>(
c10::StorageImpl::use_byte_size_t(),
size_bytes.as_int_unchecked(),
std::move(data_ptr),
allocator,
resizable);
// There is no need to consider the NPUStorageDesc information, it will be carried out in the subsequent processing.
return npu_storage_impl;
}
}

View File

@@ -0,0 +1,67 @@
// Copyright (c) 2020, Huawei Technologies Co., Ltd
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
#pragma once
#include <ATen/Tensor.h>
#include <c10/core/StorageImpl.h>
#include <c10/core/Allocator.h>
#include <c10/core/ScalarType.h>
#include <c10/util/typeid.h>
#include <c10/util/order_preserving_flat_hash_map.h>
#include "acl/acl_rt.h"
#include "acl/acl_base.h"
namespace vllm_ascend
{
struct NPUStorageDesc
{
public:
struct use_byte_size_t
{
};
c10::SmallVector<int64_t, 5> base_sizes_;
c10::SmallVector<int64_t, 5> base_strides_;
c10::SmallVector<int64_t, 5> storage_sizes_;
int64_t base_offset_ = 0;
use_byte_size_t base_dtype_ = {};
aclFormat origin_format_ = ACL_FORMAT_UNDEFINED;
aclFormat npu_format_ = ACL_FORMAT_ND;
// used to make CANN GE tensor from storagImpl
caffe2::TypeMeta data_type_ = caffe2::TypeMeta::Make<uint8_t>();
};
struct NPUStorageImpl : public c10::StorageImpl
{
explicit NPUStorageImpl(
use_byte_size_t use_byte_size,
size_t size_bytes,
at::DataPtr data_ptr,
at::Allocator *allocator,
bool resizable);
~NPUStorageImpl() override = default;
void release_resources() override;
NPUStorageDesc npu_desc_;
NPUStorageDesc get_npu_desc() const
{
return npu_desc_;
}
};
c10::intrusive_ptr<c10::StorageImpl> make_npu_storage_impl(
c10::StorageImpl::use_byte_size_t,
c10::SymInt size_bytes,
c10::DataPtr data_ptr,
c10::Allocator *allocator,
bool resizable);
}

View File

@@ -0,0 +1,754 @@
// Copyright (c) 2023 Huawei Technologies Co., Ltd
// All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef OP_API_COMMON_ADAPTER
#define OP_API_COMMON_ADAPTER
#include <fstream>
#include <torch/types.h>
#include <ATen/Tensor.h>
#include <acl/acl_base.h>
#include <c10/util/Exception.h>
#include <dlfcn.h>
#include <functional>
#include <type_traits>
#include <vector>
#include <torch_npu/csrc/framework/utils/CalcuOpUtil.h>
#include <torch_npu/csrc/framework/utils/OpAdapter.h>
#include "torch_npu/csrc/aten/NPUNativeFunctions.h"
#include "torch_npu/csrc/core/npu/NPUStream.h"
#include "torch_npu/csrc/framework/OpCommand.h"
#include "torch_npu/csrc/framework/interface/EnvVariables.h"
#include "torch_npu/csrc/framework/utils/CalcuOpUtil.h"
#include "torch_npu/csrc/framework/utils/OpPreparation.h"
#include "NPUBridge.h"
#include "NPUStorageImpl.h"
#define NPU_NAME_SPACE at_npu::native
using namespace at;
typedef struct aclOpExecutor aclOpExecutor;
typedef struct aclTensor aclTensor;
typedef struct aclScalar aclScalar;
typedef struct aclIntArray aclIntArray;
typedef struct aclFloatArray aclFloatArray;
typedef struct aclBoolArray aclBoolArray;
typedef struct aclTensorList aclTensorList;
typedef aclTensor *(*_aclCreateTensor)(
const int64_t *view_dims, uint64_t view_dims_num, aclDataType data_type,
const int64_t *stride, int64_t offset, aclFormat format,
const int64_t *storage_dims, uint64_t storage_dims_num, void *tensor_data);
typedef aclScalar *(*_aclCreateScalar)(void *value, aclDataType data_type);
typedef aclIntArray *(*_aclCreateIntArray)(const int64_t *value, uint64_t size);
typedef aclFloatArray *(*_aclCreateFloatArray)(const float *value,
uint64_t size);
typedef aclBoolArray *(*_aclCreateBoolArray)(const bool *value, uint64_t size);
typedef aclTensorList *(*_aclCreateTensorList)(const aclTensor *const *value,
uint64_t size);
typedef int (*_aclDestroyTensor)(const aclTensor *tensor);
typedef int (*_aclDestroyScalar)(const aclScalar *scalar);
typedef int (*_aclDestroyIntArray)(const aclIntArray *array);
typedef int (*_aclDestroyFloatArray)(const aclFloatArray *array);
typedef int (*_aclDestroyBoolArray)(const aclBoolArray *array);
typedef int (*_aclDestroyTensorList)(const aclTensorList *array);
constexpr int kHashBufSize = 8192;
constexpr int kHashBufMaxSize = kHashBufSize + 1024;
extern thread_local char g_hashBuf[kHashBufSize];
extern thread_local int g_hashOffset;
#ifdef MMCV_WITH_XLA
#define DEVICE_TYPE at_npu::key::NativeDeviceType
#else
#define DEVICE_TYPE c10::DeviceType::PrivateUse1
#endif
#define AT_ALL_SCALAR_TYPE_AND_ACL_DATATYPE_PAIR(_) \
_(at::ScalarType::Byte, ACL_UINT8) \
_(at::ScalarType::Char, ACL_INT8) \
_(at::ScalarType::Short, ACL_INT16) \
_(at::ScalarType::Int, ACL_INT32) \
_(at::ScalarType::Long, ACL_INT64) \
_(at::ScalarType::Half, ACL_FLOAT16) \
_(at::ScalarType::Float, ACL_FLOAT) \
_(at::ScalarType::Double, ACL_DOUBLE) \
_(at::ScalarType::ComplexHalf, ACL_COMPLEX32) \
_(at::ScalarType::ComplexFloat, ACL_COMPLEX64) \
_(at::ScalarType::ComplexDouble, ACL_COMPLEX128) \
_(at::ScalarType::Bool, ACL_BOOL) \
_(at::ScalarType::QInt8, ACL_DT_UNDEFINED) \
_(at::ScalarType::QUInt8, ACL_DT_UNDEFINED) \
_(at::ScalarType::QInt32, ACL_DT_UNDEFINED) \
_(at::ScalarType::BFloat16, ACL_BF16) \
_(at::ScalarType::QUInt4x2, ACL_DT_UNDEFINED) \
_(at::ScalarType::QUInt2x4, ACL_DT_UNDEFINED) \
_(at::ScalarType::Bits1x8, ACL_DT_UNDEFINED) \
_(at::ScalarType::Bits2x4, ACL_DT_UNDEFINED) \
_(at::ScalarType::Bits4x2, ACL_DT_UNDEFINED) \
_(at::ScalarType::Bits8, ACL_DT_UNDEFINED) \
_(at::ScalarType::Bits16, ACL_DT_UNDEFINED) \
_(at::ScalarType::Float8_e5m2, ACL_FLOAT8_E5M2) \
_(at::ScalarType::Float8_e4m3fn, ACL_FLOAT8_E4M3FN) \
_(at::ScalarType::Float8_e5m2fnuz, ACL_DT_UNDEFINED) \
_(at::ScalarType::Float8_e4m3fnuz, ACL_DT_UNDEFINED) \
_(at::ScalarType::UInt16, ACL_UINT16) \
_(at::ScalarType::UInt32, ACL_UINT32) \
_(at::ScalarType::UInt64, ACL_UINT64) \
_(at::ScalarType::UInt1, ACL_UINT1) \
_(at::ScalarType::UInt2, ACL_DT_UNDEFINED) \
_(at::ScalarType::UInt3, ACL_DT_UNDEFINED) \
_(at::ScalarType::UInt4, ACL_DT_UNDEFINED) \
_(at::ScalarType::UInt5, ACL_DT_UNDEFINED) \
_(at::ScalarType::UInt6, ACL_DT_UNDEFINED) \
_(at::ScalarType::UInt7, ACL_DT_UNDEFINED) \
_(at::ScalarType::Int1, ACL_DT_UNDEFINED) \
_(at::ScalarType::Int2, ACL_DT_UNDEFINED) \
_(at::ScalarType::Int3, ACL_DT_UNDEFINED) \
_(at::ScalarType::Int4, ACL_INT4) \
_(at::ScalarType::Int5, ACL_DT_UNDEFINED) \
_(at::ScalarType::Int6, ACL_DT_UNDEFINED) \
_(at::ScalarType::Int7, ACL_DT_UNDEFINED) \
_(at::ScalarType::Float8_e8m0fnu, ACL_FLOAT8_E8M0) \
_(at::ScalarType::Float4_e2m1fn_x2, ACL_FLOAT4_E2M1) \
_(at::ScalarType::Undefined, ACL_DT_UNDEFINED) \
_(at::ScalarType::NumOptions, ACL_DT_UNDEFINED)
constexpr aclDataType kATenScalarTypeToAclDataTypeTable
[static_cast<int64_t>(at::ScalarType::NumOptions) + 1] = {
#define DEFINE_ENUM(_1, n) n,
AT_ALL_SCALAR_TYPE_AND_ACL_DATATYPE_PAIR(DEFINE_ENUM)
#undef DEFINE_ENUM
};
#define GET_OP_API_FUNC(apiName) \
reinterpret_cast<_##apiName>(GetOpApiFuncAddr(#apiName))
#define MEMCPY_TO_BUF(data_expression, size_expression) \
if (g_hashOffset + (size_expression) > kHashBufSize) { \
g_hashOffset = kHashBufMaxSize; \
return; \
} \
memcpy(g_hashBuf + g_hashOffset, data_expression, size_expression); \
g_hashOffset += size_expression;
bool IsOpInputBaseFormat(const at::Tensor &tensor)
{
if (!tensor.is_privateuseone()) {
return true;
}
const auto format = vllm_ascend::NPUBridge::GetNpuStorageImplDesc(tensor).npu_format_;
return (format == ACL_FORMAT_ND) || (format == ACL_FORMAT_NCHW) || (format == ACL_FORMAT_NHWC) ||
(format == ACL_FORMAT_NCDHW);
}
static std::vector<std::string> split_str(std::string s, const std::string &del)
{
int end = s.find(del);
std::vector<std::string> path_list;
while (end != -1) {
path_list.push_back(s.substr(0, end));
s.erase(s.begin(), s.begin() + end + 1);
end = s.find(del);
}
path_list.push_back(s);
return path_list;
}
static bool is_file_exist(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return false;
}
return (access(path.c_str(), F_OK) == 0) ? true : false;
}
inline std::string real_path(const std::string &path)
{
if (path.empty() || path.size() > PATH_MAX) {
return "";
}
char realPath[PATH_MAX] = {0};
if (realpath(path.c_str(), realPath) == nullptr) {
return "";
}
return std::string(realPath);
}
inline std::vector<std::string> get_custom_lib_path()
{
char *ascend_custom_opppath = std::getenv("ASCEND_CUSTOM_OPP_PATH");
std::vector<std::string> custom_lib_path_list;
if (ascend_custom_opppath == nullptr) {
return std::vector<std::string>();
}
std::string ascend_custom_opppath_str(ascend_custom_opppath);
// split string with ":"
custom_lib_path_list = split_str(ascend_custom_opppath_str, ":");
if (custom_lib_path_list.empty()) {
return std::vector<std::string>();
}
for (auto &it : custom_lib_path_list) {
it = it + "/op_api/lib/";
}
return custom_lib_path_list;
}
inline std::vector<std::string> get_default_custom_lib_path()
{
char *ascend_opp_path = std::getenv("ASCEND_OPP_PATH");
std::vector<std::string> default_vendors_list;
if (ascend_opp_path == nullptr) {
return std::vector<std::string>();
}
std::string vendors_path(ascend_opp_path);
vendors_path = vendors_path + "/vendors";
std::string vendors_config_file = real_path(vendors_path + "/config.ini");
if (vendors_config_file.empty()) {
return std::vector<std::string>();
}
if (!is_file_exist(vendors_config_file)) {
return std::vector<std::string>();
}
std::ifstream ifs(vendors_config_file);
std::string line;
while (std::getline(ifs, line)) {
if (line.find("load_priority=") == 0) {
break;
}
}
std::string head = "load_priority=";
line.erase(0, head.length());
// split string with ","
default_vendors_list = split_str(line, ",");
if (default_vendors_list.empty()) {
return std::vector<std::string>();
}
for (auto &it : default_vendors_list) {
it = real_path(vendors_path + "/" + it + "/op_api/lib/");
}
return default_vendors_list;
}
const std::vector<std::string> g_custom_lib_path = get_custom_lib_path();
const std::vector<std::string> g_default_custom_lib_path = get_default_custom_lib_path();
inline const char *GetOpApiLibName(void) { return "libopapi.so"; }
inline const char *GetCustOpApiLibName(void) { return "libcust_opapi.so"; }
inline void *GetOpApiFuncAddrInLib(void *handler, const char *libName,
const char *apiName) {
auto funcAddr = dlsym(handler, apiName);
return funcAddr;
}
inline void *GetOpApiLibHandler(const char *libName) {
auto handler = dlopen(libName, RTLD_LAZY);
return handler;
}
inline void *GetOpApiFuncAddr(const char *apiName)
{
if (!g_custom_lib_path.empty()) {
for (auto &it : g_custom_lib_path) {
auto cust_opapi_lib = real_path(it + "/" + GetCustOpApiLibName());
if (cust_opapi_lib.empty()) {
continue;
}
auto custOpApiHandler = GetOpApiLibHandler(cust_opapi_lib.c_str());
if (custOpApiHandler != nullptr) {
auto funcAddr =
GetOpApiFuncAddrInLib(custOpApiHandler, GetCustOpApiLibName(), apiName);
if (funcAddr != nullptr) {
return funcAddr;
}
}
}
}
if (!g_default_custom_lib_path.empty()) {
for (auto &it : g_default_custom_lib_path) {
auto default_cust_opapi_lib = real_path(it + "/" + GetCustOpApiLibName());
if (default_cust_opapi_lib.empty()) {
continue;
}
auto custOpApiHandler = GetOpApiLibHandler(default_cust_opapi_lib.c_str());
if (custOpApiHandler != nullptr) {
auto funcAddr =
GetOpApiFuncAddrInLib(custOpApiHandler, GetCustOpApiLibName(), apiName);
if (funcAddr != nullptr) {
return funcAddr;
}
}
}
}
static auto opApiHandler = GetOpApiLibHandler(GetOpApiLibName());
if (opApiHandler == nullptr) {
return nullptr;
}
return GetOpApiFuncAddrInLib(opApiHandler, GetOpApiLibName(), apiName);
}
inline c10::Scalar ConvertTensorToScalar(const at::Tensor &tensor) {
c10::Scalar expScalar;
const at::Tensor *aclInput = &tensor;
if (aclInput->scalar_type() == at::ScalarType::Double) {
double value = *(double *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::Long) {
int64_t value = *(int64_t *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::Float) {
float value = *(float *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::Int) {
int value = *(int *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::Half) {
c10::Half value = *(c10::Half *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::Bool) {
int8_t value = *(int8_t *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::ComplexDouble) {
c10::complex<double> value = *(c10::complex<double> *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::ComplexFloat) {
c10::complex<float> value = *(c10::complex<float> *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
} else if (aclInput->scalar_type() == at::ScalarType::BFloat16) {
c10::BFloat16 value = *(c10::BFloat16 *)aclInput->data_ptr();
c10::Scalar scalar(value);
expScalar = scalar;
}
return expScalar;
}
inline at::Tensor CopyTensorHostToDevice(const at::Tensor &cpu_tensor) {
at::Tensor cpuPinMemTensor = cpu_tensor.pin_memory();
int deviceIndex = 0;
return cpuPinMemTensor.to(c10::Device(DEVICE_TYPE, deviceIndex),
cpuPinMemTensor.scalar_type(), true, true);
}
inline at::Tensor CopyScalarToDevice(const c10::Scalar &cpu_scalar,
at::ScalarType scalar_data_type) {
return CopyTensorHostToDevice(
scalar_to_tensor(cpu_scalar).to(scalar_data_type));
}
inline aclTensor *ConvertType(const at::Tensor &at_tensor) {
static const auto aclCreateTensor = GET_OP_API_FUNC(aclCreateTensor);
if (aclCreateTensor == nullptr) {
return nullptr;
}
if (!at_tensor.defined()) {
return nullptr;
}
at::ScalarType scalar_data_type = at_tensor.scalar_type();
aclDataType acl_data_type =
kATenScalarTypeToAclDataTypeTable[static_cast<int64_t>(scalar_data_type)];
TORCH_CHECK(
acl_data_type != ACL_DT_UNDEFINED,
std::string(c10::toString(scalar_data_type)) + " has not been supported")
c10::SmallVector<int64_t, 5> storageDims;
// if acl_data_type is ACL_STRING, storageDims is empty.
auto itemsize = at_tensor.itemsize();
TORCH_CHECK(itemsize != 0, "When ConvertType, tensor item size cannot be zero.");
const auto dimNum = at_tensor.sizes().size();
aclFormat format = ACL_FORMAT_ND;
if (!IsOpInputBaseFormat(at_tensor)) {
format = vllm_ascend::NPUBridge::GetNpuStorageImpl(at_tensor)->npu_desc_.npu_format_;
if (acl_data_type != ACL_STRING) {
storageDims = vllm_ascend::NPUBridge::GetNpuStorageImpl(at_tensor)->npu_desc_.storage_sizes_;
}
} else {
switch (dimNum) {
case 3:
format = ACL_FORMAT_NCL;
break;
case 4:
format = ACL_FORMAT_NCHW;
break;
case 5:
format = ACL_FORMAT_NCDHW;
break;
default:
format = ACL_FORMAT_ND;
}
if (acl_data_type != ACL_STRING) {
storageDims.push_back(at_tensor.storage().nbytes() / itemsize);
}
}
if (at_tensor.unsafeGetTensorImpl()->is_wrapped_number()) {
c10::Scalar expScalar = ConvertTensorToScalar(at_tensor);
at::Tensor aclInput = CopyScalarToDevice(expScalar, scalar_data_type);
return aclCreateTensor(aclInput.sizes().data(), aclInput.sizes().size(),
acl_data_type, aclInput.strides().data(),
aclInput.storage_offset(), format,
storageDims.data(), storageDims.size(),
const_cast<void *>(aclInput.storage().data()));
}
auto acl_tensor = aclCreateTensor(
at_tensor.sizes().data(), at_tensor.sizes().size(), acl_data_type,
at_tensor.strides().data(), at_tensor.storage_offset(), format,
storageDims.data(), storageDims.size(),
const_cast<void *>(at_tensor.storage().data()));
return acl_tensor;
}
inline aclScalar *ConvertType(const at::Scalar &at_scalar) {
static const auto aclCreateScalar = GET_OP_API_FUNC(aclCreateScalar);
if (aclCreateScalar == nullptr) {
return nullptr;
}
at::ScalarType scalar_data_type = at_scalar.type();
aclDataType acl_data_type =
kATenScalarTypeToAclDataTypeTable[static_cast<int64_t>(scalar_data_type)];
TORCH_CHECK(
acl_data_type != ACL_DT_UNDEFINED,
std::string(c10::toString(scalar_data_type)) + " has not been supported")
aclScalar *acl_scalar = nullptr;
switch (scalar_data_type) {
case at::ScalarType::Double: {
double value = at_scalar.toDouble();
acl_scalar = aclCreateScalar(&value, acl_data_type);
break;
}
case at::ScalarType::Long: {
int64_t value = at_scalar.toLong();
acl_scalar = aclCreateScalar(&value, acl_data_type);
break;
}
case at::ScalarType::Bool: {
bool value = at_scalar.toBool();
acl_scalar = aclCreateScalar(&value, acl_data_type);
break;
}
case at::ScalarType::ComplexDouble: {
auto value = at_scalar.toComplexDouble();
acl_scalar = aclCreateScalar(&value, acl_data_type);
break;
}
default:
acl_scalar = nullptr;
break;
}
return acl_scalar;
}
inline aclIntArray *ConvertType(const at::IntArrayRef &at_array) {
static const auto aclCreateIntArray = GET_OP_API_FUNC(aclCreateIntArray);
if (aclCreateIntArray == nullptr) {
return nullptr;
}
auto array = aclCreateIntArray(at_array.data(), at_array.size());
return array;
}
template <std::size_t N>
inline aclBoolArray *ConvertType(const std::array<bool, N> &value) {
static const auto aclCreateBoolArray = GET_OP_API_FUNC(aclCreateBoolArray);
if (aclCreateBoolArray == nullptr) {
return nullptr;
}
auto array = aclCreateBoolArray(value.data(), value.size());
return array;
}
inline aclBoolArray *ConvertType(const at::ArrayRef<bool> &value) {
static const auto aclCreateBoolArray = GET_OP_API_FUNC(aclCreateBoolArray);
if (aclCreateBoolArray == nullptr) {
return nullptr;
}
auto array = aclCreateBoolArray(value.data(), value.size());
return array;
}
inline aclTensorList *ConvertType(const at::TensorList &at_tensor_list) {
static const auto aclCreateTensorList = GET_OP_API_FUNC(aclCreateTensorList);
if (aclCreateTensorList == nullptr) {
return nullptr;
}
std::vector<const aclTensor *> tensor_list(at_tensor_list.size());
for (size_t i = 0; i < at_tensor_list.size(); i++) {
tensor_list[i] = ConvertType(at_tensor_list[i]);
}
auto acl_tensor_list =
aclCreateTensorList(tensor_list.data(), tensor_list.size());
return acl_tensor_list;
}
inline aclTensor *ConvertType(const c10::optional<at::Tensor> &opt_tensor) {
if (opt_tensor.has_value() && opt_tensor.value().defined()) {
return ConvertType(opt_tensor.value());
}
return nullptr;
}
inline aclTensorList *ConvertType(
const c10::optional<at::TensorList> &opt_tensor_list) {
if (opt_tensor_list.has_value()) {
return ConvertType(opt_tensor_list.value());
}
return nullptr;
}
inline aclIntArray *ConvertType(
const c10::optional<at::IntArrayRef> &opt_array) {
if (opt_array.has_value()) {
return ConvertType(opt_array.value());
}
return nullptr;
}
inline aclScalar *ConvertType(const c10::optional<at::Scalar> &opt_scalar) {
if (opt_scalar.has_value()) {
return ConvertType(opt_scalar.value());
}
return nullptr;
}
inline aclDataType ConvertType(const at::ScalarType scalarType) {
return kATenScalarTypeToAclDataTypeTable[static_cast<int64_t>(scalarType)];
}
template <typename T>
T ConvertType(T value) {
return value;
}
template <typename Tuple, size_t... I>
auto ConvertToOpApiFunc(const Tuple &params, void *opApiAddr,
std::index_sequence<I...>) {
typedef int (*OpApiFunc)(
typename std::decay<decltype(std::get<I>(params))>::type...);
auto func = reinterpret_cast<OpApiFunc>(opApiAddr);
return func;
}
template <typename Tuple>
auto ConvertToOpApiFunc(const Tuple &params, void *opApiAddr) {
static constexpr auto size = std::tuple_size<Tuple>::value;
return ConvertToOpApiFunc(params, opApiAddr,
std::make_index_sequence<size>{});
}
inline void Release(aclTensor *p) {
static const auto aclDestroyTensor = GET_OP_API_FUNC(aclDestroyTensor);
if (aclDestroyTensor == nullptr) {
return;
}
aclDestroyTensor(p);
}
inline void Release(aclScalar *p) {
static const auto aclDestroyScalar = GET_OP_API_FUNC(aclDestroyScalar);
if (aclDestroyScalar == nullptr) {
return;
}
aclDestroyScalar(p);
}
inline void Release(aclIntArray *p) {
static const auto aclDestroyIntArray = GET_OP_API_FUNC(aclDestroyIntArray);
if (aclDestroyIntArray == nullptr) {
return;
}
aclDestroyIntArray(p);
}
inline void Release(aclBoolArray *p) {
static const auto aclDestroyBoolArray = GET_OP_API_FUNC(aclDestroyBoolArray);
if (aclDestroyBoolArray == nullptr) {
return;
}
aclDestroyBoolArray(p);
}
inline void Release(aclTensorList *p) {
static const auto aclDestroyTensorList =
GET_OP_API_FUNC(aclDestroyTensorList);
if (aclDestroyTensorList == nullptr) {
return;
}
aclDestroyTensorList(p);
}
template <typename T>
void Release(T value) {
(void)value;
}
template <typename Tuple, size_t... I>
void CallRelease(Tuple t, std::index_sequence<I...>) {
(void)std::initializer_list<int>{(Release(std::get<I>(t)), 0)...};
}
template <typename Tuple>
void ReleaseConvertTypes(Tuple &t) {
static constexpr auto size = std::tuple_size<Tuple>::value;
CallRelease(t, std::make_index_sequence<size>{});
}
template <typename... Ts>
constexpr auto ConvertTypes(Ts &... args) {
return std::make_tuple(ConvertType(args)...);
}
template <typename Function, typename Tuple, size_t... I>
auto call(Function f, Tuple t, std::index_sequence<I...>) {
return f(std::get<I>(t)...);
}
template <typename Function, typename Tuple>
auto call(Function f, Tuple t) {
static constexpr auto size = std::tuple_size<Tuple>::value;
return call(f, t, std::make_index_sequence<size>{});
}
template <std::size_t N>
void AddParamToBuf(const std::array<bool, N> &value) {
MEMCPY_TO_BUF(value.data(), value.size() * sizeof(bool));
}
template <typename T>
void AddParamToBuf(const T &value) {
MEMCPY_TO_BUF(&value, sizeof(T));
}
void AddParamToBuf(const at::Tensor &);
void AddParamToBuf(const at::Scalar &);
void AddParamToBuf(const at::IntArrayRef &);
void AddParamToBuf(const at::ArrayRef<bool> &);
void AddParamToBuf(const at::TensorList &);
void AddParamToBuf(const c10::optional<at::Tensor> &);
void AddParamToBuf(const c10::optional<at::IntArrayRef> &);
void AddParamToBuf(const c10::optional<at::Scalar> &);
void AddParamToBuf(const at::ScalarType);
void AddParamToBuf(const string &);
void AddParamToBuf();
template <typename T, typename... Args>
void AddParamToBuf(const T &arg, Args &... args) {
AddParamToBuf(arg);
AddParamToBuf(args...);
}
uint64_t CalcHashId();
typedef int (*InitHugeMemThreadLocal)(void *, bool);
typedef void (*UnInitHugeMemThreadLocal)(void *, bool);
typedef void (*ReleaseHugeMem)(void *, bool);
#define EXEC_NPU_CMD(aclnn_api, ...) \
do { \
static const auto getWorkspaceSizeFuncAddr = \
GetOpApiFuncAddr(#aclnn_api "GetWorkspaceSize"); \
static const auto opApiFuncAddr = GetOpApiFuncAddr(#aclnn_api); \
static const auto initMemAddr = \
GetOpApiFuncAddr("InitHugeMemThreadLocal"); \
static const auto unInitMemAddr = \
GetOpApiFuncAddr("UnInitHugeMemThreadLocal"); \
static const auto releaseMemAddr = GetOpApiFuncAddr("ReleaseHugeMem"); \
TORCH_CHECK( \
getWorkspaceSizeFuncAddr != nullptr && opApiFuncAddr != nullptr, \
#aclnn_api, " or ", #aclnn_api "GetWorkspaceSize", " not in ", \
GetOpApiLibName(), ", or ", GetOpApiLibName(), "not found."); \
auto acl_stream = c10_npu::getCurrentNPUStream().stream(false); \
uint64_t workspace_size = 0; \
uint64_t *workspace_size_addr = &workspace_size; \
aclOpExecutor *executor = nullptr; \
aclOpExecutor **executor_addr = &executor; \
InitHugeMemThreadLocal initMemFunc = \
reinterpret_cast<InitHugeMemThreadLocal>(initMemAddr); \
UnInitHugeMemThreadLocal unInitMemFunc = \
reinterpret_cast<UnInitHugeMemThreadLocal>(unInitMemAddr); \
if (initMemFunc) { \
initMemFunc(nullptr, false); \
} \
auto converted_params = \
ConvertTypes(__VA_ARGS__, workspace_size_addr, executor_addr); \
static auto getWorkspaceSizeFunc = \
ConvertToOpApiFunc(converted_params, getWorkspaceSizeFuncAddr); \
auto workspace_status = call(getWorkspaceSizeFunc, converted_params); \
TORCH_CHECK(workspace_status == 0, \
"call " #aclnn_api " failed, detail:", aclGetRecentErrMsg()); \
void *workspace_addr = nullptr; \
if (workspace_size != 0) { \
at::TensorOptions options = \
at::TensorOptions(torch_npu::utils::get_npu_device_type()); \
auto workspace_tensor = \
at::empty({workspace_size}, options.dtype(kByte)); \
workspace_addr = const_cast<void *>(workspace_tensor.storage().data()); \
} \
auto acl_call = [converted_params, workspace_addr, workspace_size, \
acl_stream, executor]() -> int { \
typedef int (*OpApiFunc)(void *, uint64_t, aclOpExecutor *, \
const aclrtStream); \
OpApiFunc opApiFunc = reinterpret_cast<OpApiFunc>(opApiFuncAddr); \
auto api_ret = \
opApiFunc(workspace_addr, workspace_size, executor, acl_stream); \
TORCH_CHECK(api_ret == 0, "call " #aclnn_api " failed, detail:", \
aclGetRecentErrMsg()); \
ReleaseConvertTypes(converted_params); \
ReleaseHugeMem releaseMemFunc = \
reinterpret_cast<ReleaseHugeMem>(releaseMemAddr); \
if (releaseMemFunc) { \
releaseMemFunc(nullptr, false); \
} \
return api_ret; \
}; \
at_npu::native::OpCommand cmd; \
cmd.Name(#aclnn_api); \
cmd.SetCustomHandler(acl_call); \
cmd.Run(); \
if (unInitMemFunc) { \
unInitMemFunc(nullptr, false); \
} \
} while (false)
#endif

View File

@@ -0,0 +1,30 @@
# -----------------------------------------------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------------------------------------------
set(OPTEST_NAME optest_${PKG_NAME})
file(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
foreach(SUB_DIR ${CURRENT_DIRS})
if (DEFINED ASCEND_OP_NAME AND NOT "${ASCEND_OP_NAME}" STREQUAL "")
if (NOT "${ASCEND_OP_NAME}" STREQUAL "all" AND NOT "${ASCEND_OP_NAME}" STREQUAL "ALL")
if (NOT ${SUB_DIR} IN_LIST ASCEND_OP_NAME)
continue()
endif ()
endif ()
endif ()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
add_subdirectory(${SUB_DIR})
else()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/op_host/CMakeLists.txt")
add_subdirectory(${SUB_DIR}/op_host)
endif()
endif()
endforeach()

View File

@@ -0,0 +1,514 @@
/**
 * 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 CopyInL1.h
* \brief
*/
#ifndef COPYINL1_H
#define COPYINL1_H
enum class KVLAYOUT
{
BNBD, // [blockNums, headNum, blockSize, headDim]
BBH, // [blockNums, blockSize, headNum * headDim]
NZ // [blockNums, headNum, d1, blockSize, d0], d1 = headDim / d0, d0 = 32 (block byte) / sizeof(KV_T)
};
struct CopyParam{
uint32_t width;
uint32_t height;
uint32_t orgWidth;
};
struct PAShape{
uint32_t blockNum;
uint32_t blockSize;
uint32_t headNum; // 一般为kv的head num
uint32_t headDim; // mla下rope为64 非rope为512
uint32_t maxblockNumPerBatch; // block table 每一行的最大个数
uint32_t actHeadDim; // 实际拷贝col大小考虑到N切块 s*d 对应d
uint32_t copyRowNum;
uint32_t copyRowNumAlign;
uint32_t pageStride;
};
struct Position{
uint32_t bIdx;
uint32_t n2Idx;
uint32_t s2Offset;
uint32_t dIdx; // N轴被切对应D轴被切
};
template<typename L1Type>
__aicore__ inline void GmCopyInToL1(LocalTensor<L1Type>& L1Tensor, GlobalTensor<L1Type>& GmTensor, const CopyParam& mmCopyParam)
{
Nd2NzParams Gm2L1Nd2NzParams;
Gm2L1Nd2NzParams.ndNum = 1; // ND矩阵的个数
Gm2L1Nd2NzParams.nValue = mmCopyParam.height; // 单个ND矩阵的实际行数单位为元素个数
Gm2L1Nd2NzParams.dValue = mmCopyParam.width; // 单个ND矩阵的实际列数(vD),单位为元素个数
Gm2L1Nd2NzParams.srcNdMatrixStride = 0; // 相邻ND矩阵起始地址之间的偏移 单位为元素个数
Gm2L1Nd2NzParams.srcDValue = mmCopyParam.orgWidth; // 同一个ND矩阵中相邻行起始地址之间的偏移 单位为元素个数
Gm2L1Nd2NzParams.dstNzC0Stride = (Gm2L1Nd2NzParams.nValue + 15) >> 4 << 4; // 转换为NZ矩阵后相邻Block起始地址之间的偏移 单位为Block个数
Gm2L1Nd2NzParams.dstNzNStride = 1; // 转换为NZ矩阵后ND之间相邻两行在NZ矩阵中起始地址之间的偏移 单位为Block个数
Gm2L1Nd2NzParams.dstNzMatrixStride = 0; // 两个NZ矩阵起始地址之间的偏移 单位为元素数量
DataCopy(L1Tensor, GmTensor, Gm2L1Nd2NzParams);
}
// 场景key、value GM to L1
// GM按ND格式存储
// L1按NZ格式存储
// GM的行、列、列的strideD or NDBNSD 和 BSH的区别
template<typename L1Type>
__aicore__ inline void DataCopyGmNDToL1(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
uint32_t rowAct,
uint32_t rowAlign,
uint32_t col, // D
uint32_t colStride) // D or N*D
{
Nd2NzParams nd2nzPara;
nd2nzPara.ndNum = 1;
nd2nzPara.nValue = rowAct; // 行数
nd2nzPara.dValue = col;
nd2nzPara.srcDValue = colStride;
nd2nzPara.dstNzC0Stride = rowAlign;
nd2nzPara.dstNzNStride = 1;
nd2nzPara.srcNdMatrixStride = 0;
nd2nzPara.dstNzMatrixStride = 0;
DataCopy(l1Tensor, gmTensor, nd2nzPara);
}
template<typename L1Type>
__aicore__ inline void DataCopyGmScaleNDToL1(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
uint32_t rowAct,
uint32_t rowAlign,
uint32_t col, // D
uint32_t colStride) // D or N*D
{
Nd2NzParams nd2nzPara;
nd2nzPara.ndNum = 1;
nd2nzPara.nValue = rowAct;
nd2nzPara.dValue = col;
nd2nzPara.srcDValue = colStride;
nd2nzPara.dstNzC0Stride = rowAlign;
nd2nzPara.dstNzNStride = 1;
nd2nzPara.srcNdMatrixStride = 0;
nd2nzPara.dstNzMatrixStride = nd2nzPara.nValue;
LocalTensor<bfloat16_t> l1TensorCast = l1Tensor.template ReinterpretCast<bfloat16_t>();
GlobalTensor<bfloat16_t> gmTensorCast;
gmTensorCast.SetGlobalBuffer(((__gm__ bfloat16_t*)(gmTensor.GetPhyAddr())));
DataCopy(l1TensorCast, gmTensorCast, nd2nzPara);
}
template<typename L1Type>
__aicore__ inline void DataCopyGmScaleDNToL1(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
uint32_t rowAct,
uint32_t rowAlign,
uint32_t col,
uint32_t colStride)
{
Dn2NzParams dn2nzPara;
dn2nzPara.dnNum = 1;
dn2nzPara.nValue = col / 2;
dn2nzPara.dValue = rowAct;
dn2nzPara.srcDValue = colStride / 2;
dn2nzPara.dstNzC0Stride = dn2nzPara.nValue;
dn2nzPara.dstNzNStride = 1;
dn2nzPara.srcDnMatrixStride = 0;
dn2nzPara.dstNzMatrixStride = dn2nzPara.nValue;
LocalTensor<bfloat16_t> l1TensorCast = l1Tensor.template ReinterpretCast<bfloat16_t>();
GlobalTensor<bfloat16_t> gmTensorCast;
gmTensorCast.SetGlobalBuffer(((__gm__ bfloat16_t*)(gmTensor.GetPhyAddr())));
DataCopy(l1TensorCast, gmTensorCast, dn2nzPara);
}
template<typename L1Type>
__aicore__ inline void DataCopyGmNZToL1(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
uint32_t rowAct,
uint32_t dstRowStride,
uint32_t srcRowStride,
uint32_t col)
{
uint32_t blockElementCnt = 32U / sizeof(L1Type);
if constexpr (IsSameType<L1Type, int4b_t>::value) {
blockElementCnt = 64U;
}
DataCopyParams intriParams;
intriParams.blockCount = col / blockElementCnt;
intriParams.blockLen = rowAct;
intriParams.dstStride = dstRowStride;
intriParams.srcStride = srcRowStride;
DataCopy(l1Tensor, gmTensor, intriParams);
}
template<typename L1Type>
__aicore__ inline void GmCopyInToL1HasRopePANoContinue(LocalTensor<L1Type>& nopeTensor, LocalTensor<L1Type>& ropeTensor,
GlobalTensor<L1Type>& nopeGmTensor, GlobalTensor<L1Type>& ropeGmTensor,
GlobalTensor<int32_t>& blockTableGm, KVLAYOUT kvLayout,
const PAShape &shape,
const PAShape &ropeShape,
const Position &startPos)
{
uint32_t copyFinishRowCnt = 0;
uint64_t blockTableBaseOffset = startPos.bIdx * shape.maxblockNumPerBatch; // 块表的基偏移量
uint32_t curS2Idx = startPos.s2Offset;
uint32_t blockElementCnt = 32U / sizeof(L1Type); // 每个块的元素数量
// ropeshape的M方向与nopeshape保持一样 此处只判断nopeshape的
while(copyFinishRowCnt < shape.copyRowNum){
uint64_t blockIdOffset = curS2Idx / shape.blockSize; // 获取block table上的索引
uint64_t remainRowCnt = curS2Idx % shape.blockSize; // 获取在单个块上超出的行数
uint64_t idInBlockTable = blockTableGm.GetValue(blockTableBaseOffset + blockIdOffset); // 从block table上获取的编号
//计算可以拷贝行数
uint32_t copyRowCnt = shape.blockSize - remainRowCnt; // 一次只能处理一个Block
if (copyFinishRowCnt + copyRowCnt > shape.copyRowNum){
copyRowCnt = shape.copyRowNum - copyFinishRowCnt; // 一个block未拷满
}
uint64_t offset = idInBlockTable * shape.blockSize * shape.headNum * shape.headDim; // PA的偏移
if (shape.pageStride > 0) {
offset = idInBlockTable * shape.pageStride;
}
uint64_t keyRopeOffset = idInBlockTable * ropeShape.blockSize * ropeShape.headNum * ropeShape.headDim;
if (ropeShape.pageStride > 0) {
keyRopeOffset = idInBlockTable * ropeShape.pageStride;
}
if (kvLayout == KVLAYOUT::NZ) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.blockSize * shape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * shape.blockSize;
keyRopeOffset += static_cast<uint64_t>(startPos.n2Idx * ropeShape.blockSize * ropeShape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * ropeShape.blockSize;
LocalTensor<L1Type> tmpNopeDstTensor = nopeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = nopeGmTensor[offset];
DataCopyGmNZToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, (shape.copyRowNumAlign - copyRowCnt), (shape.blockSize - copyRowCnt), shape.actHeadDim);
LocalTensor<L1Type> tmpRopeDstTensor = ropeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpRopeSrcTensor = ropeGmTensor[keyRopeOffset];
DataCopyGmNZToL1(tmpRopeDstTensor, tmpRopeSrcTensor, copyRowCnt, (ropeShape.copyRowNumAlign - copyRowCnt), (ropeShape.blockSize - copyRowCnt), ropeShape.actHeadDim);
} else {
uint64_t dStride = shape.headDim;
uint64_t dRopeStride = ropeShape.headDim;
if (kvLayout == KVLAYOUT::BBH) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim) + remainRowCnt * shape.headDim * shape.headNum + startPos.dIdx;
keyRopeOffset += static_cast<uint64_t>(startPos.n2Idx * ropeShape.headDim) + remainRowCnt * ropeShape.headDim * ropeShape.headNum;
dStride = shape.headDim * shape.headNum;
dRopeStride = ropeShape.headDim * ropeShape.headNum;
} else{
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim * shape.blockSize) + remainRowCnt * shape.headDim + startPos.dIdx;
keyRopeOffset += static_cast<uint64_t>(startPos.n2Idx * ropeShape.headDim * ropeShape.blockSize) + remainRowCnt * ropeShape.headDim;
}
uint32_t dValue = shape.actHeadDim;
uint32_t srcDValue = dStride;
uint32_t dRopeValue = ropeShape.actHeadDim;
uint32_t srcRopeDValue = dRopeStride;
LocalTensor<L1Type> tmpNopeDstTensor = nopeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = nopeGmTensor[offset];
DataCopyGmNDToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, shape.copyRowNumAlign, dValue, srcDValue);
LocalTensor<L1Type> tmpRopeDstTensor = ropeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpRopeSrcTensor = ropeGmTensor[keyRopeOffset];
DataCopyGmNDToL1(tmpRopeDstTensor, tmpRopeSrcTensor, copyRowCnt, shape.copyRowNumAlign, dRopeValue, srcRopeDValue);
}
copyFinishRowCnt += copyRowCnt;
curS2Idx += copyRowCnt;
}
}
template<typename L1Type>
__aicore__ inline void GmCopyInToL1HasRopePA(LocalTensor<L1Type>& nopeTensor, LocalTensor<L1Type>& ropeTensor,
GlobalTensor<L1Type>& nopeGmTensor, GlobalTensor<L1Type>& ropeGmTensor,
GlobalTensor<int32_t>& blockTableGm, KVLAYOUT kvLayout,
const PAShape &shape,
const PAShape &ropeShape,
const Position &startPos)
{
uint32_t copyFinishRowCnt = 0;
uint64_t blockTableBaseOffset = startPos.bIdx * shape.maxblockNumPerBatch; // 块表的基偏移量
uint32_t curS2Idx = startPos.s2Offset;
uint32_t blockElementCnt = 32U / sizeof(L1Type); // 每个块的元素数量
// ropeshape的M方向与nopeshape保持一样 此处只判断nopeshape的
while(copyFinishRowCnt < shape.copyRowNum){
uint64_t blockIdOffset = curS2Idx / shape.blockSize; // 获取block table上的索引
uint64_t remainRowCnt = curS2Idx % shape.blockSize; // 获取在单个块上超出的行数
uint64_t idInBlockTable = blockTableGm.GetValue(blockTableBaseOffset + blockIdOffset); // 从block table上获取的编号
//计算可以拷贝行数
uint32_t copyRowCnt = shape.blockSize - remainRowCnt; // 一次只能处理一个Block
if (copyFinishRowCnt + copyRowCnt > shape.copyRowNum){
copyRowCnt = shape.copyRowNum - copyFinishRowCnt; // 一个block未拷满
}
uint64_t offset = idInBlockTable * shape.blockSize * shape.headNum * shape.headDim; // PA的偏移
uint64_t keyRopeOffset = idInBlockTable * ropeShape.blockSize * ropeShape.headNum * ropeShape.headDim;
if (kvLayout == KVLAYOUT::NZ) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.blockSize * shape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * shape.blockSize;
keyRopeOffset += static_cast<uint64_t>(startPos.n2Idx * ropeShape.blockSize * ropeShape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * ropeShape.blockSize;
LocalTensor<L1Type> tmpNopeDstTensor = nopeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = nopeGmTensor[offset];
DataCopyGmNZToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, (shape.copyRowNumAlign - copyRowCnt), (shape.blockSize - copyRowCnt), shape.actHeadDim);
LocalTensor<L1Type> tmpRopeDstTensor = ropeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpRopeSrcTensor = ropeGmTensor[keyRopeOffset];
DataCopyGmNZToL1(tmpRopeDstTensor, tmpRopeSrcTensor, copyRowCnt, (ropeShape.copyRowNumAlign - copyRowCnt), (ropeShape.blockSize - copyRowCnt), ropeShape.actHeadDim);
} else {
uint64_t dStride = shape.headDim;
uint64_t dRopeStride = ropeShape.headDim;
if (kvLayout == KVLAYOUT::BBH) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim) + remainRowCnt * shape.headDim * shape.headNum + startPos.dIdx;
keyRopeOffset += static_cast<uint64_t>(startPos.n2Idx * ropeShape.headDim) + remainRowCnt * ropeShape.headDim * ropeShape.headNum;
dStride = shape.headDim * shape.headNum;
dRopeStride = ropeShape.headDim * ropeShape.headNum;
} else{
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim * shape.blockSize) + remainRowCnt * shape.headDim + startPos.dIdx;
keyRopeOffset += static_cast<uint64_t>(startPos.n2Idx * ropeShape.headDim * ropeShape.blockSize) + remainRowCnt * ropeShape.headDim;
}
uint32_t dValue = shape.actHeadDim;
uint32_t srcDValue = dStride;
uint32_t dRopeValue = ropeShape.actHeadDim;
uint32_t srcRopeDValue = dRopeStride;
LocalTensor<L1Type> tmpNopeDstTensor = nopeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = nopeGmTensor[offset];
DataCopyGmNDToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, shape.copyRowNumAlign, dValue, srcDValue);
LocalTensor<L1Type> tmpRopeDstTensor = ropeTensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpRopeSrcTensor = ropeGmTensor[keyRopeOffset];
DataCopyGmNDToL1(tmpRopeDstTensor, tmpRopeSrcTensor, copyRowCnt, shape.copyRowNumAlign, dRopeValue, srcRopeDValue);
}
copyFinishRowCnt += copyRowCnt;
curS2Idx += copyRowCnt;
}
}
template<typename L1Type>
__aicore__ inline void GmCopyInToL1PA(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
GlobalTensor<int32_t>& blockTableGm, KVLAYOUT kvLayout,
const PAShape &shape, const Position &startPos)
{
uint32_t copyFinishRowCnt = 0;
uint64_t blockTableBaseOffset = startPos.bIdx * shape.maxblockNumPerBatch; // 块表的基偏移量
uint32_t curS2Idx = startPos.s2Offset;
uint32_t blockElementCnt = 32U / sizeof(L1Type); // 每个块的元素数量
while(copyFinishRowCnt < shape.copyRowNum){
uint64_t blockIdOffset = curS2Idx / shape.blockSize; // 获取block table上的索引
uint64_t remainRowCnt = curS2Idx % shape.blockSize; // 获取在单个块上超出的行数
uint64_t idInBlockTable = blockTableGm.GetValue(blockTableBaseOffset + blockIdOffset); // 从block table上获取的编号
//计算可以拷贝行数
uint32_t copyRowCnt = shape.blockSize - remainRowCnt; // 一次只能处理一个Block
if (copyFinishRowCnt + copyRowCnt > shape.copyRowNum){
copyRowCnt = shape.copyRowNum - copyFinishRowCnt; // 一个block未拷满
}
uint64_t offset = idInBlockTable * shape.blockSize * shape.headNum * shape.headDim; // PA的偏移
if (kvLayout == KVLAYOUT::NZ) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.blockSize * shape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * shape.blockSize;
LocalTensor<L1Type> tmpNopeDstTensor = l1Tensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = gmTensor[offset];
DataCopyGmNZToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, (shape.copyRowNumAlign - copyRowCnt), (shape.blockSize - copyRowCnt), shape.actHeadDim);
} else {
uint64_t dStride = shape.headDim;
if (kvLayout == KVLAYOUT::BBH) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim) + remainRowCnt * shape.headDim * shape.headNum + startPos.dIdx;
dStride = shape.headDim * shape.headNum;
} else {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim * shape.blockSize) + remainRowCnt * shape.headDim + startPos.dIdx;
}
uint32_t dValue = shape.actHeadDim;
uint32_t srcDValue = dStride;
LocalTensor<L1Type> tmpNopeDstTensor = l1Tensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = gmTensor[offset];
DataCopyGmNDToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, shape.copyRowNumAlign, dValue, srcDValue);
}
copyFinishRowCnt += copyRowCnt;
curS2Idx += copyRowCnt;
}
}
template<typename L1Type>
__aicore__ inline void GmScaleCopyInToL1PAForND(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
GlobalTensor<int32_t>& blockTableGm, KVLAYOUT kvLayout,
const PAShape &shape, const Position &startPos)
{
uint32_t copyFinishRowCnt = 0;
uint64_t blockTableBaseOffset = startPos.bIdx * shape.maxblockNumPerBatch;
uint32_t curS2Idx = startPos.s2Offset;
constexpr uint32_t blockElementCnt = 32U / sizeof(L1Type);
while(copyFinishRowCnt < shape.copyRowNum) {
uint64_t blockIdOffset = curS2Idx / shape.blockSize;
uint64_t remainRowCnt = curS2Idx % shape.blockSize;
uint64_t idInBlockTable = blockTableGm.GetValue(blockTableBaseOffset + blockIdOffset);
uint32_t copyRowCnt = shape.blockSize - remainRowCnt;
if (copyFinishRowCnt + copyRowCnt > shape.copyRowNum) {
copyRowCnt = shape.copyRowNum - copyFinishRowCnt;
}
uint64_t offset = idInBlockTable * shape.blockSize * shape.headNum * shape.headDim;
if (kvLayout == KVLAYOUT::NZ) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.blockSize * shape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * shape.blockSize;
LocalTensor<L1Type> tmpNopeDstTensor = l1Tensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = gmTensor[offset];
DataCopyGmNZToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, (shape.copyRowNumAlign - copyRowCnt), (shape.blockSize - copyRowCnt), shape.actHeadDim);
} else {
uint64_t dStride = shape.headDim;
if (kvLayout == KVLAYOUT::BBH) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim) + remainRowCnt * shape.headDim * shape.headNum + startPos.dIdx;
dStride = shape.headDim * shape.headNum;
} else {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim * shape.blockSize) + remainRowCnt * shape.headDim + startPos.dIdx;
}
uint32_t dValue = shape.actHeadDim;
uint32_t srcDValue = dStride;
LocalTensor<L1Type> tmpNopeDstTensor = l1Tensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = gmTensor[offset * 2];
DataCopyGmScaleNDToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, copyRowCnt, dValue, srcDValue);
}
copyFinishRowCnt += copyRowCnt;
curS2Idx += copyRowCnt;
}
}
template<typename L1Type>
__aicore__ inline void GmScaleCopyInToL1PAForDN(LocalTensor<L1Type>& l1Tensor, GlobalTensor<L1Type>& gmTensor,
GlobalTensor<int32_t>& blockTableGm, KVLAYOUT kvLayout,
const PAShape &shape, const Position &startPos)
{
uint32_t copyFinishRowCnt = 0;
uint64_t blockTableBaseOffset = startPos.bIdx * shape.maxblockNumPerBatch;
uint32_t curS2Idx = startPos.s2Offset;
constexpr uint32_t blockElementCnt = 32U / sizeof(L1Type);
while(copyFinishRowCnt < shape.copyRowNum) {
uint64_t blockIdOffset = curS2Idx / shape.blockSize;
uint64_t remainRowCnt = curS2Idx % shape.blockSize;
uint64_t idInBlockTable = blockTableGm.GetValue(blockTableBaseOffset + blockIdOffset);
uint32_t copyRowCnt = shape.blockSize - remainRowCnt;
if (copyFinishRowCnt + copyRowCnt > shape.copyRowNum) {
copyRowCnt = shape.copyRowNum - copyFinishRowCnt;
}
uint64_t offset = idInBlockTable * shape.blockSize * shape.headNum * shape.headDim;
if (kvLayout == KVLAYOUT::NZ) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.blockSize * shape.headDim) + remainRowCnt * blockElementCnt + startPos.dIdx * shape.blockSize;
LocalTensor<L1Type> tmpNopeDstTensor = l1Tensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = gmTensor[offset];
DataCopyGmNZToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, (shape.copyRowNumAlign - copyRowCnt), (shape.blockSize - copyRowCnt), shape.actHeadDim);
} else {
uint64_t dStride = shape.headDim;
if (kvLayout == KVLAYOUT::BBH) {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim) + remainRowCnt * shape.headDim * shape.headNum + startPos.dIdx;
dStride = shape.headDim * shape.headNum;
} else {
offset += static_cast<uint64_t>(startPos.n2Idx * shape.headDim * shape.blockSize) + remainRowCnt * shape.headDim + startPos.dIdx;
}
uint32_t dValue = shape.actHeadDim;
uint32_t srcDValue = dStride;
LocalTensor<L1Type> tmpNopeDstTensor = l1Tensor[copyFinishRowCnt * blockElementCnt];
GlobalTensor<L1Type> tmpNopeSrcTensor = gmTensor[offset];
DataCopyGmScaleDNToL1(tmpNopeDstTensor, tmpNopeSrcTensor, copyRowCnt, copyRowCnt, dValue, srcDValue);
}
copyFinishRowCnt += copyRowCnt;
curS2Idx += copyRowCnt;
}
}
template<typename INPUT_T>
__aicore__ inline void CopyToL1Nd2Nz(const LocalTensor<INPUT_T> &l1Tensor, const GlobalTensor<INPUT_T> &gmTensor,
uint32_t nValue, uint32_t dValue, uint32_t srcDValue)
{
Nd2NzParams gm2L1Nd2NzParams;
gm2L1Nd2NzParams.ndNum = 1; // ND矩阵的个数
gm2L1Nd2NzParams.nValue = nValue; // 单个ND矩阵的实际行数单位为元素个数
gm2L1Nd2NzParams.dValue = dValue; // 单个ND矩阵的实际列数单位为元素个数
gm2L1Nd2NzParams.srcNdMatrixStride = 0; // 相邻ND矩阵起始地址之间的偏移 单位为元素个数
gm2L1Nd2NzParams.srcDValue = srcDValue; // 同一个ND矩阵中相邻行起始地址之间的偏移 单位为元素个数
#if (__CCE_AICORE__ == 310) || (defined __DAV_310R6__) || (__NPU_ARCH__ == 5102)
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value || IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value || IsSameType<INPUT_T, int8_t>::value) {
gm2L1Nd2NzParams.dstNzC0Stride = (nValue + 31) >> 5 << 5;
} else {
gm2L1Nd2NzParams.dstNzC0Stride = (nValue + 15) >> 4 << 4;
}
#else
gm2L1Nd2NzParams.dstNzC0Stride = (nValue + 15) >> 4 << 4; // NZ矩阵相邻Block起始地址之间的偏移 单位为Block个数
#endif
gm2L1Nd2NzParams.dstNzNStride = 1; // 转换为NZ矩阵后ND之间相邻两行在NZ矩阵中起始地址之间的偏移 单位为Block个数
gm2L1Nd2NzParams.dstNzMatrixStride = 0; // 两个NZ矩阵起始地址之间的偏移 单位为元素数量
DataCopy(l1Tensor, gmTensor, gm2L1Nd2NzParams);
}
template<typename INPUT_T>
__aicore__ inline void CopyScaleToL1Nd2Nz(const LocalTensor<INPUT_T> &l1Tensor, const GlobalTensor<INPUT_T> &gmTensor,
uint32_t nValue, uint32_t dValue, uint32_t srcDValue)
{
Nd2NzParams gm2L1Nd2NzParams;
gm2L1Nd2NzParams.ndNum = 1; // ND矩阵的个数
gm2L1Nd2NzParams.nValue = nValue / 2; // 单个ND矩阵的实际行数单位为元素个数
gm2L1Nd2NzParams.dValue = dValue; // 单个ND矩阵的实际列数单位为元素个数
gm2L1Nd2NzParams.srcNdMatrixStride = 0; // 相邻ND矩阵起始地址之间的偏移 单位为元素个数
gm2L1Nd2NzParams.srcDValue = srcDValue; // 同一个ND矩阵中相邻行起始地址之间的偏移 单位为元素个数
gm2L1Nd2NzParams.dstNzC0Stride = nValue / 2; // NZ矩阵相邻Block起始地址之间的偏移 单位为Block个数
gm2L1Nd2NzParams.dstNzNStride = 1; // 转换为NZ矩阵后ND之间相邻两行在NZ矩阵中起始地址之间的偏移 单位为Block个数
gm2L1Nd2NzParams.dstNzMatrixStride = gm2L1Nd2NzParams.nValue; // 两个NZ矩阵起始地址之间的偏移 单位为元素数量
LocalTensor<bfloat16_t> l1TensorCast = l1Tensor.template ReinterpretCast<bfloat16_t>();
GlobalTensor<bfloat16_t> gmTensorCast;
gmTensorCast.SetGlobalBuffer(((__gm__ bfloat16_t*)(gmTensor.GetPhyAddr())));
DataCopy(l1TensorCast, gmTensorCast, gm2L1Nd2NzParams);
}
template<typename INPUT_T>
__aicore__ inline void CopyScaleToL1Dn2Nz(const LocalTensor<INPUT_T> &l1Tensor, const GlobalTensor<INPUT_T> &gmTensor,
uint32_t nValue, uint32_t dValue, uint32_t srcDValue)
{
Dn2NzParams gm2L1Dn2NzParams;
gm2L1Dn2NzParams.dnNum = 1; // ND矩阵的个数
gm2L1Dn2NzParams.nValue = nValue / 2; // 单个DN矩阵的实际列数单位为元素个数
gm2L1Dn2NzParams.dValue = dValue; // 单个DN矩阵的实际行数单位为元素个数
gm2L1Dn2NzParams.srcDnMatrixStride = 0; // 相邻Dn矩阵起始地址之间的偏移 单位为元素个数
gm2L1Dn2NzParams.srcDValue = srcDValue / 2; // 同一个Dn矩阵中相邻行起始地址之间的偏移 单位为元素个数
gm2L1Dn2NzParams.dstNzC0Stride = nValue / 2;
gm2L1Dn2NzParams.dstNzNStride = 1; // 转换为NZ矩阵后ND之间相邻两行在NZ矩阵中起始地址之间的偏移 单位为Block个数
gm2L1Dn2NzParams.dstNzMatrixStride = gm2L1Dn2NzParams.nValue; // 两个NZ矩阵起始地址之间的偏移 单位为元素数量
LocalTensor<bfloat16_t> l1TensorCast = l1Tensor.template ReinterpretCast<bfloat16_t>();
GlobalTensor<bfloat16_t> gmTensorCast;
gmTensorCast.SetGlobalBuffer(((__gm__ bfloat16_t*)(gmTensor.GetPhyAddr())));
DataCopy(l1TensorCast, gmTensorCast, gm2L1Dn2NzParams);
}
template<typename INPUT_T>
__aicore__ inline void CopyToL1Nd2NzGS1Merge(const LocalTensor<INPUT_T> &l1Tensor, const GlobalTensor<INPUT_T> &gmTensor,
uint32_t ndNum, uint32_t nValue, uint32_t dValue, uint32_t srcNdMatrixStride, uint32_t srcDValue, uint32_t dstNzC0Stride) // BSNGD 合轴拷贝
{
Nd2NzParams gm2L1Nd2NzParams;
gm2L1Nd2NzParams.ndNum = ndNum; // ND矩阵的个数
gm2L1Nd2NzParams.nValue = nValue; // 单个ND矩阵的实际行数单位为元素个数
gm2L1Nd2NzParams.dValue = dValue; // 单个ND矩阵的实际列数单位为元素个数
gm2L1Nd2NzParams.srcNdMatrixStride = srcNdMatrixStride; // 相邻ND矩阵起始地址之间的偏移 单位为元素个数
gm2L1Nd2NzParams.srcDValue = srcDValue; // 同一个ND矩阵中相邻行起始地址之间的偏移 单位为元素个数
#if (__CCE_AICORE__ == 310) || (defined __DAV_310R6__) || (__NPU_ARCH__ == 5102)
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value || IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value || IsSameType<INPUT_T, int8_t>::value) {
gm2L1Nd2NzParams.dstNzC0Stride = (dstNzC0Stride + 31) >> 5 << 5; // NZ矩阵相邻Block起始地址之间的偏移单位为Block个数32对齐
} else {
gm2L1Nd2NzParams.dstNzC0Stride = (dstNzC0Stride + 15) >> 4 << 4; // NZ矩阵相邻Block起始地址之间的偏移单位为Block个数16对齐
}
#else
gm2L1Nd2NzParams.dstNzC0Stride = (dstNzC0Stride + 15) >> 4 << 4; // NZ矩阵相邻Block起始地址之间的偏移单位为Block个数16对齐
#endif
gm2L1Nd2NzParams.dstNzNStride = 1; // 转换为NZ矩阵后ND之间相邻两行在NZ矩阵中起始地址之间的偏移 单位为Block个数
gm2L1Nd2NzParams.dstNzMatrixStride = nValue * 32 / sizeof(INPUT_T); // 两个NZ矩阵起始地址之间的偏移 单位为元素数量
DataCopy(l1Tensor, gmTensor, gm2L1Nd2NzParams);
}
#endif

View File

@@ -0,0 +1,56 @@
/**
 * 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 FixpipeOut.h
* \brief
*/
#ifndef FIXPIPEOUT_H
#define FIXPIPEOUT_H
constexpr FixpipeConfig PFA_CFG_ROW_MAJOR_UB = {CO2Layout::ROW_MAJOR, true}; // ROW_MAJOR: 使能NZ2ND输出数据格式为ND格式; true: 用于用户指定目的地址的位置是否是UB
constexpr FixpipeConfig PFA_CFG_ROW_MAJOR_GM = {CO2Layout::ROW_MAJOR, false}; // ROW_MAJOR: 使能NZ2ND输出数据格式为ND格式; true: 用于用户指定目的地址的位置是否是UB
constexpr FixpipeConfig FA_CFG_NZ_UB = {CO2Layout::NZ, true}; // 不使能NZ2ND输出数据格式为NZ格式; true: 用于用户指定目的地址的位置是否是UB
struct fixpipeOutParams {
uint32_t fixpOutMSize;
uint32_t fixpOutNSize;
};
template<typename mmOutputType, typename computeType, typename l0cType>
__aicore__ inline void FixpipeMmCopyOutToUB(LocalTensor<mmOutputType>& mmResUb, LocalTensor<l0cType>& L0CTensor, const fixpipeOutParams& fixpOutParam)
{
FixpipeParamsC310<CO2Layout::ROW_MAJOR> L0C2UbFixpParams; // L0C->UB
L0C2UbFixpParams.nSize = (fixpOutParam.fixpOutNSize + 7) >> 3 << 3; // L0C上的bmm1结果矩阵N方向的size大小同mmadParams.n8个元素32B)对齐
L0C2UbFixpParams.mSize = (fixpOutParam.fixpOutMSize + 1) >> 1 << 1; // 有效数据不足16行只需输出部分行即可;L0C上的bmm1结果矩阵M方向的size大小必须是偶数
L0C2UbFixpParams.srcStride = ((L0C2UbFixpParams.mSize + 15) >> 4) << 4; // L0C上matmul结果相邻连续数据片断间隔前面一个数据块的头与后面数据块的头的间隔单位为16 *sizeof(T) //源NZ矩阵中相邻Z排布的起始地址偏移
L0C2UbFixpParams.dstStride = (L0C2UbFixpParams.nSize + 15) >> 4 << 4; // mmResUb上两行之间的间隔单位element。 // 128根据比对dump文件得到ND方案(S1 * S2)时脏数据用mask剔除
L0C2UbFixpParams.dualDstCtl = 1; // 双目标模式按M维度拆分 M / 2 * N写入每个UBM必须为2的倍数
L0C2UbFixpParams.params.ndNum = 1;
L0C2UbFixpParams.params.srcNdStride = 0;
L0C2UbFixpParams.params.dstNdStride = 0;
Fixpipe<mmOutputType, computeType, PFA_CFG_ROW_MAJOR_UB>(mmResUb, L0CTensor, L0C2UbFixpParams); // 将matmul结果从L0C搬运到UB
}
template<typename mmOutputType, typename computeType, typename l0cType>
__aicore__ inline void FixpipeMmCopyOutToGm(GlobalTensor<mmOutputType>& mmResGm,LocalTensor<l0cType>& L0CTensor, const fixpipeOutParams& fixpOutParam)
{
FixpipeParamsC310<CO2Layout::ROW_MAJOR> L0C2GmFixpParams; // L0C->Gm
L0C2GmFixpParams.nSize = (fixpOutParam.fixpOutNSize + 7) >> 3 << 3; // L0C上的bmm1结果矩阵N方向的size大小同mmadParams.n8个元素32B)对齐分档计算且vector1中通过mask筛选出实际有效值
L0C2GmFixpParams.mSize = (fixpOutParam.fixpOutMSize + 1) >> 1 << 1; // 有效数据不足16行只需输出部分行即可;L0C上的bmm1结果矩阵M方向的size大小同mmadParams.m
L0C2GmFixpParams.srcStride = ((L0C2GmFixpParams.mSize + 15) >> 4) << 4; // L0C上bmm1结果相邻连续数据片断间隔前面一个数据块的头与后面数据块的头的间隔
L0C2GmFixpParams.dstStride = (L0C2GmFixpParams.nSize + 15) >> 4 << 4; // mmResGm上两行之间的间隔
L0C2GmFixpParams.dualDstCtl = 1;
L0C2GmFixpParams.params.ndNum = 1;
L0C2GmFixpParams.params.srcNdStride = 0;
L0C2GmFixpParams.params.dstNdStride = 0;
Fixpipe<mmOutputType, computeType, PFA_CFG_ROW_MAJOR_GM>(mmResGm, L0CTensor, L0C2GmFixpParams); // 将matmul结果从L0C搬运到Gm
}
#endif

View File

@@ -0,0 +1,139 @@
/**
 * 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 vf_basic_block_aligned128_no_update_sfa.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_ALIGNED128_NO_UPDATE_SFA_H
#define VF_BASIC_BLOCK_ALIGNED128_NO_UPDATE_SFA_H
#include "vf_basic_block_utils.h"
using namespace regbaseutil;
namespace FaVectorApi {
// no update, originN == 128
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__simd_vf__ void ProcessVec1NoUpdateImpl128VF(
__ubuf__ T2 * expUb, __ubuf__ T * expSumUb, __ubuf__ T * maxUb, __ubuf__ T * maxUbStart,
__ubuf__ T * srcUb, const uint32_t blockStride, const uint32_t repeatStride,
const uint16_t m, const T scale, const T minValue)
{
AscendC::MicroAPI::RegTensor<float> vreg_input_x;
AscendC::MicroAPI::RegTensor<float> vreg_input_x_unroll;
AscendC::MicroAPI::RegTensor<float> vreg_max_tmp;
AscendC::MicroAPI::RegTensor<float> vreg_input_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_brc;
AscendC::MicroAPI::RegTensor<float> vreg_exp_sum;
AscendC::MicroAPI::RegTensor<float> vreg_exp_even;
AscendC::MicroAPI::RegTensor<float> vreg_exp_odd;
// bfloat16_t
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_even_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_odd_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_bf16;
// half
AscendC::MicroAPI::RegTensor<half> vreg_exp_even_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_odd_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_fp16;
AscendC::MicroAPI::UnalignRegForStore ureg_max;
AscendC::MicroAPI::UnalignRegForStore ureg_exp_sum;
AscendC::MicroAPI::MaskReg preg_all = AscendC::MicroAPI::CreateMask<T, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::ALL>();
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::LoadAlign(vreg_input_x_unroll, srcUb + floatRepSize + i * s2BaseSize);
AscendC::MicroAPI::Muls(vreg_input_x, vreg_input_x, scale, preg_all); // Muls(scale)
AscendC::MicroAPI::Muls(vreg_input_x_unroll, vreg_input_x_unroll, scale, preg_all);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + i * s2BaseSize, vreg_input_x, preg_all);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + floatRepSize + i * s2BaseSize, vreg_input_x_unroll, preg_all);
AscendC::MicroAPI::Max(vreg_max_tmp, vreg_input_x, vreg_input_x_unroll, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::MAX, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_input_max, vreg_max_tmp, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)maxUb), vreg_input_max, ureg_max, 1);
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)maxUb), ureg_max, 0);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
for (uint16_t i = 0; i < m; ++i) {
// maxUb is [S1, 1], BRC_B32 is reading one fp32 element and broadcast it to all 64 vreg element
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(
vreg_max_brc, maxUbStart + i);
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_DINTLV_B32>(
vreg_input_x, vreg_input_x_unroll, srcUb + i * s2BaseSize);
AscendC::MicroAPI::ExpSub(vreg_exp_even, vreg_input_x, vreg_max_brc, preg_all);
AscendC::MicroAPI::ExpSub(vreg_exp_odd, vreg_input_x_unroll, vreg_max_brc, preg_all);
// x_sum = sum(x_exp, axis=-1, keepdims=True)
AscendC::MicroAPI::Add(vreg_exp_sum, vreg_exp_even, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::SUM, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_exp_sum, vreg_exp_sum, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)expSumUb), vreg_exp_sum, ureg_exp_sum, 1);
if constexpr (IsSameType<T2, bfloat16_t>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_bf16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_bf16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_bf16, (RegTensor<uint16_t>&)vreg_exp_even_bf16,
(RegTensor<uint16_t>&)vreg_exp_odd_bf16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_bf16, blockStride, repeatStride, preg_all_b16);
} else if constexpr (IsSameType<T2, half>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_fp16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_fp16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_fp16, (RegTensor<uint16_t>&)vreg_exp_even_fp16,
(RegTensor<uint16_t>&)vreg_exp_odd_fp16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_fp16, blockStride, repeatStride, preg_all_b16);
}
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)expSumUb), ureg_exp_sum, 0);
}
// no update, originN == 128
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__aicore__ inline void ProcessVec1NoUpdateImpl128(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
// 写的时候固定用65或者33的stride去写因为正向目前使能settail之后mm2的s1方向必须算满128或者64行
// stride, high 16bits: blockStride (m*16*2/32), low 16bits: repeatStride (1)
const uint32_t blockStride = s1BaseSize >> 1 | 0x1;
const uint32_t repeatStride = 1;
__ubuf__ T2 * expUb = (__ubuf__ T2*)dstTensor.GetPhyAddr();
__ubuf__ T * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
__ubuf__ T * maxUb = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * maxUbStart = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
ProcessVec1NoUpdateImpl128VF<T, T2, s1BaseSize, s2BaseSize>(
expUb, expSumUb, maxUb, maxUbStart, srcUb, blockStride, repeatStride, m, scale, minValue);
}
} // namespace
#endif // VF_BASIC_BLOCK_ALIGNED128_NO_UPDATE_SFA_H

View File

@@ -0,0 +1,143 @@
/**
 * 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 vf_basic_block_aligned128_update_sfa.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_ALIGNED128_UPDATE_SFA_H
#define VF_BASIC_BLOCK_ALIGNED128_UPDATE_SFA_H
#include "vf_basic_block_utils.h"
using namespace regbaseutil;
namespace FaVectorApi {
// update, originN == 128
template <typename T, typename T2, uint32_t s1BaseSize = 128, uint32_t s2BaseSize = 128>
__simd_vf__ void ProcessVec1UpdateImpl128VF(
__ubuf__ T2 * expUb, __ubuf__ T * srcUb, __ubuf__ T * inMaxUb,
__ubuf__ T * tmpExpSumUb, __ubuf__ T * tmpMaxUb, __ubuf__ T * tmpMaxUb2, const uint32_t blockStride,
const uint32_t repeatStride, const uint16_t m, const T scale, const T minValue)
{
AscendC::MicroAPI::RegTensor<float> vreg_input_x;
AscendC::MicroAPI::RegTensor<float> vreg_input_x_unroll;
AscendC::MicroAPI::RegTensor<float> vreg_max_tmp;
AscendC::MicroAPI::RegTensor<float> vreg_in_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_new;
AscendC::MicroAPI::RegTensor<float> vreg_max_brc;
AscendC::MicroAPI::RegTensor<float> vreg_cur_max;
AscendC::MicroAPI::RegTensor<float> vreg_exp_sum;
AscendC::MicroAPI::RegTensor<float> vreg_in_exp_sum;
AscendC::MicroAPI::RegTensor<float> vreg_exp_even;
AscendC::MicroAPI::RegTensor<float> vreg_exp_odd;
// bfloat16_t
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_even_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_odd_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_bf16;
// half
AscendC::MicroAPI::RegTensor<half> vreg_exp_even_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_odd_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_fp16;
AscendC::MicroAPI::UnalignRegForStore ureg_max;
AscendC::MicroAPI::UnalignRegForStore ureg_exp_sum;
AscendC::MicroAPI::MaskReg preg_all = AscendC::MicroAPI::CreateMask<float, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::ALL>();
// x_max = max(src, axis=-1, keepdims=True); x_max = Max(x_max, inMax)
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::LoadAlign(vreg_input_x_unroll, srcUb + floatRepSize + i * s2BaseSize);
AscendC::MicroAPI::Muls(vreg_input_x, vreg_input_x, scale, preg_all); // Muls(scale)
AscendC::MicroAPI::Muls(vreg_input_x_unroll, vreg_input_x_unroll, scale, preg_all);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + i * s2BaseSize, vreg_input_x, preg_all);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + floatRepSize + i * s2BaseSize, vreg_input_x_unroll, preg_all);
AscendC::MicroAPI::Max(vreg_max_tmp, vreg_input_x, vreg_input_x_unroll, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::MAX, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_max_tmp, vreg_max_tmp, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpMaxUb), vreg_max_tmp, ureg_max, 1);
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpMaxUb), ureg_max, 0);
AscendC::MicroAPI::LoadAlign(vreg_in_max, inMaxUb);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
AscendC::MicroAPI::LoadAlign(vreg_cur_max, tmpMaxUb2); // 获取新的max[s1, 1]
AscendC::MicroAPI::Max(vreg_max_new, vreg_cur_max, vreg_in_max, preg_all); // 计算新、旧max的最大值
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)tmpMaxUb2, vreg_max_new, preg_all);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_max_brc, tmpMaxUb2 + i);
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_DINTLV_B32>(
vreg_input_x, vreg_input_x_unroll, srcUb + i * s2BaseSize);
AscendC::MicroAPI::ExpSub(vreg_exp_even, vreg_input_x, vreg_max_brc, preg_all);
AscendC::MicroAPI::ExpSub(vreg_exp_odd, vreg_input_x_unroll, vreg_max_brc, preg_all);
// x_sum = sum(x_exp, axis=-1, keepdims=True)
AscendC::MicroAPI::Add(vreg_exp_sum, vreg_exp_even, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::SUM, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_exp_sum, vreg_exp_sum, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpExpSumUb), vreg_exp_sum, ureg_exp_sum, 1);
if constexpr (IsSameType<T2, bfloat16_t>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_bf16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_bf16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_bf16, (RegTensor<uint16_t>&)vreg_exp_even_bf16,
(RegTensor<uint16_t>&)vreg_exp_odd_bf16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_bf16, blockStride, repeatStride, preg_all_b16);
} else if constexpr (IsSameType<T2, half>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_fp16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_fp16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_fp16, (RegTensor<uint16_t>&)vreg_exp_even_fp16,
(RegTensor<uint16_t>&)vreg_exp_odd_fp16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_fp16, blockStride, repeatStride, preg_all_b16);
}
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpExpSumUb), ureg_exp_sum, 0);
}
// update, originN == 128
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__aicore__ inline void ProcessVec1UpdateImpl128(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
// 写的时候固定用65或者33的stride去写因为正向目前使能settail之后mm2的s1方向必须算满128或者64行
// stride, high 16bits: blockStride (m*16*2/32), low 16bits: repeatStride (1)
const uint32_t blockStride = s1BaseSize >> 1 | 0x1;
const uint32_t repeatStride = 1;
__ubuf__ T2 * expUb = (__ubuf__ T2*)dstTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
__ubuf__ T * inMaxUb = (__ubuf__ T*)inMaxTensor.GetPhyAddr();
__ubuf__ T * tmpExpSumUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr();
__ubuf__ T * tmpMaxUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
__ubuf__ T * tmpMaxUb2 = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
ProcessVec1UpdateImpl128VF <T, T2, s1BaseSize, s2BaseSize>(
expUb, srcUb, inMaxUb, tmpExpSumUb, tmpMaxUb, tmpMaxUb2, blockStride, repeatStride, m, scale, minValue);
}
} // namespace
#endif // VF_BASIC_BLOCK_ALIGNED128_UPDATE_SFA_H

View File

@@ -0,0 +1,149 @@
/**
 * 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 vf_basic_block_unaligned128_no_update_sfa.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_UNALIGNED128_NO_UPDATE_SFA_H
#define VF_BASIC_BLOCK_UNALIGNED128_NO_UPDATE_SFA_H
#include "vf_basic_block_utils.h"
using namespace regbaseutil;
namespace FaVectorApi {
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__simd_vf__ void ProcessVec1NoUpdateGeneralImpl128VF(
__ubuf__ T2 * expUb, __ubuf__ T * expSumUb, __ubuf__ T * maxUb, __ubuf__ T * maxUbStart,
__ubuf__ T * srcUb, const uint32_t blockStride, const uint32_t repeatStride,
const uint16_t m, const T scale, const T minValue, uint32_t pltOriTailN, uint32_t pltTailN)
{
AscendC::MicroAPI::RegTensor<float> vreg_min;
AscendC::MicroAPI::RegTensor<float> vreg_input_x;
AscendC::MicroAPI::RegTensor<float> vreg_input_x_unroll;
AscendC::MicroAPI::RegTensor<float> vreg_input_x_unroll_new;
AscendC::MicroAPI::RegTensor<float> vreg_max_tmp;
AscendC::MicroAPI::RegTensor<float> vreg_input_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_brc;
AscendC::MicroAPI::RegTensor<float> vreg_exp_sum;
AscendC::MicroAPI::RegTensor<float> vreg_exp_even;
AscendC::MicroAPI::RegTensor<float> vreg_exp_odd;
// bfloat16_t
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_even_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_odd_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_bf16;
// half
AscendC::MicroAPI::RegTensor<half> vreg_exp_even_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_odd_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_fp16;
AscendC::MicroAPI::UnalignRegForStore ureg_max;
AscendC::MicroAPI::UnalignRegForStore ureg_exp_sum;
AscendC::MicroAPI::MaskReg preg_all = AscendC::MicroAPI::CreateMask<float, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b8 = AscendC::MicroAPI::CreateMask<T2, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_tail_n = AscendC::MicroAPI::UpdateMask<float>(pltTailN);
AscendC::MicroAPI::MaskReg preg_ori_tail_n = AscendC::MicroAPI::UpdateMask<float>(pltOriTailN);
AscendC::MicroAPI::MaskReg preg_reduce_n =
AscendC::MicroAPI::CreateMask<float, AscendC::MicroAPI::MaskPattern::VL8>();
AscendC::MicroAPI::Duplicate(vreg_min, minValue);
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::LoadAlign(vreg_input_x_unroll, srcUb + floatRepSize + i * s2BaseSize);
AscendC::MicroAPI::Muls(vreg_input_x, vreg_input_x, scale, preg_all); // Muls(scale)
AscendC::MicroAPI::Muls(vreg_input_x_unroll, vreg_input_x_unroll, scale, preg_ori_tail_n);
AscendC::MicroAPI::Select(vreg_input_x_unroll_new, vreg_input_x_unroll, vreg_min, preg_ori_tail_n);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + i * s2BaseSize, vreg_input_x, preg_all);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + floatRepSize + i * s2BaseSize, vreg_input_x_unroll_new, preg_tail_n);
AscendC::MicroAPI::Max(vreg_max_tmp, vreg_input_x, vreg_input_x_unroll_new, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::MAX, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_input_max, vreg_max_tmp, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)maxUb), vreg_input_max, ureg_max, 1);
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)maxUb), ureg_max, 0);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_max_brc, maxUbStart + i);
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_DINTLV_B32>(
vreg_input_x, vreg_input_x_unroll, srcUb + i * s2BaseSize);
AscendC::MicroAPI::ExpSub(vreg_exp_even, vreg_input_x, vreg_max_brc, preg_all);
AscendC::MicroAPI::ExpSub(vreg_exp_odd, vreg_input_x_unroll, vreg_max_brc, preg_all);
// x_sum = sum(x_exp, axis=-1, keepdims=True)
AscendC::MicroAPI::Add(vreg_exp_sum, vreg_exp_even, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::SUM, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_exp_sum, vreg_exp_sum, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)expSumUb), vreg_exp_sum, ureg_exp_sum, 1);
if constexpr (IsSameType<T2, bfloat16_t>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_bf16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_bf16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_bf16, (RegTensor<uint16_t>&)vreg_exp_even_bf16,
(RegTensor<uint16_t>&)vreg_exp_odd_bf16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_bf16, blockStride, repeatStride, preg_all_b16);
} else if constexpr (IsSameType<T2, half>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_fp16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_fp16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_fp16, (RegTensor<uint16_t>&)vreg_exp_even_fp16,
(RegTensor<uint16_t>&)vreg_exp_odd_fp16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_fp16, blockStride, repeatStride, preg_all_b16);
}
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)expSumUb), ureg_exp_sum, 0);
}
// no update, 64 < originN <= 128
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__aicore__ inline void ProcessVec1NoUpdateGeneralImpl128(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
// 写的时候固定用65或者33的stride去写因为正向目前使能settail之后mm2的s1方向必须算满128或者64行
// stride, high 16bits: blockStride (65*16*2/32)单位block, low 16bits: repeatStride (1)
const uint32_t blockStride = s1BaseSize >> 1 | 0x1;
const uint32_t repeatStride = 1;
__ubuf__ T2 * expUb = (__ubuf__ T2*)dstTensor.GetPhyAddr();
__ubuf__ T * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
__ubuf__ T * maxUb = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * maxUbStart = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
const uint32_t oriTailN = originN - floatRepSize;
const uint32_t tailN = s2BaseSize - floatRepSize;
uint32_t pltOriTailN = oriTailN;
uint32_t pltTailN = tailN;
ProcessVec1NoUpdateGeneralImpl128VF<T, T2, s1BaseSize, s2BaseSize>(
expUb, expSumUb, maxUb, maxUbStart, srcUb, blockStride, repeatStride, m, scale, minValue,
pltOriTailN, pltTailN);
}
} // namespace
#endif // VF_BASIC_BLOCK_UNALIGNED128_NO_UPDATE_SFA_H

View File

@@ -0,0 +1,159 @@
/**
 * 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 vf_basic_block_unaligned128_update_sfa.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_UNALIGNED128_UPDATE_SFA_H
#define VF_BASIC_BLOCK_UNALIGNED128_UPDATE_SFA_H
#include "vf_basic_block_utils.h"
using namespace regbaseutil;
namespace FaVectorApi {
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__simd_vf__ void ProcessVec1UpdateGeneralImpl128VF(
__ubuf__ T2 * expUb, __ubuf__ T * srcUb, __ubuf__ T * inMaxUb,
__ubuf__ T * tmpExpSumUb, __ubuf__ T * tmpMaxUb, __ubuf__ T * tmpMaxUb2, const uint32_t blockStride,
const uint32_t repeatStride, const uint16_t m, const T scale, const T minValue, uint32_t pltOriTailN,
uint32_t pltTailN, uint32_t pltN)
{
AscendC::MicroAPI::RegTensor<float> vreg_min;
AscendC::MicroAPI::RegTensor<float> vreg_input_x;
AscendC::MicroAPI::RegTensor<float> vreg_input_x_unroll;
AscendC::MicroAPI::RegTensor<float> vreg_input_x_unroll_new;
AscendC::MicroAPI::RegTensor<float> vreg_max_tmp;
AscendC::MicroAPI::RegTensor<float> vreg_cur_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_new;
AscendC::MicroAPI::RegTensor<float> vreg_exp_sum;
AscendC::MicroAPI::RegTensor<float> vreg_in_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_brc;
AscendC::MicroAPI::RegTensor<float> vreg_exp_even;
AscendC::MicroAPI::RegTensor<float> vreg_exp_odd;
// bfloat16_t
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_even_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_odd_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_bf16;
// half
AscendC::MicroAPI::RegTensor<half> vreg_exp_even_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_odd_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_exp_fp16;
AscendC::MicroAPI::UnalignRegForStore ureg_max;
AscendC::MicroAPI::UnalignRegForStore ureg_exp_sum;
AscendC::MicroAPI::MaskReg preg_all = AscendC::MicroAPI::CreateMask<float, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b16 = AscendC::MicroAPI::CreateMask<uint16_t,
AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_n_b16 = AscendC::MicroAPI::UpdateMask<uint16_t>(pltN);
AscendC::MicroAPI::MaskReg preg_tail_n = AscendC::MicroAPI::UpdateMask<T>(pltTailN);
AscendC::MicroAPI::MaskReg preg_ori_tail_n = AscendC::MicroAPI::UpdateMask<T>(pltOriTailN);
AscendC::MicroAPI::Duplicate(vreg_min, minValue);
// x_max = max(src, axis=-1, keepdims=True); x_max = Max(x_max, inMax)
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::LoadAlign(vreg_input_x_unroll, srcUb + floatRepSize + i * s2BaseSize);
AscendC::MicroAPI::Muls(vreg_input_x, vreg_input_x, scale, preg_all); // Muls(scale)
AscendC::MicroAPI::Muls(vreg_input_x_unroll, vreg_input_x_unroll, scale, preg_ori_tail_n);
AscendC::MicroAPI::Select(vreg_input_x_unroll_new, vreg_input_x_unroll, vreg_min, preg_ori_tail_n);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + i * s2BaseSize, vreg_input_x, preg_all);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + floatRepSize + i * s2BaseSize, vreg_input_x_unroll_new, preg_tail_n);
AscendC::MicroAPI::Max(vreg_max_tmp, vreg_input_x, vreg_input_x_unroll_new, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::MAX, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_cur_max, vreg_max_tmp, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpMaxUb), vreg_cur_max, ureg_max, 1);
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpMaxUb), ureg_max, 0);
AscendC::MicroAPI::LoadAlign(vreg_in_max, inMaxUb);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
AscendC::MicroAPI::LoadAlign(vreg_cur_max, tmpMaxUb2); // 获取新的max[s1, 1]
AscendC::MicroAPI::Max(vreg_max_new, vreg_cur_max, vreg_in_max, preg_all); // 计算新、旧max的最大值
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)tmpMaxUb2, vreg_max_new, preg_all);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(
vreg_max_brc, tmpMaxUb2 + i);
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_DINTLV_B32>(
vreg_input_x, vreg_input_x_unroll, srcUb + i * s2BaseSize);
AscendC::MicroAPI::ExpSub(vreg_exp_even, vreg_input_x, vreg_max_brc, preg_all);
AscendC::MicroAPI::ExpSub(vreg_exp_odd, vreg_input_x_unroll, vreg_max_brc, preg_all);
// x_sum = sum(x_exp, axis=-1, keepdims=True)
AscendC::MicroAPI::Add(vreg_exp_sum, vreg_exp_even, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::SUM, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_exp_sum, vreg_exp_sum, preg_all);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpExpSumUb), vreg_exp_sum, ureg_exp_sum, 1);
if constexpr (IsSameType<T2, bfloat16_t>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_bf16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_bf16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_bf16, (RegTensor<uint16_t>&)vreg_exp_even_bf16,
(RegTensor<uint16_t>&)vreg_exp_odd_bf16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_bf16, blockStride, repeatStride, preg_n_b16);
} else if constexpr (IsSameType<T2, half>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_even_fp16, vreg_exp_even, preg_all);
AscendC::MicroAPI::Cast<T2, T, castTraitOne>(vreg_exp_odd_fp16, vreg_exp_odd, preg_all);
AscendC::MicroAPI::Or((RegTensor<uint16_t>&)vreg_exp_fp16, (RegTensor<uint16_t>&)vreg_exp_even_fp16,
(RegTensor<uint16_t>&)vreg_exp_odd_fp16, preg_all_b16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_exp_fp16, blockStride, repeatStride, preg_n_b16);
}
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpExpSumUb), ureg_exp_sum, 0);
}
// update, 64 < originN <= 128
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__aicore__ inline void ProcessVec1UpdateGeneralImpl128(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
// 写的时候固定用65或者33的stride去写因为正向目前使能settail之后mm2的s1方向必须算满128或者64行
// stride, high 16bits: blockStride (m*16*2/32), low 16bits: repeatStride (1)
const uint32_t blockStride = s1BaseSize >> 1 | 0x1;
const uint32_t repeatStride = 1;
const uint32_t oriTailN = originN - floatRepSize;
const uint32_t tailN = s2BaseSize - floatRepSize;
uint32_t pltOriTailN = oriTailN;
uint32_t pltTailN = tailN;
uint32_t pltN = s2BaseSize;
__ubuf__ T2 * expUb = (__ubuf__ T2*)dstTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
__ubuf__ T * inMaxUb = (__ubuf__ T*)inMaxTensor.GetPhyAddr();
__ubuf__ T * tmpExpSumUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr();
__ubuf__ T * tmpMaxUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
__ubuf__ T * tmpMaxUb2 = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
ProcessVec1UpdateGeneralImpl128VF<T, T2, s1BaseSize, s2BaseSize>(
expUb, srcUb, inMaxUb, tmpExpSumUb, tmpMaxUb, tmpMaxUb2, blockStride, repeatStride,
m, scale, minValue, pltOriTailN, pltTailN, pltN);
}
} // namespace
#endif // VF_BASIC_BLOCK_UNALIGNED128_UPDATE_SFA_H

View File

@@ -0,0 +1,129 @@
/**
 * 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 vf_basic_block_unaligned64_no_update_sfa.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_UNALIGNED64_NO_UPDATE_SFA_H
#define VF_BASIC_BLOCK_UNALIGNED64_NO_UPDATE_SFA_H
#include "vf_basic_block_utils.h"
using namespace regbaseutil;
namespace FaVectorApi {
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__simd_vf__ void ProcessVec1NoUpdateImpl64VF(
__ubuf__ T2 * expUb, __ubuf__ T * expSumUb, __ubuf__ T * maxUb, __ubuf__ T * maxUbStart,
__ubuf__ T * srcUb, const uint32_t blockStride, const uint32_t repeatStride,
const uint16_t m, const T scale, const T minValue, uint32_t pltOriginalN, uint32_t pltSrcN)
{
AscendC::MicroAPI::RegTensor<float> vreg_min;
AscendC::MicroAPI::RegTensor<float> vreg_input_x;
AscendC::MicroAPI::RegTensor<float> vreg_input_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_brc;
AscendC::MicroAPI::RegTensor<float> vreg_exp;
AscendC::MicroAPI::RegTensor<float> vreg_exp_sum;
// bfloat16_t
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_dst_even_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_dst_odd_bf16;
// half
AscendC::MicroAPI::RegTensor<half> vreg_exp_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_dst_even_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_dst_odd_fp16;
AscendC::MicroAPI::UnalignRegForStore ureg_max;
AscendC::MicroAPI::UnalignRegForStore ureg_exp_sum;
AscendC::MicroAPI::MaskReg preg_all = AscendC::MicroAPI::CreateMask<float, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_src_n = AscendC::MicroAPI::UpdateMask<float>(pltSrcN);
AscendC::MicroAPI::MaskReg preg_src_n_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::H>();
AscendC::MicroAPI::MaskReg preg_ori_src_n = AscendC::MicroAPI::UpdateMask<T>(pltOriginalN);
// x_max = max(src, axis=-1, keepdims=True)
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::Muls(vreg_input_x, vreg_input_x, scale, preg_ori_src_n); // Muls(scale)
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + i * s2BaseSize, vreg_input_x, preg_src_n);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::MAX, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_input_max, vreg_input_x, preg_ori_src_n);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)maxUb), vreg_input_max, ureg_max, 1);
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)maxUb), ureg_max, 0);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(
vreg_max_brc, maxUbStart + i);
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::ExpSub(vreg_exp, vreg_input_x, vreg_max_brc, preg_ori_src_n);
// x_sum = sum(x_exp, axis=-1, keepdims=True)
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::SUM, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_exp_sum, vreg_exp, preg_ori_src_n);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)expSumUb), vreg_exp_sum, ureg_exp_sum, 1);
if constexpr (IsSameType<T2, bfloat16_t>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_bf16, vreg_exp, preg_all_b16);
AscendC::MicroAPI::DeInterleave(vreg_dst_even_bf16, vreg_dst_odd_bf16,
vreg_exp_bf16, vreg_exp_bf16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_dst_even_bf16, blockStride, repeatStride, preg_src_n_b16);
} else if constexpr (IsSameType<T2, half>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_fp16, vreg_exp, preg_all_b16);
AscendC::MicroAPI::DeInterleave(vreg_dst_even_fp16, vreg_dst_odd_fp16,
vreg_exp_fp16, vreg_exp_fp16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_dst_even_fp16, blockStride, repeatStride, preg_src_n_b16);
}
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)expSumUb), ureg_exp_sum, 0);
}
// no update, originN <= 64
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__aicore__ inline void ProcessVec1NoUpdateImpl64(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
__ubuf__ T2 * expUb = (__ubuf__ T2*)dstTensor.GetPhyAddr();
__ubuf__ T * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
__ubuf__ T * maxUb = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * maxUbStart = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
// 写的时候固定用65或者33的stride去写因为正向目前使能settail之后mm2的s1方向必须算满128或者64行
// stride, high 16bits: blockStride (m*16*2/32), low 16bits: repeatStride (1)
const uint32_t blockStride = s1BaseSize >> 1 | 0x1;
const uint32_t repeatStride = 1;
uint32_t pltOriginalN = originN;
uint32_t pltSrcN = s2BaseSize;
ProcessVec1NoUpdateImpl64VF<T, T2, s1BaseSize, s2BaseSize>(
expUb, expSumUb, maxUb, maxUbStart, srcUb, blockStride, repeatStride, m, scale, minValue,
pltOriginalN, pltSrcN);
}
} // namespace
#endif // VF_BASIC_BLOCK_UNALIGNED64_NO_UPDATE_SFA_H

View File

@@ -0,0 +1,141 @@
/**
 * 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 vf_basic_block_aligned64_update_sfa.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_ALIGNED64_UPDATE_SFA_H
#define VF_BASIC_BLOCK_ALIGNED64_UPDATE_SFA_H
#include "vf_basic_block_utils.h"
using namespace regbaseutil;
namespace FaVectorApi {
// update, originN <= 64
template <typename T, typename T2, uint32_t s1BaseSize = 128, uint32_t s2BaseSize = 128>
__simd_vf__ void ProcessVec1UpdateImpl64VF(
__ubuf__ T2 * expUb, __ubuf__ T * srcUb, __ubuf__ T * inMaxUb,
__ubuf__ T * tmpExpSumUb, __ubuf__ T * tmpMaxUb, __ubuf__ T * tmpMaxUb2, const uint32_t blockStride,
const uint32_t repeatStride, const uint16_t m, const T scale, const T minValue, uint32_t pltOriginalN,
uint32_t pltSrcN)
{
AscendC::MicroAPI::RegTensor<float> vreg_input_x;
AscendC::MicroAPI::RegTensor<float> vreg_max_tmp;
AscendC::MicroAPI::RegTensor<float> vreg_in_max;
AscendC::MicroAPI::RegTensor<float> vreg_max_new;
AscendC::MicroAPI::RegTensor<float> vreg_max_brc;
AscendC::MicroAPI::RegTensor<float> vreg_cur_max;
AscendC::MicroAPI::RegTensor<float> vreg_exp;
AscendC::MicroAPI::RegTensor<float> vreg_exp_sum;
// bfloat16_t
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_exp_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_dst_even_bf16;
AscendC::MicroAPI::RegTensor<bfloat16_t> vreg_dst_odd_bf16;
// half
AscendC::MicroAPI::RegTensor<half> vreg_exp_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_dst_even_fp16;
AscendC::MicroAPI::RegTensor<half> vreg_dst_odd_fp16;
AscendC::MicroAPI::UnalignRegForStore ureg_max;
AscendC::MicroAPI::UnalignRegForStore ureg_exp_sum;
AscendC::MicroAPI::MaskReg preg_all = AscendC::MicroAPI::CreateMask<float, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_all_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::ALL>();
AscendC::MicroAPI::MaskReg preg_ori_src_n = AscendC::MicroAPI::UpdateMask<T>(pltOriginalN);
AscendC::MicroAPI::MaskReg preg_src_n = AscendC::MicroAPI::UpdateMask<T>(pltSrcN);
AscendC::MicroAPI::MaskReg preg_src_n_b16 =
AscendC::MicroAPI::CreateMask<uint16_t, AscendC::MicroAPI::MaskPattern::H>();
// x_max = max(src, axis=-1, keepdims=True)
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::Muls(vreg_input_x, vreg_input_x, scale, preg_ori_src_n);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)srcUb + i * s2BaseSize, vreg_input_x, preg_src_n);
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::MAX, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_cur_max, vreg_input_x, preg_ori_src_n);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpMaxUb), vreg_cur_max, ureg_max, 1);
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpMaxUb), ureg_max, 0);
AscendC::MicroAPI::LoadAlign(vreg_in_max, inMaxUb);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
AscendC::MicroAPI::LoadAlign(vreg_cur_max, tmpMaxUb2);
AscendC::MicroAPI::Max(vreg_max_new, vreg_cur_max, vreg_in_max, preg_all); // 计算新、旧的最大值
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)tmpMaxUb2, vreg_max_new, preg_all);
AscendC::MicroAPI::LocalMemBar<MemType::VEC_STORE, MemType::VEC_LOAD>();
for (uint16_t i = 0; i < m; ++i) {
AscendC::MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(
vreg_max_brc, tmpMaxUb2 + i);
AscendC::MicroAPI::LoadAlign(vreg_input_x, srcUb + i * s2BaseSize);
AscendC::MicroAPI::ExpSub(vreg_exp, vreg_input_x, vreg_max_brc, preg_ori_src_n);
// x_sum = sum(x_exp, axis=-1, keepdims=True)
AscendC::MicroAPI::Reduce<MicroAPI::ReduceType::SUM, float, float, MicroAPI::MaskMergeMode::ZEROING>(
vreg_exp_sum, vreg_exp, preg_ori_src_n);
AscendC::MicroAPI::StoreUnAlign<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpExpSumUb), vreg_exp_sum, ureg_exp_sum, 1);
if constexpr (IsSameType<T2, bfloat16_t>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_bf16, vreg_exp, preg_all_b16);
AscendC::MicroAPI::DeInterleave(vreg_dst_even_bf16, vreg_dst_odd_bf16,
vreg_exp_bf16, vreg_exp_bf16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_dst_even_bf16, blockStride, repeatStride, preg_src_n_b16);
} else if constexpr (IsSameType<T2, half>::value) {
AscendC::MicroAPI::Cast<T2, T, castTraitZero>(vreg_exp_fp16, vreg_exp, preg_all_b16);
AscendC::MicroAPI::DeInterleave(vreg_dst_even_fp16, vreg_dst_odd_fp16,
vreg_exp_fp16, vreg_exp_fp16);
AscendC::MicroAPI::StoreAlign<T2, MicroAPI::DataCopyMode::DATA_BLOCK_COPY,
MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T2 *&)expUb), vreg_dst_even_fp16, blockStride, repeatStride, preg_src_n_b16);
}
}
AscendC::MicroAPI::StoreUnAlignPost<float, MicroAPI::PostLiteral::POST_MODE_UPDATE>(
((__ubuf__ T *&)tmpExpSumUb), ureg_exp_sum, 0);
}
// update, originN <= 64
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128>
__aicore__ inline void ProcessVec1UpdateImpl64(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
// 写的时候固定用65或者33的stride去写因为正向目前使能settail之后mm2的s1方向必须算满128或者64行
// stride, high 16bits: blockStride (m*16*2/32), low 16bits: repeatStride (1)
const uint32_t blockStride = s1BaseSize >> 1 | 0x1;
const uint32_t repeatStride = 1;
uint32_t pltOriginalN = originN;
uint32_t pltSrcN = s2BaseSize;
__ubuf__ T2 * expUb = (__ubuf__ T2*)dstTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
__ubuf__ T * inMaxUb = (__ubuf__ T*)inMaxTensor.GetPhyAddr();
__ubuf__ T * tmpExpSumUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr();
__ubuf__ T * tmpMaxUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
__ubuf__ T * tmpMaxUb2 = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
ProcessVec1UpdateImpl64VF <T, T2, s1BaseSize, s2BaseSize>(
expUb, srcUb, inMaxUb, tmpExpSumUb, tmpMaxUb, tmpMaxUb2, blockStride, repeatStride, m, scale, minValue,
pltOriginalN, pltSrcN);
}
} // namespace
#endif // VF_BASIC_BLOCK_ALIGNED64_UPDATE_SFA_H

View File

@@ -0,0 +1,112 @@
/**
 * 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 vf_basic_block_utils.h
* \brief
*/
#ifndef VF_BASIC_BLOCK_UTILS_H
#define VF_BASIC_BLOCK_UTILS_H
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_basic_intf.h"
#else
#include "kernel_operator.h"
#endif
namespace FaVectorApi {
constexpr uint32_t floatRepSize = 64;
constexpr uint32_t halfRepSize = 128;
constexpr uint32_t blockBytesU8 = 32;
constexpr float fp8e4m3MaxValue = 448.0f;
constexpr float int8MaxValue = 127.0f;
constexpr float hifp8MaxValue = 32768.0f;
constexpr float floatEps = 2.220446049250313e-16;
/* **************************************************************************************************
* Muls + Select(optional) + SoftmaxFlashV2 + Cast(fp32->fp16/bf16) + ND2NZ
* ************************************************************************************************* */
using namespace MicroAPI;
constexpr static AscendC::MicroAPI::CastTrait castTraitZero = {
AscendC::MicroAPI::RegLayout::ZERO,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_ROUND,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitOne = {
AscendC::MicroAPI::RegLayout::ONE,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_ROUND,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitTwo = {
AscendC::MicroAPI::RegLayout::TWO,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_ROUND,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitThree = {
AscendC::MicroAPI::RegLayout::THREE,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_ROUND,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitRintZero = {
AscendC::MicroAPI::RegLayout::ZERO,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_RINT,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitRintOne = {
AscendC::MicroAPI::RegLayout::ONE,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_RINT,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitRintTwo = {
AscendC::MicroAPI::RegLayout::TWO,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_RINT,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitRintThree = {
AscendC::MicroAPI::RegLayout::THREE,
AscendC::MicroAPI::SatMode::SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_RINT,
};
#define USE_MLA_FULLQUANT_V1_P(vreg_exp, vreg_rowmax_p, MaskReg) \
do { \
Muls(vreg_exp, vreg_exp, fp8e4m3MaxValue, MaskReg); \
Div(vreg_exp, vreg_exp, vreg_rowmax_p, MaskReg); \
} while (0)
#define USE_MLA_FULLQUANT_V1_P_INT8(vreg_exp, vreg_rowmax_p, MaskReg) \
do { \
Muls(vreg_exp, vreg_exp, int8MaxValue, MaskReg); \
Div(vreg_exp, vreg_exp, vreg_rowmax_p, MaskReg); \
} while (0)
#define USE_MLA_FULLQUANT_V1_P_HIFP8(vreg_exp, vreg_rowmax_p, MaskReg) \
do { \
Muls(vreg_exp, vreg_exp, hifp8MaxValue, MaskReg); \
Div(vreg_exp, vreg_exp, vreg_rowmax_p, MaskReg); \
} while (0)
} // namespace
#endif // VF_BASIC_BLOCK_UTILS_H

View File

@@ -0,0 +1,727 @@
/**
 * 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 vf_flashupdate_new.h
* \brief
*/
#ifndef MY_FLASH_UPDATE_NEW_INTERFACE_H
#define MY_FLASH_UPDATE_NEW_INTERFACE_H
#include "kernel_tensor.h"
namespace FaVectorApi {
// bf16->fp32
static constexpr MicroAPI::CastTrait castTraitFp16_32_update = {MicroAPI::RegLayout::ZERO, MicroAPI::SatMode::UNKNOWN,
MicroAPI::MaskMergeMode::ZEROING, RoundMode::UNKNOWN};
constexpr uint16_t REDUCE_SIZE = 1;
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t srcD, uint16_t reduceSize, bool isUpdatePre, bool isMlaFullQuant>
__simd_vf__ inline void FlashUpdateBasicVF(__ubuf__ float * dstUb, __ubuf__ float * curUb, __ubuf__ float * preUb,
__ubuf__ float * expMaxUb, __ubuf__ float * rowMaxUb, const uint16_t m, const uint16_t d,
const float deScaleV, const float deScaleVPre)
{
constexpr uint16_t floatRepSize = 64;
constexpr uint16_t dLoops = srcD / floatRepSize;
RegTensor<float> vreg_exp_max;
RegTensor<float> vreg_row_max;
RegTensor<float> vreg_input_pre;
RegTensor<float> vreg_input_cur;
RegTensor<float> vreg_mul;
RegTensor<float> vreg_add;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
// dstTensor = preTensor * expMaxTensor + curTensor
for (uint16_t i = 0; i < m; ++i) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_max, expMaxUb + i * reduceSize); // [m,8]
if constexpr (isMlaFullQuant) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_row_max, rowMaxUb + i * reduceSize);
}
for (uint16_t j = 0; j < dLoops; ++j) {
LoadAlign(vreg_input_pre, preUb + i * d + j * floatRepSize);
LoadAlign(vreg_input_cur, curUb + i * d + j * floatRepSize);
if constexpr (isMlaFullQuant) {
Mul(vreg_input_cur, vreg_input_cur, vreg_row_max, preg_all);
}
Mul(vreg_mul, vreg_exp_max, vreg_input_pre, preg_all);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
if constexpr (isUpdatePre) {
Muls(vreg_mul, vreg_mul, deScaleVPre, preg_all);
}
}
Add(vreg_add, vreg_mul, vreg_input_cur, preg_all);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + j * floatRepSize, vreg_add, preg_all);
}
}
}
/* **************************************************************************************************
* FlashUpdate, fp32
* ************************************************************************************************* */
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t srcD, uint16_t reduceSize, bool isUpdatePre, bool isMlaFullQuant>
__aicore__ inline void FlashUpdateBasic(const LocalTensor<T>& dstTensor, const LocalTensor<T>& curTensor,
const LocalTensor<T>& preTensor, const LocalTensor<T>& expMaxTensor, const LocalTensor<T>& rowMaxTensor,
const uint16_t m, const uint16_t d, const float deScaleV, const float deScaleVPre)
{
__ubuf__ float * dstUb = (__ubuf__ T*)dstTensor.GetPhyAddr();
__ubuf__ float * curUb = (__ubuf__ T*)curTensor.GetPhyAddr();
__ubuf__ float * preUb = (__ubuf__ T*)preTensor.GetPhyAddr();
__ubuf__ float * expMaxUb = (__ubuf__ T*)expMaxTensor.GetPhyAddr();
__ubuf__ float * rowMaxUb = (__ubuf__ T*)rowMaxTensor.GetPhyAddr();
FlashUpdateBasicVF<T, INPUT_T, OUTPUT_T, srcD, reduceSize, isUpdatePre, isMlaFullQuant>(
dstUb, curUb, preUb, expMaxUb, rowMaxUb, m, d, deScaleV, deScaleVPre);
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t reduceSize, bool isUpdatePre>
__simd_vf__ inline void FlashUpdateGeneralVF(__ubuf__ float * dstUb, __ubuf__ float * curUb, __ubuf__ float * preUb,
__ubuf__ float * expMaxUb, const uint16_t m, const uint16_t d,
const float deScaleV, const float deScaleVPre, const uint32_t pltTailD, const uint16_t hasTail)
{
RegTensor<float> vreg_exp_max;
RegTensor<float> vreg_input_pre;
RegTensor<float> vreg_input_cur;
RegTensor<float> vreg_mul;
RegTensor<float> vreg_add;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
uint32_t tmpTailD = pltTailD;
MaskReg preg_tail_d = UpdateMask<float>(tmpTailD);
constexpr uint16_t floatRepSize = 64;
const uint16_t dLoops = d / floatRepSize;
// dstTensor = preTensor * expMaxTensor + curTensor
for (uint16_t i = 0; i < m; ++i) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_max, expMaxUb + i * reduceSize); // [m,8]
for (uint16_t j = 0; j < dLoops; ++j) {
LoadAlign(vreg_input_pre, preUb + i * d + j * floatRepSize);
LoadAlign(vreg_input_cur, curUb + i * d + j * floatRepSize);
Mul(vreg_mul, vreg_exp_max, vreg_input_pre, preg_all);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
if constexpr (isUpdatePre) {
Muls(vreg_mul, vreg_mul, deScaleVPre, preg_all);
}
}
Add(vreg_add, vreg_mul, vreg_input_cur, preg_all);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + j * floatRepSize, vreg_add, preg_all);
}
for (uint16_t t = 0; t < hasTail; ++t) {
LoadAlign(vreg_input_pre, preUb + i * d + dLoops * floatRepSize);
LoadAlign(vreg_input_cur, curUb + i * d + dLoops * floatRepSize);
Mul(vreg_mul, vreg_exp_max, vreg_input_pre, preg_tail_d);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
if constexpr (isUpdatePre) {
Muls(vreg_mul, vreg_mul, deScaleVPre, preg_all);
}
}
Add(vreg_add, vreg_mul, vreg_input_cur, preg_tail_d);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + dLoops * floatRepSize, vreg_add, preg_tail_d);
}
}
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t reduceSize, bool isUpdatePre>
__aicore__ inline void FlashUpdateGeneral(const LocalTensor<T>& dstTensor, const LocalTensor<T>& curTensor,
const LocalTensor<T>& preTensor, const LocalTensor<T>& expMaxTensor, const uint16_t m, const uint16_t d,
const float deScaleV, const float deScaleVPre)
{
__ubuf__ float * dstUb = (__ubuf__ T*)dstTensor.GetPhyAddr();
__ubuf__ float * curUb = (__ubuf__ T*)curTensor.GetPhyAddr();
__ubuf__ float * preUb = (__ubuf__ T*)preTensor.GetPhyAddr();
__ubuf__ float * expMaxUb = (__ubuf__ T*)expMaxTensor.GetPhyAddr();
constexpr uint16_t floatRepSize = 64;
const uint16_t tailD = d % floatRepSize;
uint32_t pltTailD = static_cast<uint32_t>(tailD);
uint16_t hasTail = 0;
if (tailD > 0) {
hasTail = 1;
}
FlashUpdateGeneralVF<T, INPUT_T, OUTPUT_T, reduceSize, isUpdatePre>(
dstUb, curUb, preUb, expMaxUb, m, d, deScaleV, deScaleVPre, pltTailD, hasTail);
}
/*
* @ingroup FlashUpdate
* @brief compute, dstTensor = preTensor * expMaxTensor + curTensor
* @param [out] dstTensor, output LocalTensor
* @param [in] curTensor, input LocalTensor
* @param [in] preTensor, input LocalTensor
* @param [in] expMaxTensor, input LocalTensor
* @param [in] m, input rows
* @param [in] d, input columns, should be 32 bytes aligned
*/
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t srcD, bool isUpdatePre, bool isMlaFullQuant>
__aicore__ inline void FlashUpdateNew(const LocalTensor<T>& dstTensor, const LocalTensor<T>& curTensor,
const LocalTensor<T>& preTensor, const LocalTensor<T>& expMaxTensor, const LocalTensor<T>& rowMaxTensor, const uint16_t m, const uint16_t d,
const float deScaleV, const float deScaleVPre)
{
static_assert(IsSameType<T, float>::value, "VF FlashUpdate, T must be float");
constexpr uint16_t floatRepSize = 64;
if constexpr(srcD % floatRepSize == 0) {
FlashUpdateBasic<T, INPUT_T, OUTPUT_T, srcD, REDUCE_SIZE, isUpdatePre, isMlaFullQuant>(dstTensor, curTensor, preTensor, expMaxTensor, rowMaxTensor,
m, d, deScaleV, deScaleVPre);
} else {
FlashUpdateGeneral<T, INPUT_T, OUTPUT_T, REDUCE_SIZE, isUpdatePre>(dstTensor, curTensor, preTensor, expMaxTensor, m, d,
deScaleV, deScaleVPre);
}
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t srcD, uint16_t reduceSize, bool isUpdatePre, bool isMlaFullQuant>
__simd_vf__ inline void FlashUpdateLastBasicVF(__ubuf__ float * dstUb, __ubuf__ float * curUb, __ubuf__ float * preUb,
__ubuf__ float * expMaxUb, __ubuf__ float * expSumUb, __ubuf__ float * rowMaxUb, const uint16_t m, const uint16_t d,
const float deScaleV, const float deScaleVPre)
{
RegTensor<float> vreg_exp_max;
RegTensor<float> vreg_row_max;
RegTensor<float> vreg_input_pre;
RegTensor<float> vreg_input_cur;
RegTensor<float> vreg_mul;
RegTensor<float> vreg_add;
RegTensor<float> vreg_div;
RegTensor<half> vreg_cast;
RegTensor<float> vreg_exp_sum;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
constexpr uint16_t floatRepSize = 64;
constexpr uint16_t dLoops = srcD / floatRepSize;
constexpr float fp8e4m3MaxValueRec = 1 / 448.0f;
constexpr float int8MaxValueRec = 1 / 127.0f;
constexpr float hifp8MaxValueRec = 1 / 32768.0f;
for (uint16_t i = 0; i < m; ++i) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_max, expMaxUb + i * reduceSize);
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_sum, expSumUb + i * reduceSize);
if constexpr (isMlaFullQuant) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_row_max, rowMaxUb + i * reduceSize);
}
for (uint16_t j = 0; j < dLoops; ++j) {
LoadAlign(vreg_input_pre, preUb + i * d + j * floatRepSize);
LoadAlign(vreg_input_cur, curUb + i * d + j * floatRepSize);
if constexpr (isMlaFullQuant) {
Mul(vreg_input_cur, vreg_input_cur, vreg_row_max, preg_all);
}
Mul(vreg_mul, vreg_exp_max, vreg_input_pre, preg_all);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
if constexpr (isUpdatePre) {
Muls(vreg_mul, vreg_mul, deScaleVPre, preg_all);
}
}
Add(vreg_add, vreg_mul, vreg_input_cur, preg_all);
Div(vreg_div, vreg_add, vreg_exp_sum, preg_all);
if constexpr (isMlaFullQuant) {
if constexpr (IsSameType<INPUT_T, fp8_e4m3fn_t>::value) {
Muls(vreg_div, vreg_div, fp8e4m3MaxValueRec, preg_all);
} else if constexpr (IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_div, vreg_div, int8MaxValueRec, preg_all);
} else {
Muls(vreg_div, vreg_div, hifp8MaxValueRec, preg_all);
}
}
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + j * floatRepSize, vreg_div, preg_all);
}
}
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t srcD, uint16_t reduceSize, bool isUpdatePre, bool isMlaFullQuant>
__aicore__ inline void FlashUpdateLastBasic(const LocalTensor<T>& dstTensor,
const LocalTensor<T>& curTensor, const LocalTensor<T>& preTensor,
const LocalTensor<T>& expMaxTensor, const LocalTensor<T>& rowMaxTensor, const LocalTensor<T>& expSumTensor,
const uint16_t m, const uint16_t d, const float deScaleV, const float deScaleVPre)
{
__ubuf__ float * dstUb = (__ubuf__ T*)dstTensor.GetPhyAddr();
__ubuf__ float * curUb = (__ubuf__ T*)curTensor.GetPhyAddr();
__ubuf__ float * preUb = (__ubuf__ T*)preTensor.GetPhyAddr();
__ubuf__ float * expMaxUb = (__ubuf__ T*)expMaxTensor.GetPhyAddr();
__ubuf__ float * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
__ubuf__ float * rowMaxUb = (__ubuf__ T*)rowMaxTensor.GetPhyAddr();
FlashUpdateLastBasicVF<T, INPUT_T, OUTPUT_T, srcD, reduceSize, isUpdatePre, isMlaFullQuant>(
dstUb, curUb, preUb, expMaxUb, expSumUb, rowMaxUb, m, d, deScaleV, deScaleVPre);
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t reduceSize, bool isUpdatePre>
__simd_vf__ inline void FlashUpdateLastGeneralVF(__ubuf__ float * dstUb, __ubuf__ float * curUb,
__ubuf__ float * preUb, __ubuf__ float * expMaxUb, __ubuf__ float * expSumUb, const uint16_t m, const uint16_t d,
const float deScaleV, const float deScaleVPre, const uint32_t pltTailD, const uint16_t hasTail)
{
RegTensor<float> vreg_exp_max;
RegTensor<float> vreg_input_pre;
RegTensor<float> vreg_input_cur;
RegTensor<float> vreg_mul;
RegTensor<float> vreg_add;
RegTensor<float> vreg_div;
RegTensor<half> vreg_cast;
RegTensor<float> vreg_exp_sum;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
uint32_t tmpTailD = pltTailD;
MaskReg preg_tail_d = UpdateMask<float>(tmpTailD);
constexpr uint16_t floatRepSize = 64;
uint16_t dLoops = d / floatRepSize;
for (uint16_t i = 0; i < m; ++i) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_max, expMaxUb + i * reduceSize);
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_sum, expSumUb + i * reduceSize);
for (uint16_t j = 0; j < dLoops; ++j) {
LoadAlign(vreg_input_pre, preUb + i * d + j * floatRepSize);
LoadAlign(vreg_input_cur, curUb + i * d + j * floatRepSize);
Mul(vreg_mul, vreg_exp_max, vreg_input_pre, preg_all);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
if constexpr (isUpdatePre) {
Muls(vreg_mul, vreg_mul, deScaleVPre, preg_all);
}
}
Add(vreg_add, vreg_mul, vreg_input_cur, preg_all);
Div(vreg_div, vreg_add, vreg_exp_sum, preg_all);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + j * floatRepSize, vreg_div, preg_all);
}
for (uint16_t t = 0; t < hasTail; ++t) {
LoadAlign(vreg_input_pre, preUb + i * d + dLoops * floatRepSize);
LoadAlign(vreg_input_cur, curUb + i * d + dLoops * floatRepSize);
Mul(vreg_mul, vreg_exp_max, vreg_input_pre, preg_tail_d);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
if constexpr (isUpdatePre) {
Muls(vreg_mul, vreg_mul, deScaleVPre, preg_all);
}
}
Add(vreg_add, vreg_mul, vreg_input_cur, preg_tail_d);
Div(vreg_div, vreg_add, vreg_exp_sum, preg_tail_d);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + dLoops * floatRepSize, vreg_div, preg_tail_d);
}
}
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t reduceSize, bool isUpdatePre>
__aicore__ inline void FlashUpdateLastGeneral(const LocalTensor<T>& dstTensor,
const LocalTensor<T>& curTensor, const LocalTensor<T>& preTensor,
const LocalTensor<T>& expMaxTensor, const LocalTensor<T>& expSumTensor,
const uint16_t m, const uint16_t d, const float deScaleV, const float deScaleVPre)
{
__ubuf__ float * dstUb = (__ubuf__ T*)dstTensor.GetPhyAddr();
__ubuf__ float * curUb = (__ubuf__ T*)curTensor.GetPhyAddr();
__ubuf__ float * preUb = (__ubuf__ T*)preTensor.GetPhyAddr();
__ubuf__ float * expMaxUb = (__ubuf__ T*)expMaxTensor.GetPhyAddr();
__ubuf__ float * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
constexpr uint16_t floatRepSize = 64;
uint16_t tailD = d % floatRepSize;
uint32_t pltTailD = tailD;
uint16_t hasTail = 0;
if (tailD > 0) {
hasTail = 1;
}
FlashUpdateLastGeneralVF<T, INPUT_T, OUTPUT_T, reduceSize, isUpdatePre>(
dstUb, curUb, preUb, expMaxUb, expSumUb, m, d, deScaleV, deScaleVPre, pltTailD, hasTail);
}
/*
* @ingroup FlashUpdateLast
* @brief compute, dstTensor = (preTensor * expMaxTensor + curTensor) / expSumTensor
* @param [out] dstTensor, output LocalTensor
* @param [in] curTensor, input LocalTensor
* @param [in] preTensor, input LocalTensor
* @param [in] expMaxTensor, input LocalTensor
* @param [in] expSumTensor, input LocalTensor
* @param [in] m, input rows
* @param [in] d, input columns, 32 bytes align
*/
template <typename T, typename INPUT_T, typename OUTPUT_T, uint16_t srcD, bool isUpdatePre, bool isMlaFullQuant>
__aicore__ inline void FlashUpdateLastNew(const LocalTensor<T>& dstTensor,
const LocalTensor<T>& curTensor, const LocalTensor<T>& preTensor,
const LocalTensor<T>& expMaxTensor, const LocalTensor<T>& rowMaxTensor, const LocalTensor<T>& expSumTensor,
uint16_t m, uint16_t d, const float deScaleV, const float deScaleVPre)
{
static_assert(IsSameType<T, float>::value, "VF FlashUpdateLast, T must be float");
constexpr uint16_t floatRepSize = 64;
if constexpr(srcD % floatRepSize == 0) {
FlashUpdateLastBasic<T, INPUT_T, OUTPUT_T, srcD, REDUCE_SIZE, isUpdatePre, isMlaFullQuant>(
dstTensor, curTensor, preTensor, expMaxTensor, rowMaxTensor, expSumTensor, m, d, deScaleV, deScaleVPre);
} else {
FlashUpdateLastGeneral<T, INPUT_T, OUTPUT_T, REDUCE_SIZE, isUpdatePre>(
dstTensor, curTensor, preTensor, expMaxTensor, expSumTensor, m, d, deScaleV, deScaleVPre);
}
}
template <typename T, typename INPUT_T, typename OUTPUT_T, uint32_t srcD, bool isMlaFullQuant>
__simd_vf__ inline void LastDivNewVF(__ubuf__ float * dstUb, __ubuf__ float * curUb, __ubuf__ float * expSumUb,
const uint16_t m, const uint16_t d, const float deScaleV)
{
RegTensor<float> vreg_input_cur;
RegTensor<float> vreg_div;
RegTensor<float> vreg_exp_sum;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
constexpr uint16_t floatRepSize = 64;
const uint16_t dLoops = d >> 6;
constexpr float fp8e4m3MaxValueRec = 1 / 448.0f;
constexpr float int8MaxValueRec = 1 / 127.0f;
constexpr float hifp8MaxValueRec = 1 / 32768.0f;
for (uint16_t i = 0; i < m; ++i) {
uint32_t sreg_init = d;
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_exp_sum, expSumUb + i * REDUCE_SIZE);
for (uint16_t j = 0; j < dLoops; ++j) {
MaskReg preg_update = UpdateMask<float>(sreg_init);
LoadAlign(vreg_input_cur, curUb + i * d + j * floatRepSize);
if constexpr (IsSameType<INPUT_T, fp8_e5m2_t>::value ||
IsSameType<INPUT_T, fp8_e4m3fn_t>::value ||
IsSameType<INPUT_T, hifloat8_t>::value ||
IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_input_cur, vreg_input_cur, deScaleV, preg_all);
}
Div(vreg_div, vreg_input_cur, vreg_exp_sum, preg_update);
if constexpr (isMlaFullQuant) {
if constexpr (IsSameType<INPUT_T, fp8_e4m3fn_t>::value) {
Muls(vreg_div, vreg_div, fp8e4m3MaxValueRec, preg_all);
} else if constexpr (IsSameType<INPUT_T, int8_t>::value) {
Muls(vreg_div, vreg_div, int8MaxValueRec, preg_all);
} else {
Muls(vreg_div, vreg_div, hifp8MaxValueRec, preg_all);
}
}
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + j * floatRepSize, vreg_div, preg_update);
}
}
}
// dstTensor = curTensor / expSumTensor, curTensor: [64,128], expSumTensor: [64,8]
template <typename T, typename INPUT_T, typename OUTPUT_T, uint32_t srcD, bool isMlaFullQuant>
__aicore__ inline void LastDivNew(const LocalTensor<T>& dstTensor, const LocalTensor<T>& curTensor,
const LocalTensor<T>& expSumTensor, const uint16_t m, const uint16_t d, const float deScaleV)
{
__ubuf__ float * dstUb = (__ubuf__ T*)dstTensor.GetPhyAddr();
__ubuf__ float * curUb = (__ubuf__ T*)curTensor.GetPhyAddr();
__ubuf__ float * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
LastDivNewVF<T, INPUT_T, OUTPUT_T, srcD, isMlaFullQuant>(dstUb, curUb, expSumUb, m, d, deScaleV);
}
template <typename T, uint32_t srcD>
__simd_vf__ inline void InvalidLineUpdateVF(__ubuf__ T * dstUb, __ubuf__ T * srcUb, __ubuf__ T * maxUb,
const uint16_t m, const uint16_t d, const T minValue, const T invalidValue)
{
RegTensor<float> vreg_invalid_value;
RegTensor<float> vreg_max;
RegTensor<float> vreg_input;
RegTensor<float> vreg_input_brc;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
MaskReg preg_compare;
const uint16_t dLoops = d >> 6;
Duplicate(vreg_invalid_value, invalidValue);
for (uint16_t i = 0; i < m; ++i) {
LoadAlign<T, MicroAPI::LoadDist::DIST_BRC_B32>(vreg_max, maxUb + i);
Compares<T, CMPMODE::EQ>(preg_compare, vreg_max, minValue, preg_all);
for (uint16_t j = 0; j < dLoops; ++j) {
LoadAlign(vreg_input, srcUb + i * d + j * floatRepSize);
Select(vreg_input_brc, vreg_invalid_value, vreg_input, preg_compare);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)dstUb + i * d + j * floatRepSize, vreg_input_brc, preg_all);
}
}
}
template <typename T, uint32_t srcD>
__aicore__ inline void InvalidLineUpdate(const LocalTensor<T>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& maxTensor, const uint16_t m, const uint16_t d, const T minValue, const T invalidValue)
{
__ubuf__ T * dstUb = (__ubuf__ T*)dstTensor.GetPhyAddr();
__ubuf__ T * srcUb = (__ubuf__ T*)srcTensor.GetPhyAddr();
__ubuf__ T * maxUb = (__ubuf__ T*)maxTensor.GetPhyAddr();
constexpr uint16_t floatRepSize = 64;
uint16_t dLoops = d >> 6;
InvalidLineUpdateVF<T, srcD>(dstUb, srcUb, maxUb, m, d, minValue, invalidValue);
}
template <typename T>
__simd_vf__ inline void ComputeLseOutputVF(__ubuf__ T *srcSumUb, __ubuf__ T *srcMaxUb, __ubuf__ T *dstUb, const uint32_t dealCount)
{
MicroAPI::RegTensor<T> vregSum;
MicroAPI::RegTensor<T> vregMax;
MicroAPI::RegTensor<T> vregRes;
MicroAPI::RegTensor<T> vregResFinal;
MicroAPI::RegTensor<float> vregMinValue;
MicroAPI::RegTensor<float> vregInfValue;
MicroAPI::MaskReg pregCompare;
constexpr uint32_t dealRows = 8;
constexpr uint32_t floatRepSize = 64; // 64: 一个寄存器存64个float
constexpr float infValue = 3e+99; // 3e+99 for float inf
constexpr uint32_t tmpMin = 0xFF7FFFFF;
float minValue = *((float*)&tmpMin);
uint16_t updateLoops = dealCount / dealRows;
uint16_t tailLSize = dealCount % dealRows * 8;
uint32_t pltTail = static_cast<uint32_t>(tailLSize);
MicroAPI::MaskReg pregAll = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg pregTail = MicroAPI::UpdateMask<T>(pltTail);
MicroAPI::Duplicate<float, float>(vregMinValue, minValue);
MicroAPI::Duplicate<float, float>(vregInfValue, infValue);
for (uint16_t i = 0; i < updateLoops; ++i) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_E2B_B32>(vregSum, srcSumUb + (i * dealRows));
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_E2B_B32>(vregMax, srcMaxUb + (i * dealRows));
MicroAPI::Log<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregSum, pregAll);
MicroAPI::Add<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregRes, vregMax, pregAll);
MicroAPI::Compare<float, CMPMODE::EQ>(pregCompare, vregMax, vregMinValue, pregAll);
MicroAPI::Select<T>(vregResFinal, vregInfValue, vregRes, pregCompare);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(dstUb + (i * floatRepSize), vregResFinal, pregAll);
}
if (tailLSize != 0) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_E2B_B32>(vregSum, srcSumUb + dealRows * updateLoops);
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_E2B_B32>(vregMax, srcMaxUb + dealRows * updateLoops);
MicroAPI::Log<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregSum, pregTail);
MicroAPI::Add<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregRes, vregMax, pregTail);
MicroAPI::Compare<float, CMPMODE::EQ>(pregCompare, vregMax, vregMinValue, pregTail);
MicroAPI::Select<T>(vregResFinal, vregInfValue, vregRes, pregCompare);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(dstUb + floatRepSize * updateLoops, vregResFinal, pregTail);
}
}
template <typename T>
__aicore__ inline void ComputeLseOutputVF(const LocalTensor<T>& dstTensor, const LocalTensor<T>& softmaxSumTensor,
const LocalTensor<T>& softmaxMaxTensor, uint32_t dealCount)
{
__ubuf__ T * srcSumUb = (__ubuf__ T *)softmaxSumTensor.GetPhyAddr();
__ubuf__ T * srcMaxUb = (__ubuf__ T *)softmaxMaxTensor.GetPhyAddr();
__ubuf__ T * dstUb = (__ubuf__ T *)dstTensor.GetPhyAddr();
ComputeLseOutputVF<T>(srcSumUb, srcMaxUb, dstUb, dealCount);
}
template <typename T>
__simd_vf__ inline void SinkSubExpAddVF(__ubuf__ T *srcSumUb, __ubuf__ T *srcMaxUb, const T sinkValue, const uint32_t dealCount)
{
MicroAPI::RegTensor<T> vregSum;
MicroAPI::RegTensor<T> vregMax;
MicroAPI::RegTensor<T> vregRes;
MicroAPI::RegTensor<T> vregSink;
constexpr uint32_t floatRepSize = 64;
uint16_t updateLoops = dealCount / floatRepSize;
uint16_t tailSize = dealCount % floatRepSize;
uint32_t pltTail = static_cast<uint32_t>(tailSize);
//mask
MicroAPI::MaskReg pregAll = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg pregTail = MicroAPI::UpdateMask<T>(pltTail);
Duplicate(vregSink, sinkValue);
for (uint16_t i = 0; i < updateLoops; ++i) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregSum, srcSumUb + (i * floatRepSize));
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregMax, srcMaxUb + (i * floatRepSize));
MicroAPI::Sub<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregSink, vregMax, pregAll);
MicroAPI::Exp<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregRes, pregAll);
MicroAPI::Add<T, MicroAPI::MaskMergeMode::ZEROING>(vregSum, vregSum, vregRes, pregAll);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(srcSumUb + (i * floatRepSize), vregSum, pregAll);
}
for (uint16_t i = 0; i < tailSize; i = i + tailSize) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregSum, srcSumUb + (updateLoops * floatRepSize));
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregMax, srcMaxUb + (updateLoops * floatRepSize));
MicroAPI::Sub<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregSink, vregMax, pregTail);
MicroAPI::Exp<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregRes, pregTail);
MicroAPI::Add<T, MicroAPI::MaskMergeMode::ZEROING>(vregSum, vregSum, vregRes, pregTail);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(srcSumUb + (updateLoops * floatRepSize), vregSum, pregTail);
}
}
template <typename T>
__aicore__ inline void SinkSubExpAddVF(const LocalTensor<T>& softmaxSumTensor, const LocalTensor<T>& softmaxMaxTensor,
const T sinkValue, uint32_t dealCount)
{
__ubuf__ T * srcSumUb = (__ubuf__ T *)softmaxSumTensor.GetPhyAddr();
__ubuf__ T * srcMaxUb = (__ubuf__ T *)softmaxMaxTensor.GetPhyAddr();
SinkSubExpAddVF<T>(srcSumUb, srcMaxUb, sinkValue, dealCount);
}
template <typename T, typename SINK_T>
__simd_vf__ inline void SinkSubExpAddGSFusedVF(__ubuf__ T *srcSumUb, __ubuf__ T *srcMaxUb, __ubuf__ uint16_t *sinkUb, const uint32_t dealCount)
{
MicroAPI::RegTensor<T> vregSum;
MicroAPI::RegTensor<T> vregMax;
MicroAPI::RegTensor<T> vregRes;
MicroAPI::RegTensor<SINK_T> vregSink;
MicroAPI::RegTensor<T> vregSinkCast;
constexpr uint32_t floatRepSize = 64;
uint16_t updateLoops = dealCount / floatRepSize;
uint16_t tailSize = dealCount % floatRepSize;
uint32_t pltTail = static_cast<uint32_t>(tailSize);
//mask
MicroAPI::MaskReg pregAll = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg pregTail = MicroAPI::UpdateMask<T>(pltTail);
MicroAPI::MaskReg pregSinkAll = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::LoadAlign<uint16_t, MicroAPI::LoadDist::DIST_UNPACK_B16>((MicroAPI::RegTensor<uint16_t>&)vregSink, sinkUb);
MicroAPI::Cast<T, SINK_T, castTraitFp16_32_update>(vregSinkCast, vregSink, pregSinkAll);
for (uint16_t i = 0; i < updateLoops; ++i) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregSum, srcSumUb + (i * floatRepSize));
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregMax, srcMaxUb + (i * floatRepSize));
MicroAPI::Sub<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregSinkCast, vregMax, pregAll);
MicroAPI::Exp<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregRes, pregAll);
MicroAPI::Add<T, MicroAPI::MaskMergeMode::ZEROING>(vregSum, vregSum, vregRes, pregAll);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(srcSumUb + (i * floatRepSize), vregSum, pregAll);
}
if (tailSize != 0) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregSum, srcSumUb + (updateLoops * floatRepSize));
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregMax, srcMaxUb + (updateLoops * floatRepSize));
MicroAPI::Sub<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregSinkCast, vregMax, pregTail);
MicroAPI::Exp<T, MicroAPI::MaskMergeMode::ZEROING>(vregRes, vregRes, pregTail);
MicroAPI::Add<T, MicroAPI::MaskMergeMode::ZEROING>(vregSum, vregSum, vregRes, pregTail);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(srcSumUb + (updateLoops * floatRepSize), vregSum, pregTail);
}
}
template <typename T, typename SINK_T>
__aicore__ inline void SinkSubExpAddGSFusedVF(const LocalTensor<SINK_T>& dstTensor, const LocalTensor<T>& softmaxSumTensor,
const LocalTensor<T>& softmaxMaxTensor, uint32_t dealCount)
{
__ubuf__ T * srcSumUb = (__ubuf__ T *)softmaxSumTensor.GetPhyAddr();
__ubuf__ T * srcMaxUb = (__ubuf__ T *)softmaxMaxTensor.GetPhyAddr();
__ubuf__ uint16_t * dstUb = (__ubuf__ uint16_t *)dstTensor.GetPhyAddr();
SinkSubExpAddGSFusedVF<T, SINK_T>(srcSumUb, srcMaxUb, dstUb, dealCount);
}
template <typename T>
__simd_vf__ inline void RowInvalidUpdateVF(__ubuf__ T *finalUb, __ubuf__ float *maxUb, const uint16_t m,
const uint16_t d, int64_t dSize, const uint32_t pltTailD, const uint16_t hasTail)
{
constexpr uint16_t floatRepSize = 64; // 64: 一个寄存器可以存储64个float类型数据
const uint16_t dLoops = d / floatRepSize;
constexpr uint32_t tmpZero = 0x00000000; // zero value of fp16 and fp32
const T zeroValue = *((T*)&tmpZero);
constexpr uint32_t tmpMin = 0xFF7FFFFF; // min value of float
const float minValue = *((float*)&tmpMin);
MicroAPI::RegTensor<float> vregMinValue;
MicroAPI::RegTensor<T> vregZeroValue;
MicroAPI::RegTensor<float> vregMax;
MicroAPI::RegTensor<T> vregFinal;
MicroAPI::RegTensor<T> vregFinalNew;
MicroAPI::MaskReg pregAll = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
uint32_t tmpTailD = pltTailD;
MicroAPI::MaskReg pregTailD = MicroAPI::UpdateMask<T>(tmpTailD);
MicroAPI::MaskReg pregCompare;
MicroAPI::Duplicate<float, float>(vregMinValue, minValue);
MicroAPI::Duplicate<T, T>(vregZeroValue, zeroValue);
for (uint16_t i = 0; i < m; ++i) {
MicroAPI::LoadAlign<float, MicroAPI::LoadDist::DIST_BRC_B32>(vregMax, maxUb + i);
MicroAPI::Compare<float, CMPMODE::EQ>(pregCompare, vregMax, vregMinValue, pregAll);
for (uint16_t j = 0; j < dLoops; ++j) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregFinal, finalUb + i * dSize + j * floatRepSize);
MicroAPI::Select<T>(vregFinalNew, vregZeroValue, vregFinal, pregCompare);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(finalUb + i * dSize + j * floatRepSize,
vregFinalNew, pregAll);
}
for (uint16_t t = 0; t < hasTail; ++t) {
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregFinal, finalUb + i * dSize + dLoops * floatRepSize);
MicroAPI::Select<T>(vregFinalNew, vregZeroValue, vregFinal, pregCompare);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(finalUb + i * dSize + dLoops * floatRepSize,
vregFinalNew, pregTailD);
}
}
}
template <typename T>
__aicore__ inline void RowInvalidUpdateVF(const LocalTensor<T>& finalTensor, const LocalTensor<float>& maxTensor,
const uint16_t m, const uint16_t d, int64_t dSize)
{
__ubuf__ T * finalUb = (__ubuf__ T*)finalTensor.GetPhyAddr();
__ubuf__ float * maxUb = (__ubuf__ float*)maxTensor.GetPhyAddr();
constexpr uint16_t floatRepSize = 64;
const uint16_t tailD = d % floatRepSize;
uint32_t pltTailD = static_cast<uint32_t>(tailD);
uint16_t hasTail = 0;
if (tailD > 0) {
hasTail = 1;
}
RowInvalidUpdateVF<T>(finalUb, maxUb, m, d, dSize, pltTailD, hasTail);
}
} // namespace
#endif // MY_FLASH_UPDATE_INTERFACE_H

View File

@@ -0,0 +1,164 @@
/**
 * 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 vf_mul_sel_softmaxflashv2_cast_nz_sfa.h
* \brief
*/
#ifndef MUL_SEL_SOFTMAX_FLASH_V2_CAST_NZ_SFA_INTERFACE_H
#define MUL_SEL_SOFTMAX_FLASH_V2_CAST_NZ_SFA_INTERFACE_H
#include "vf_basic_block_aligned128_no_update_sfa.h"
#include "vf_basic_block_aligned128_update_sfa.h"
#include "vf_basic_block_unaligned64_update_sfa.h"
#include "vf_basic_block_unaligned64_no_update_sfa.h"
#include "vf_basic_block_unaligned128_no_update_sfa.h"
#include "vf_basic_block_unaligned128_update_sfa.h"
using namespace regbaseutil;
namespace FaVectorApi {
/* **************************************************************************************************
* Muls + Select(optional) + SoftmaxFlashV2 + Cast(fp32->fp16/bf16) + ND2NZ
* ************************************************************************************************* */
using AscendC::LocalTensor;
enum class OriginNRange {
EQ_128_SFA = 0, // originN == 128, better performance than GT_64_AND_LTE_128 (s2BaseSize=128)
GT_0_AND_LTE_64_SFA, // 0 < originN <= 64 (s2BaseSize <= 64 or tail s2)
GT_64_AND_LTE_128_SFA, // 64 < originN <= 128, support for non-alignment (s2BaseSize=128)
N_INVALID_SFA
};
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128,
OriginNRange oriNRange = OriginNRange::EQ_128_SFA>
__aicore__ inline void ProcessVec1NoUpdate(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
if constexpr (oriNRange == OriginNRange::EQ_128_SFA) {
ProcessVec1NoUpdateImpl128<T, T2, s1BaseSize, s2BaseSize>(
dstTensor, srcTensor, expSumTensor, maxTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
} else if constexpr (oriNRange == OriginNRange::GT_0_AND_LTE_64_SFA) {
ProcessVec1NoUpdateImpl64<T, T2, s1BaseSize, s2BaseSize>(
dstTensor, srcTensor, expSumTensor, maxTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
} else if constexpr (oriNRange == OriginNRange::GT_64_AND_LTE_128_SFA) {
ProcessVec1NoUpdateGeneralImpl128<T, T2, s1BaseSize, s2BaseSize>(
dstTensor, srcTensor, expSumTensor, maxTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
}
}
template <typename T, typename T2, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128,
OriginNRange oriNRange = OriginNRange::EQ_128_SFA>
__aicore__ inline void ProcessVec1Update(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
if constexpr (oriNRange == OriginNRange::EQ_128_SFA) {
ProcessVec1UpdateImpl128<T, T2, s1BaseSize, s2BaseSize>(
dstTensor, srcTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
} else if constexpr (oriNRange == OriginNRange::GT_0_AND_LTE_64_SFA) {
ProcessVec1UpdateImpl64<T, T2, s1BaseSize, s2BaseSize>(
dstTensor, srcTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
} else if constexpr (oriNRange == OriginNRange::GT_64_AND_LTE_128_SFA) {
ProcessVec1UpdateGeneralImpl128<T, T2, s1BaseSize, s2BaseSize>(
dstTensor, srcTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
}
}
template <typename T, typename T2, bool isUpdate = false, uint32_t s1BaseSize = 64, uint32_t s2BaseSize = 128,
OriginNRange oriNRange = OriginNRange::EQ_128_SFA>
__aicore__ inline void ProcessVec1Vf(
const LocalTensor<T2>& dstTensor, const LocalTensor<T>& srcTensor,
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor, const LocalTensor<T>& inMaxTensor,
const LocalTensor<T>& sharedTmpBuffer, const uint16_t m, const uint32_t originN, const T scale, const T minValue)
{
static_assert(IsSameType<T, float>::value, "VF mul_sel_softmaxflashv2_cast_nz, T must be float");
static_assert((IsSameType<T2, half>::value || IsSameType<T2, bfloat16_t>::value),
"VF mul_sel_softmaxflashv2_cast_nz, T2 must be half or bfloat16");
if constexpr (!isUpdate) {
ProcessVec1NoUpdate<T, T2, s1BaseSize, s2BaseSize, oriNRange>(
dstTensor, srcTensor, expSumTensor, maxTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
} else {
ProcessVec1Update<T, T2, s1BaseSize, s2BaseSize, oriNRange>(
dstTensor, srcTensor, expSumTensor, maxTensor, inMaxTensor, sharedTmpBuffer, m, originN, scale, minValue);
}
}
template <typename T>
__simd_vf__ inline void UpdateExpSumAndExpMaxVF(__ubuf__ T * maxUb, __ubuf__ T * inMaxUb, __ubuf__ T * expMaxUb,
__ubuf__ T * expSumUb, __ubuf__ T * inExpSumUb, __ubuf__ T * tmpExpSumUb, __ubuf__ T * tmpMaxUb, const uint32_t m)
{
RegTensor<float> vreg_input_x;
RegTensor<float> vreg_input_x_unroll;
RegTensor<float> vreg_max;
RegTensor<float> vreg_in_max;
RegTensor<float> vreg_exp_sum;
RegTensor<float> vreg_in_exp_sum;
RegTensor<float> vreg_exp_max;
RegTensor<float> vreg_exp_sum_brc;
RegTensor<float> vreg_exp_sum_update;
MaskReg preg_all = CreateMask<float, MaskPattern::ALL>();
// 注意当m大于64的时候需要开启循环
LoadAlign(vreg_max, tmpMaxUb);
LoadAlign(vreg_in_max, inMaxUb);
FusedExpSub(vreg_exp_max, vreg_in_max, vreg_max, preg_all);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)expMaxUb, vreg_exp_max, preg_all);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)maxUb, vreg_max, preg_all);
LoadAlign(vreg_in_exp_sum, inExpSumUb);
// x_sum = exp_max * insum + x_sum
LoadAlign(vreg_exp_sum_brc, tmpExpSumUb);
Mul(vreg_exp_sum_update, vreg_exp_max, vreg_in_exp_sum, preg_all);
Add(vreg_exp_sum_update, vreg_exp_sum_update, vreg_exp_sum_brc, preg_all);
StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(
(__ubuf__ T *&)expSumUb, vreg_exp_sum_update, preg_all);
}
template <typename T>
__aicore__ inline void SFAUpdateExpSumAndExpMax(
const LocalTensor<T>& expSumTensor, const LocalTensor<T>& maxTensor,
const LocalTensor<T>& expMaxTensor, const LocalTensor<T>& inExpSumTensor,
const LocalTensor<T>& inMaxTensor, const LocalTensor<T>& sharedTmpBuffer, const uint32_t m)
{
__ubuf__ T * maxUb = (__ubuf__ T*)maxTensor.GetPhyAddr();
__ubuf__ T * inMaxUb = (__ubuf__ T*)inMaxTensor.GetPhyAddr();
__ubuf__ T * expMaxUb = (__ubuf__ T*)expMaxTensor.GetPhyAddr();
__ubuf__ T * expSumUb = (__ubuf__ T*)expSumTensor.GetPhyAddr();
__ubuf__ T * inExpSumUb = (__ubuf__ T*)inExpSumTensor.GetPhyAddr();
__ubuf__ T * tmpExpSumUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr();
__ubuf__ T * tmpMaxUb = (__ubuf__ T*)sharedTmpBuffer.GetPhyAddr() + 64;
UpdateExpSumAndExpMaxVF<T>(maxUb, inMaxUb, expMaxUb, expSumUb, inExpSumUb, tmpExpSumUb, tmpMaxUb, m);
}
template <typename T>
__simd_vf__ inline void DuplicateSumWithR0VF(__ubuf__ T * sumUb, const T R0, uint32_t m) {
AscendC::MicroAPI::RegTensor<T> vreg_sum;
AscendC::MicroAPI::MaskReg preg_m = AscendC::MicroAPI::UpdateMask<T>(m);
AscendC::MicroAPI::UnalignRegForStore ureg;
AscendC::MicroAPI::Duplicate<T, MicroAPI::MaskMergeMode::ZEROING, T>(vreg_sum, R0, preg_m);
AscendC::MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM_B32>(sumUb, vreg_sum, preg_m);
}
template <typename T>
__aicore__ inline void DuplicateSumWithR0(const LocalTensor<T>& sumTensor, const T R0, uint32_t m)
{
__ubuf__ T * sumUb = (__ubuf__ T*)sumTensor.GetPhyAddr();
DuplicateSumWithR0VF<T>(sumUb, R0, m);
}
} // namespace
#endif // MUL_SEL_SOFTMAX_FLASH_V2_CAST_NZ_SFA_INTERFACE_H

View File

@@ -0,0 +1,292 @@
/**
 * 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 buffer.h
* \brief同步管理
*/
#ifndef BUFFER_H
#define BUFFER_H
#include<type_traits>
#include"lib/matmul_intf.h"
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_basic_intf.h"
#else
#include "kernel_operator.h"
#endif
using namespace AscendC;
namespace fa_base_matmul {
__BLOCK_LOCAL__ __inline__ uint32_t idCounterNum;
#define MAKE_ID ((++idCounterNum) % 11)
// 核间同步中AIC(flagId 0-10)对应AIV0(flagId 0-10)对应AIV1(flagId 16-26)
#define AIV0_AIV1_OFFSET 16
enum class BufferType {
L1 = 0,
L0A = 1,
L0B = 2,
L0C = 3,
UB = 4,
GM = 5,
C2 = 6,
};
enum class SyncType {
NO_SYNC,
INNER_CORE_SYNC,
CROSS_CORE_SYNC_FORWARD,
CROSS_CORE_SYNC_BOTH,
CROSS_CORE_SYNC_BACKWARD,
};
constexpr uint32_t INVALID_CROSS_CORE_EVENT_ID = 16;
static constexpr uint64_t CROSS_CORE_SYNC_MODE = 4;
template<BufferType Type>
struct BufferInfo{
// Cons 消费者Prod 生产者
__aicore__ const static constexpr HardEvent ConsWaitProdStatus() {
if constexpr (Type == BufferType::L1) {
return HardEvent::MTE2_MTE1;
} else if constexpr (Type == BufferType::L0A) {
return HardEvent::MTE1_M;
} else if constexpr (Type == BufferType::L0B) {
return HardEvent::MTE1_M;
} else if constexpr (Type == BufferType::L0C) {
return HardEvent::M_FIX;
} else if constexpr (Type == BufferType::C2) {
return HardEvent::MTE1_M;
}
}
__aicore__ const static constexpr HardEvent ProdWaitConsStatus() {
if constexpr (Type == BufferType::L1) {
return HardEvent::MTE1_MTE2;
} else if constexpr (Type == BufferType::L0A) {
return HardEvent::M_MTE1;
} else if constexpr (Type == BufferType::L0B) {
return HardEvent::M_MTE1;
} else if constexpr (Type == BufferType::L0C) {
return HardEvent::FIX_M;
} else if constexpr (Type == BufferType::C2) {
return HardEvent::M_MTE1;
}
}
__aicore__ const static constexpr TPosition GetTPosition() {
if constexpr (Type == BufferType::L1) {
return TPosition::A1;
} else if constexpr (Type == BufferType::L0A) {
return TPosition::A2;
} else if constexpr (Type == BufferType::L0B) {
return TPosition::B2;
} else if constexpr (Type == BufferType::L0C) {
return TPosition::CO1;
} else if constexpr (Type == BufferType::UB) {
return TPosition::VECIN;
} else if constexpr (Type == BufferType::GM) {
return TPosition::GM;
} else if constexpr (Type == BufferType::C2) {
return TPosition::C2;
}
}
static constexpr HardEvent EventP2C = ConsWaitProdStatus(); // 生产者到消费者方向的HardEvent消费者等生产者提供/生产者通知消费者已生成
static constexpr HardEvent EventC2P = ProdWaitConsStatus(); // 消费者到生产者方向的HardEvent生产者等消费者消耗/消费者通知生产者已消耗’
static constexpr TPosition Position = GetTPosition();
};
// buffer绑定生产者、消费者关系
// L1 buffer的生产者为MTE2或者MTE3消费者为MTE1
// L0A buffer的生产者为MTE1消费者为M
// L0B buffer的生产者为MTE1消费者为M
// L0C buffer的生产者为M消费者为FIX
template<BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class Buffer {
using TensorType = std::conditional_t<bufferType == BufferType::GM, GlobalTensor<uint8_t>, LocalTensor<uint8_t>>;
template <typename T>
using TargetTensorType = std::conditional_t<bufferType == BufferType::GM, GlobalTensor<T>, LocalTensor<T>>;
public:
__aicore__ inline Buffer() {}
__aicore__ inline Buffer(TensorType tensor, uint32_t size) {
tensor_ = tensor;
size_ = size;
if constexpr (syncType == SyncType::CROSS_CORE_SYNC_FORWARD) {
id0_ = MAKE_ID;
id1_ = INVALID_CROSS_CORE_EVENT_ID;
} else if constexpr (syncType == SyncType::CROSS_CORE_SYNC_BACKWARD) {
id0_ = INVALID_CROSS_CORE_EVENT_ID;
id1_ = MAKE_ID;
} else if constexpr (syncType == SyncType::CROSS_CORE_SYNC_BOTH) {
id0_ = MAKE_ID;
id1_ = MAKE_ID;
} else {
id0_ = INVALID_CROSS_CORE_EVENT_ID;
id1_ = INVALID_CROSS_CORE_EVENT_ID;
}
}
__aicore__ inline void Init() {
if ASCEND_IS_AIC {
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
p2cEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventP2C>(); // 确保只能被调用一次
c2pEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventC2P>();
SetFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_);
}
}
}
__aicore__ inline void UnInit() {
if ASCEND_IS_AIC {
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
WaitFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_);
GetTPipePtr()->ReleaseEventID<BufferInfo<bufferType>::EventP2C>(p2cEventId_); // 确保只能被调用一次
GetTPipePtr()->ReleaseEventID<BufferInfo<bufferType>::EventC2P>(c2pEventId_);
}
}
}
template<HardEvent EventType>
__aicore__ inline void Wait() {
if ASCEND_IS_AIC {
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
if constexpr (EventType == BufferInfo<bufferType>::EventP2C) {
WaitFlag<BufferInfo<bufferType>::EventP2C>(p2cEventId_); // 消费者等待生产者完成生产
} else {
WaitFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_); // 生产者等待消费者完成消费
}
}
}
}
template<HardEvent EventType>
__aicore__ inline void Set() {
if ASCEND_IS_AIC {
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
if constexpr (EventType == BufferInfo<bufferType>::EventP2C) {
SetFlag<BufferInfo<bufferType>::EventP2C>(p2cEventId_); // 生产者通知消费者已完成生产
} else {
SetFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_); // 消费者通知生产者已完成消费
}
}
}
}
__aicore__ inline void SetEventID() {
if ASCEND_IS_AIC {
p2cEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventP2C>(); // 确保只能被调用一次
c2pEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventC2P>();
}
}
template<HardEvent EventType>
__aicore__ inline TEventID GetEventID() {
if ASCEND_IS_AIC {
if constexpr (EventType == BufferInfo<bufferType>::EventP2C) {
return p2cEventId_; // 生产者通知消费者已完成生产
} else {
return c2pEventId_; // 消费者通知生产者已完成消费
}
}
}
template<bool isReuse = false>
__aicore__ inline void WaitCrossCore() {
if constexpr (bufferType == BufferType::GM && syncType == SyncType::CROSS_CORE_SYNC_BACKWARD) {
// AIC属于消费者AIV属于生产者且一个AIC对应两个AIV
if ASCEND_IS_AIC {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE2>(id1_);
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE2>(id1_ + AIV0_AIV1_OFFSET);
} else {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE2>(id0_);
}
} else if constexpr (bufferType == BufferType::UB || bufferType == BufferType::GM) {
// AIC属于生产者AIV属于消费者且一个AIC对应两个AIV
if ASCEND_IS_AIC {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_FIX>(id1_);
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_FIX>(id1_ + AIV0_AIV1_OFFSET);
} else {
if constexpr (isReuse) {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE3>(id0_);
} else {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_V>(id0_);
}
}
} else if constexpr (bufferType == BufferType::L1) {
// AIC属于消费者AIV属于生产者且一个AIC对应两个AIV
if ASCEND_IS_AIC {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE1>(id0_);
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE1>(id0_ + AIV0_AIV1_OFFSET);
} else {
if constexpr (syncType == SyncType::CROSS_CORE_SYNC_BOTH) {
CrossCoreWaitFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE3>(id1_);
}
}
}
}
template<bool isReuse = false>
__aicore__ inline void SetCrossCore() {
if constexpr (bufferType == BufferType::GM && syncType == SyncType::CROSS_CORE_SYNC_BACKWARD) {
// AIC属于消费者AIV属于生产者且一个AIC对应两个AIV
if ASCEND_IS_AIC {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_FIX>(id0_);
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_FIX>(id0_ + AIV0_AIV1_OFFSET);
} else {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE3>(id1_);
}
} else if constexpr (bufferType == BufferType::UB || bufferType == BufferType::GM) {
// AIC属于生产者AIV属于消费者且一个AIC对应两个AIV
if ASCEND_IS_AIC {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_FIX>(id0_);
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_FIX>(id0_ + AIV0_AIV1_OFFSET);
} else {
if constexpr (isReuse) {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE3>(id1_);
} else {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_V>(id1_);
}
}
} else if constexpr (bufferType == BufferType::L1) {
// AIC属于消费者AIV属于生产者且一个AIC对应两个AIV
if ASCEND_IS_AIC {
if constexpr (syncType == SyncType::CROSS_CORE_SYNC_BOTH) {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE1>(id1_);
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE1>(id1_ + AIV0_AIV1_OFFSET);
}
} else {
CrossCoreSetFlag<CROSS_CORE_SYNC_MODE, PIPE_MTE3>(id0_);
}
}
}
template<typename T>
__aicore__ inline TargetTensorType<T> GetTensor() {
return tensor_.template ReinterpretCast<T>();
}
template<typename T>
__aicore__ inline TargetTensorType<T> GetTensor(uint64_t startindex) {
TargetTensorType<T> tmpTensor = tensor_.template ReinterpretCast<T>();
return tmpTensor[startindex];
}
private:
TensorType tensor_;
uint32_t size_;
TEventID p2cEventId_;
TEventID c2pEventId_;
uint32_t id0_; // 用作正向同步:生产者通知消费者,或者消费者等待生产者;
uint32_t id1_; // 用作反向同步:消费者通知生产者,或者生产者等待消费者;
};
}
#endif

View File

@@ -0,0 +1,61 @@
/**
 * 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 buffer_manager.h
* \brief buffer内存管理
*/
#ifndef BUFFER_MANAGER_H
#define BUFFER_MANAGER_H
#if (__NPU_ARCH__ == 5102)
#include "buffer_mix_core.h"
#else
#include "buffer.h"
#endif
// L1 TPosition::A1
// L0A TPosition::A2
// L0B TPosition::B2
// L0C TPosition::CO1
// UB TPosition::VECIN
namespace fa_base_matmul {
template<BufferType bufferType>
class BufferManager {
using TensorType = std::conditional_t<bufferType == BufferType::GM, GlobalTensor<uint8_t>, LocalTensor<uint8_t>>;
public:
__aicore__ inline void Init(TPipe *pipe, uint32_t size) {
static_assert(bufferType != BufferType::GM, "GM should use workspace.");
TBuf<BufferInfo<bufferType>::Position> tbuf;
pipe->InitBuffer(tbuf, size);
mem_ = tbuf.template Get<uint8_t>();
}
__aicore__ inline void Init(__gm__ uint8_t* workspace) {
static_assert(bufferType == BufferType::GM, "BufferType should be GM.");
mem_.SetGlobalBuffer((__gm__ uint8_t*)workspace);
}
template<SyncType syncType = SyncType::INNER_CORE_SYNC>
__aicore__ inline Buffer<bufferType, syncType> AllocBuffer(uint32_t size) {
TensorType temp = mem_[offset_];
offset_ += size;
return Buffer<bufferType, syncType>(temp, size);
}
template<SyncType syncType = SyncType::INNER_CORE_SYNC>
__aicore__ inline void FreeBuffer(Buffer<bufferType, syncType> &buffer){
}
private:
uint32_t offset_ = 0;
TensorType mem_;
};
}
#endif

View File

@@ -0,0 +1,216 @@
/**
 * 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 buffer_mix_core.h
* \brief同步管理
*/
#ifndef BUFFER_MIX_CORE_H
#define BUFFER_MIX_CORE_H
#include <type_traits>
#include "lib/matmul_intf.h"
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_basic_intf.h"
#else
#include "kernel_operator.h"
#endif
using namespace AscendC;
namespace fa_base_matmul {
__BLOCK_LOCAL__ __inline__ uint32_t idCounterNum;
#define MAKE_ID ((++idCounterNum) % 11)
// 核间同步中AIC(flagId 0-10)对应AIV0(flagId 0-10)对应AIV1(flagId 16-26)
#define AIV0_AIV1_OFFSET 16
enum class BufferType {
L1 = 0,
L0A = 1,
L0B = 2,
L0C = 3,
UB = 4,
GM = 5,
};
enum class SyncType {
NO_SYNC,
INNER_CORE_SYNC,
CROSS_CORE_SYNC_FORWARD,
CROSS_CORE_SYNC_BOTH,
};
constexpr uint32_t INVALID_CROSS_CORE_EVENT_ID = 16;
static constexpr uint64_t CROSS_CORE_SYNC_MODE = 4;
template <BufferType Type>
struct BufferInfo {
// Cons 消费者Prod 生产者
__aicore__ const static constexpr HardEvent ConsWaitProdStatus()
{
if constexpr (Type == BufferType::L1) {
return HardEvent::MTE2_MTE1;
} else if constexpr (Type == BufferType::L0A) {
return HardEvent::MTE1_M;
} else if constexpr (Type == BufferType::L0B) {
return HardEvent::MTE1_M;
} else if constexpr (Type == BufferType::L0C) {
return HardEvent::M_FIX;
}
}
__aicore__ const static constexpr HardEvent ProdWaitConsStatus()
{
if constexpr (Type == BufferType::L1) {
return HardEvent::MTE1_MTE2;
} else if constexpr (Type == BufferType::L0A) {
return HardEvent::M_MTE1;
} else if constexpr (Type == BufferType::L0B) {
return HardEvent::M_MTE1;
} else if constexpr (Type == BufferType::L0C) {
return HardEvent::FIX_M;
}
}
__aicore__ const static constexpr TPosition GetTPosition()
{
if constexpr (Type == BufferType::L1) {
return TPosition::A1;
} else if constexpr (Type == BufferType::L0A) {
return TPosition::A2;
} else if constexpr (Type == BufferType::L0B) {
return TPosition::B2;
} else if constexpr (Type == BufferType::L0C) {
return TPosition::CO1;
} else if constexpr (Type == BufferType::UB) {
return TPosition::VECIN;
} else if constexpr (Type == BufferType::GM) {
return TPosition::GM;
}
}
static constexpr HardEvent EventP2C =
ConsWaitProdStatus(); // 生产者到消费者方向的HardEvent消费者等生产者提供/生产者通知消费者已生成
static constexpr HardEvent EventC2P =
ProdWaitConsStatus(); // 消费者到生产者方向的HardEvent生产者等消费者消耗/消费者通知生产者已消耗’
static constexpr TPosition Position = GetTPosition();
};
// buffer绑定生产者、消费者关系
// L1 buffer的生产者为MTE2或者MTE3消费者为MTE1
// L0A buffer的生产者为MTE1消费者为M
// L0B buffer的生产者为MTE1消费者为M
// L0C buffer的生产者为M消费者为FIX
template <BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class Buffer {
using TensorType = std::conditional_t<bufferType == BufferType::GM, GlobalTensor<uint8_t>, LocalTensor<uint8_t>>;
template <typename T>
using TargetTensorType = std::conditional_t<bufferType == BufferType::GM, GlobalTensor<T>, LocalTensor<T>>;
public:
__aicore__ inline Buffer()
{
}
__aicore__ inline Buffer(TensorType tensor, uint32_t size)
{
tensor_ = tensor;
size_ = size;
if constexpr (syncType == SyncType::CROSS_CORE_SYNC_FORWARD) {
id0_ = MAKE_ID;
id1_ = INVALID_CROSS_CORE_EVENT_ID;
} else if constexpr (syncType == SyncType::CROSS_CORE_SYNC_BOTH) {
id0_ = MAKE_ID;
id1_ = MAKE_ID;
} else {
id0_ = INVALID_CROSS_CORE_EVENT_ID;
id1_ = INVALID_CROSS_CORE_EVENT_ID;
}
}
__aicore__ inline void Init()
{
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
p2cEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventP2C>(); // 确保只能被调用一次
c2pEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventC2P>();
SetFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_);
}
}
__aicore__ inline void UnInit()
{
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
WaitFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_);
GetTPipePtr()->ReleaseEventID<BufferInfo<bufferType>::EventP2C>(p2cEventId_); // 确保只能被调用一次
GetTPipePtr()->ReleaseEventID<BufferInfo<bufferType>::EventC2P>(c2pEventId_);
}
}
template <HardEvent EventType>
__aicore__ inline void Wait()
{
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
if constexpr (EventType == BufferInfo<bufferType>::EventP2C) {
WaitFlag<BufferInfo<bufferType>::EventP2C>(p2cEventId_); // 消费者等待生产者完成生产
} else {
WaitFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_); // 生产者等待消费者完成消费
}
}
}
template <HardEvent EventType>
__aicore__ inline void Set()
{
if constexpr (syncType == SyncType::INNER_CORE_SYNC) {
if constexpr (EventType == BufferInfo<bufferType>::EventP2C) {
SetFlag<BufferInfo<bufferType>::EventP2C>(p2cEventId_); // 生产者通知消费者已完成生产
} else {
SetFlag<BufferInfo<bufferType>::EventC2P>(c2pEventId_); // 消费者通知生产者已完成消费
}
}
}
__aicore__ inline void SetEventID()
{
p2cEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventP2C>(); // 确保只能被调用一次
c2pEventId_ = GetTPipePtr()->AllocEventID<BufferInfo<bufferType>::EventC2P>();
}
template <HardEvent EventType>
__aicore__ inline TEventID GetEventID()
{
if constexpr (EventType == BufferInfo<bufferType>::EventP2C) {
return p2cEventId_; // 生产者通知消费者已完成生产
} else {
return c2pEventId_; // 消费者通知生产者已完成消费
}
}
template <typename T>
__aicore__ inline TargetTensorType<T> GetTensor()
{
return tensor_.template ReinterpretCast<T>();
}
template <typename T>
__aicore__ inline TargetTensorType<T> GetTensor(uint64_t startindex)
{
TargetTensorType<T> tmpTensor = tensor_.template ReinterpretCast<T>();
return tmpTensor[startindex];
}
private:
TensorType tensor_;
uint32_t size_;
TEventID p2cEventId_;
TEventID c2pEventId_;
uint32_t id0_; // 用作正向同步:生产者通知消费者,或者消费者等待生产者;
uint32_t id1_; // 用作反向同步:消费者通知生产者,或者生产者等待消费者;
};
} // namespace fa_base_matmul
#endif

View File

@@ -0,0 +1,407 @@
/**
 * 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 buffers_policy.h
* \brief 综合管理buffer的内存和同步
*/
#ifndef BUFFERS_POLICY_H
#define BUFFERS_POLICY_H
#include "buffer_manager.h"
#define NUM_2 2
#define NUM_3 3
#define NUM_4 4
// Q复用 KV复用
// 申请单块buffer
namespace fa_base_matmul {
template<BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class BuffersPolicySingleBuffer {
public:
__aicore__ inline void Init(BufferManager<bufferType> &bufferManager, uint32_t size){
buffer_ = bufferManager.template AllocBuffer<syncType>(size);
buffer_.Init();
}
__aicore__ inline void Uninit(BufferManager<bufferType> &bufferManager){
buffer_.UnInit();
bufferManager.FreeBuffer(buffer_);
}
__aicore__ inline Buffer<bufferType, syncType> &Get(){
return buffer_;
}
__aicore__ inline Buffer<bufferType, syncType> &GetPre(){
return Get();
}
__aicore__ inline Buffer<bufferType, syncType> &GetReused(){
return Get();
}
private:
Buffer<bufferType, syncType> buffer_;
};
// 申请2个buffer乒乓轮转
template<BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class BuffersPolicyDB {
public:
__aicore__ inline void Init(BufferManager<bufferType> &bufferManager, uint32_t size){
ping_ = bufferManager.template AllocBuffer<syncType>(size);
pong_ = bufferManager.template AllocBuffer<syncType>(size);
ping_.Init();
pong_.Init();
}
__aicore__ inline void Uninit(BufferManager<bufferType> &bufferManager){
ping_.UnInit();
pong_.UnInit();
bufferManager.FreeBuffer(ping_);
bufferManager.FreeBuffer(pong_);
}
__aicore__ inline Buffer<bufferType, syncType> &Get() {
if (flag1_) { // 1
flag1_ = 0;
return ping_;
} else { // 0
flag1_ = 1;
return pong_;
}
}
// 需要与Get联用 首次调用Get第二次调用GetPre(Q复用)
__aicore__ inline Buffer<bufferType, syncType> &GetPre() {
if (flag1_) { // 0->1
return pong_;
} else { // 1->0
return ping_;
}
}
// 需要与Get,GetPre联用 首次调用Get第二次调用GetPre,第三次复用时GetReused(KV复用)
__aicore__ inline Buffer<bufferType, syncType> &GetReused() {
if (flag2_ == 0) {
flag2_ = 1;
return pong_;
} else {
flag2_ = 0;
return ping_;
}
}
__aicore__ inline Buffer<bufferType, syncType> &GetReused(bool isNextS2IdxNoChange) {
if (isNextS2IdxNoChange) {
if (flag2_ == 0) {
return pong_;
} else {
return ping_;
}
} else {
return GetReused();
}
}
private:
Buffer<bufferType, syncType> ping_;
Buffer<bufferType, syncType> pong_;
uint32_t flag1_ = 0;
uint32_t flag2_ = 0;
};
// 申请3个buffer, 轮转
template<BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class BuffersPolicy3buff {
public:
__aicore__ inline void Init(BufferManager<bufferType> &bufferManager, uint32_t size) {
a_ = bufferManager.template AllocBuffer<syncType>(size);
b_ = bufferManager.template AllocBuffer<syncType>(size);
c_ = bufferManager.template AllocBuffer<syncType>(size);
a_.Init();
b_.Init();
c_.Init();
}
__aicore__ inline void Uninit(BufferManager<bufferType> &bufferManager) {
a_.UnInit();
b_.UnInit();
c_.UnInit();
bufferManager.FreeBuffer(a_);
bufferManager.FreeBuffer(b_);
bufferManager.FreeBuffer(c_);
}
__aicore__ inline Buffer<bufferType, syncType> &Get() {
if (flag1_ == 0) {
flag1_ = 1;
return a_;
} else if (flag1_ == 1) {
flag1_ = NUM_2;
return b_;
} else {
flag1_ = 0;
return c_;
}
}
__aicore__ inline Buffer<bufferType, syncType> &GetVec() { // mixcore architecture
if (flag1_vec1_ == 0) {
flag1_vec1_ = 1;
return a_;
} else if (flag1_vec1_ == 1) {
flag1_vec1_ = NUM_2;
return b_;
} else {
flag1_vec1_ = 0;
return c_;
}
}
__aicore__ inline Buffer<bufferType, syncType> &GetCube() { // mixcore architecture
if (flag1_bmm2_ == 0) {
flag1_bmm2_ = 1;
return a_;
} else if (flag1_bmm2_ == 1) {
flag1_bmm2_ = NUM_2;
return b_;
} else {
flag1_bmm2_ = 0;
return c_;
}
}
// Q复用
__aicore__ inline Buffer<bufferType, syncType> &GetPre() {
if (flag1_ == 0) {
return c_;
} else if (flag1_ == 1) {
return a_;
} else {
return b_;
}
}
// KV复用
__aicore__ inline Buffer<bufferType, syncType> &GetReused() {
if (flag2_ == 0) {
flag2_ = 1;
return a_;
} else if (flag2_ == 1){
flag2_ = NUM_2;
return b_;
} else {
flag2_ = 0;
return c_;
}
}
private:
Buffer<bufferType, syncType> a_;
Buffer<bufferType, syncType> b_;
Buffer<bufferType, syncType> c_;
uint32_t flag1_ = 0;
uint32_t flag1_vec1_ = 0;
uint32_t flag1_bmm2_ = 0;
uint32_t flag2_ = 0;
};
// 申请4个buffer + kv复用
template<BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class BuffersPolicy4buff {
public:
__aicore__ inline void Init(BufferManager<bufferType> &bufferManager, uint32_t size) {
a_ = bufferManager.template AllocBuffer<syncType>(size);
b_ = bufferManager.template AllocBuffer<syncType>(size);
c_ = bufferManager.template AllocBuffer<syncType>(size);
d_ = bufferManager.template AllocBuffer<syncType>(size);
a_.Init();
b_.Init();
c_.Init();
d_.Init();
}
__aicore__ inline void Uninit(BufferManager<bufferType> &bufferManager) {
a_.UnInit();
b_.UnInit();
c_.UnInit();
d_.UnInit();
bufferManager.FreeBuffer(a_);
bufferManager.FreeBuffer(b_);
bufferManager.FreeBuffer(c_);
bufferManager.FreeBuffer(d_);
}
__aicore__ inline Buffer<bufferType, syncType> &Get(uint32_t id) {
uint32_t flag = id % 4;
if (flag == 0) {
return a_;
} else if (flag == 1) {
return b_;
} else if (flag == 2) { // 2:c_
return c_;
} else {
return d_;
}
}
__aicore__ inline Buffer<bufferType, syncType> &Get() {
auto& buffer = Get(head_);
head_++;
return buffer;
}
__aicore__ inline Buffer<bufferType, syncType> &GetReused() {
auto& buffer = Get(used_);
used_ = (used_ - tail_ + 1) % (head_ - tail_) + tail_;
return buffer;
}
__aicore__ inline Buffer<bufferType, syncType> &GetFree() {
if (tail_ == used_) {
used_++;
}
auto& buffer = Get(tail_);
tail_++;
return buffer;
}
private:
Buffer<bufferType, syncType> a_;
Buffer<bufferType, syncType> b_;
Buffer<bufferType, syncType> c_;
Buffer<bufferType, syncType> d_;
uint32_t tail_ = 0; // 表示当前正在使用的buffer队列队尾
uint32_t head_ = 0; // 表示当前正在使用的buffer队列队首+1
uint32_t used_ = 0; // 表示当前正在使用的buffer于首尾间左闭右开
};
template<BufferType bufferType, SyncType syncType = SyncType::INNER_CORE_SYNC>
class Matrix2x2BufferPolicy { // 4buffer
// 二维buffer管理地址行优先使用列优先
// MracBuffer:memory address with row first, alloc/use/free with column first
public:
__aicore__ inline void Init(BufferManager<bufferType> &bufferManager, uint32_t size) {
bufferM0k0_ = bufferManager.template AllocBuffer<syncType>(size);
bufferM0k1_ = bufferManager.template AllocBuffer<syncType>(size);
bufferM1k0_ = bufferManager.template AllocBuffer<syncType>(size);
bufferM1k1_ = bufferManager.template AllocBuffer<syncType>(size);
bufferM0k0_.Init();
bufferM0k1_.Init();
bufferM1k0_.Init();
bufferM1k1_.Init();
}
__aicore__ inline void Uninit(BufferManager<bufferType> &bufferManager) {
bufferM0k0_.UnInit();
bufferM0k1_.UnInit();
bufferM1k0_.UnInit();
bufferM1k1_.UnInit();
bufferManager.FreeBuffer(bufferM0k0_);
bufferManager.FreeBuffer(bufferM0k1_);
bufferManager.FreeBuffer(bufferM1k0_);
bufferManager.FreeBuffer(bufferM1k1_);
}
__aicore__ inline void SetMExtent(int32_t mExtent) {
aIdx_ = -1;
amIdx_ = (amIdx_ + mSize_ - 1) % mSize_; // 翻转 0->1, 1->0
akIdx_ = 0;
uIdx_ = -1;
umIdx_ = (umIdx_ + mSize_ - 1) % mSize_;
ukIdx_ = 0;
fIdx_ = -1;
fmIdx_ = (fmIdx_ + mSize_ - 1) % mSize_;
fkIdx_ = 0;
mExtent_ = mExtent;
}
__aicore__ inline Buffer<bufferType, syncType> &AllocNext() {
aIdx_++;
return GetBuffer(aIdx_, amIdx_, akIdx_);
}
__aicore__ inline Buffer<bufferType, syncType> &ReuseNext() {
uIdx_++;
return GetBuffer(uIdx_, umIdx_, ukIdx_);
}
__aicore__ inline Buffer<bufferType, syncType> &FreeNext() {
fIdx_++;
return GetBuffer(fIdx_, fmIdx_, fkIdx_);
}
__aicore__ inline Buffer<bufferType, syncType> &PeekNextK() { // 在Alloc阶段使用k方向取下一个
return PeekBuffer(amIdx_, (1 - akIdx_)); // k翻转
}
private:
__aicore__ inline Buffer<bufferType, syncType> &GetBuffer(int32_t xIdx, int32_t &mIdx, int32_t &kIdx) {
// xIdx为入参表示当前alloc/use/free的idxmIdx和kIdx为下标出参移动到下一个buffer并获取
mIdx = (mIdx + mExtent_ - 1) % mExtent_;
kIdx = (xIdx / mExtent_) % kSize_;
if (mIdx == 0 && kIdx == 0) {
return bufferM0k0_;
} else if (mIdx == 0 && kIdx == 1) {
return bufferM0k1_;
} else if (mIdx == 1 && kIdx == 0) {
return bufferM1k0_;
} else { // 该分支条件为mIdx == 1 && kIdx == 1
return bufferM1k1_;
}
}
__aicore__ inline Buffer<bufferType, syncType> &PeekBuffer(int32_t mIdx, int32_t kIdx) {
// 只访问buffer不进行下标移动
if (mIdx == 0 && kIdx == 0) {
return bufferM0k0_;
} else if (mIdx == 0 && kIdx == 1) {
return bufferM0k1_;
} else if ((mIdx == 1) && (kIdx == 0)) {
return bufferM1k0_;
} else { // mIdx == 1 && kIdx == 1
return bufferM1k1_;
}
}
Buffer<bufferType, syncType> bufferM0k0_;
Buffer<bufferType, syncType> bufferM0k1_;
Buffer<bufferType, syncType> bufferM1k0_;
Buffer<bufferType, syncType> bufferM1k1_;
int32_t mSize_ = 2; // m的总buffer数
int32_t kSize_ = 2; // k的总buffer数
// Alloc
int32_t aIdx_ = -1; // 当前第几次Alloc Buffer
int32_t amIdx_ = 0; // 当前Alloc Buffer的m下标
int32_t akIdx_ = 0; // 当前Alloc Buffer的k下标
// Reuse
int32_t uIdx_ = -1;
int32_t umIdx_ = 0;
int32_t ukIdx_ = 0;
// Free
int32_t fIdx_ = -1;
int32_t fmIdx_ = 0;
int32_t fkIdx_ = 0;
int32_t mExtent_ = 0; // m实际使用的大小可以为1或者2
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
/**
 * 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 fa_gm_tensor.h
* \brief
*/
#ifndef FA_GM_TENSOR_H
#define FA_GM_TENSOR_H
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_vec_intf.h"
#include "kernel_cube_intf.h"
#else
#include "kernel_operator.h"
#endif
#include "gm_layout.h"
#include "offset_calculator_v2.h"
using AscendC::GlobalTensor;
template <typename Q_T, GmFormat FORMAT, typename ACTLEN_T = uint64_t>
struct FaGmTensor {
GlobalTensor<Q_T> gmTensor;
OffsetCalculator<FORMAT, ACTLEN_T> offsetCalculator;
};
#endif

View File

@@ -0,0 +1,43 @@
/**
 * 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 fa_l1_tensor.h
* \brief
*/
#ifndef FA_L1_TENSOR_H
#define FA_L1_TENSOR_H
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_vec_intf.h"
#include "kernel_cube_intf.h"
#else
#include "kernel_operator.h"
#endif
using AscendC::LocalTensor;
enum class L1Format {
NZ = 0
};
enum class ScaleTrans {
NO_TRANS = 0,
ND2NZ = 1,
DN2NZ = 2
};
template <typename Q_T, L1Format FORMAT>
struct FaL1Tensor {
LocalTensor<Q_T> tensor;
uint32_t rowCount;
};
#endif

View File

@@ -0,0 +1,26 @@
/**
 * 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 gm_coord.h
* \brief
*/
#ifndef GM_COORD_H
#define GM_COORD_H
struct GmCoord {
uint32_t bIdx;
uint32_t n2Idx;
uint32_t gS1Idx;
uint32_t dIdx;
uint32_t gS1DealSize;
uint32_t dDealSize;
};
#endif

View File

@@ -0,0 +1,427 @@
/**
 * 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 gm_layout.h
* \brief
*/
#ifndef GM_LAYOUT_H
#define GM_LAYOUT_H
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_vec_intf.h"
#include "kernel_cube_intf.h"
#else
#include "kernel_operator.h"
#endif
// ----------------------------------------------GmLayout--------------------------------
enum class GmFormat {
BSNGD = 0,
BNGSD = 1,
NGBSD = 2,
TNGD = 3,
NGTD = 4,
BSND = 5,
BNSD = 6,
TND = 7,
NTD = 8,
PA_BnBsND = 9,
PA_BnNBsD = 10,
PA_NZ = 11,
NGD = 12, // post_quant
ND = 13, //antiquant no PA
BS2 = 14,
BNS2 = 15,
PA_BnBs = 16, //antiquant PA
PA_BnNBs = 17,
BN2GS1S2 = 18, //PSE_GmFormat
SBNGD = 19,
SBND = 20,
NTGD = 21,
PA_NZ_K_SCALE = 22,
};
template <GmFormat FORMAT>
struct GmLayout {
};
template <>
struct GmLayout<GmFormat::BSNGD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t g, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, g, s, d);
uint64_t dStride = 1;
uint64_t gStride = dStride * d;
uint64_t nStride = gStride * g;
uint64_t sStride = nStride * n;
uint64_t bStride = sStride * s;
stride = AscendC::MakeStride(bStride, nStride, gStride, sStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::BNGSD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t g, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, g, s, d);
uint64_t dStride = 1;
uint64_t sStride = dStride * d;
uint64_t gStride = sStride * s;
uint64_t nStride = gStride * g;
uint64_t bStride = nStride * n;
stride = AscendC::MakeStride(bStride, nStride, gStride, sStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::NGBSD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t g, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, g, s, d);
uint64_t dStride = 1;
uint64_t sStride = dStride * d;
uint64_t bStride = sStride * s;
uint64_t gStride = bStride * b;
uint64_t nStride = gStride * g;
stride = AscendC::MakeStride(bStride, nStride, gStride, sStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::TNGD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t t, uint32_t n, uint32_t g, uint32_t d) {
shape = AscendC::MakeShape(t, n, g, d);
uint64_t dStride = 1;
uint64_t gStride = dStride * d;
uint64_t nStride = gStride * g;
uint64_t tStride = nStride * n;
stride = AscendC::MakeStride(tStride, nStride, gStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::NGTD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t t, uint32_t n, uint32_t g, uint32_t d) {
shape = AscendC::MakeShape(t, n, g, d);
uint64_t dStride = 1;
uint64_t tStride = dStride * d;
uint64_t gStride = tStride * t;
uint64_t nStride = gStride * g;
stride = AscendC::MakeStride(tStride, nStride, gStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::NTGD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t t, uint32_t n, uint32_t g, uint32_t d) {
shape = AscendC::MakeShape(t, n, g, d);
uint64_t dStride = 1;
uint64_t gStride = dStride * d;
uint64_t tStride = gStride * g;
uint64_t nStride = tStride * t;
stride = AscendC::MakeStride(tStride, nStride, gStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::BSND> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, s, d);
uint64_t dStride = 1;
uint64_t nStride = dStride * d;
uint64_t sStride = nStride * n;
uint64_t bStride = sStride * s;
stride = AscendC::MakeStride(bStride, nStride, sStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::BNSD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, s, d);
uint64_t dStride = 1;
uint64_t sStride = dStride * d;
uint64_t nStride = sStride * s;
uint64_t bStride = nStride * n;
stride = AscendC::MakeStride(bStride, nStride, sStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::TND> {
AscendC::Shape<uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t t, uint32_t n, uint32_t d) {
shape = AscendC::MakeShape(t, n, d);
uint64_t dStride = 1;
uint64_t nStride = dStride * d;
uint64_t tStride = nStride * n;
stride = AscendC::MakeStride(tStride, nStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::NTD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t t, uint32_t n, uint32_t d) {
shape = AscendC::MakeShape(t, n, d);
uint64_t dStride = 1;
uint64_t tStride = dStride * d;
uint64_t nStride = tStride * t;
stride = AscendC::MakeStride(tStride, nStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::PA_BnBsND> {
AscendC::Shape<uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t blockSize, uint32_t d) {
shape = AscendC::MakeShape(n, blockSize, d);
uint64_t dStride = 1;
uint64_t nStride = dStride * d;
uint64_t bsStride = nStride * n;
uint64_t bnStride = bsStride * blockSize;
stride = AscendC::MakeStride(bnStride, nStride, bsStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::PA_BnNBsD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t blockSize, uint32_t d) {
shape = AscendC::MakeShape(n, blockSize, d);
uint64_t dStride = 1;
uint64_t bsStride = dStride * d;
uint64_t nStride = bsStride * blockSize;
uint64_t bnStride = nStride * n;
stride = AscendC::MakeStride(bnStride, nStride, bsStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::PA_NZ> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t blockSize, uint32_t d1, uint32_t d0) {
shape = AscendC::MakeShape(n, d1, blockSize, d0);
uint64_t d0Stride = 1;
uint64_t bsStride = d0Stride * d0;
uint64_t d1Stride = bsStride * blockSize;
uint64_t nStride = d1Stride * d1;
uint64_t bnStride = nStride * n;
stride = AscendC::MakeStride(bnStride, nStride, d1Stride, bsStride, d0Stride);
}
};
template <>
struct GmLayout<GmFormat::PA_NZ_K_SCALE> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t blockSize1, uint32_t d, uint32_t blockSize0) {
shape = AscendC::MakeShape(n, blockSize1, d, blockSize0);
uint64_t bs0Stride = 1;
uint64_t dStride = bs0Stride * blockSize0;
uint64_t bs1Stride = dStride * d;
uint64_t nStride = bs1Stride * blockSize1;
uint64_t bnStride = nStride * n;
stride = AscendC::MakeStride(bnStride, nStride, bs1Stride, dStride, bs0Stride);
}
};
// post_quant
template <>
struct GmLayout<GmFormat::NGD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t g, uint32_t d) {
shape = AscendC::MakeShape(n, g, d);
uint64_t dStride = 1;
uint64_t gStride = dStride * d;
uint64_t nStride = gStride * g;
stride = AscendC::MakeStride(nStride, gStride, dStride);
}
};
//antiquant
template <>
struct GmLayout<GmFormat::ND> {
AscendC::Shape<uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t d) {
shape = AscendC::MakeShape(n, d);
uint64_t dStride = 1;
uint64_t nStride = dStride * d; //headDim
stride = AscendC::MakeStride(nStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::BS2> {
AscendC::Shape<uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t s) {
shape = AscendC::MakeShape(b, s);
uint64_t sStride = 1;
uint64_t bStride = sStride * s;
stride = AscendC::MakeStride(bStride, sStride);
}
};
template <>
struct GmLayout<GmFormat::BNS2> {
AscendC::Shape<uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t s) {
shape = AscendC::MakeShape(b, n, s);
uint64_t sStride = 1;
uint64_t nStride = sStride * s;
uint64_t bStride = nStride * n;
stride = AscendC::MakeStride(bStride, nStride, sStride);
}
};
template <>
struct GmLayout<GmFormat::PA_BnBs> {
AscendC::Shape<uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t blockSize) {
shape = AscendC::MakeShape(blockSize);
uint64_t bsStride = 1;
uint64_t bnStride = bsStride * blockSize;
stride = AscendC::MakeStride(bnStride, bsStride);
}
};
template <>
struct GmLayout<GmFormat::PA_BnNBs> {
AscendC::Shape<uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t n, uint32_t blockSize) {
shape = AscendC::MakeShape(n, blockSize);
uint64_t bsStride = 1;
uint64_t nStride = bsStride * blockSize;
uint64_t bnStride = nStride * n; //blockSize * kvHeadNum
stride = AscendC::MakeStride(bnStride, nStride, bsStride);
}
};
//PSE_GmLayout
template <>
struct GmLayout<GmFormat::BN2GS1S2> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t g, uint32_t s1, uint32_t s2)
{
shape = AscendC::MakeShape(b, n, g, s1, s2);
uint64_t s2Stride = 1;
uint64_t s1Stride = s2Stride * s2;
uint64_t gStride = s1Stride * s1;
uint64_t nStride = gStride * g;
uint64_t bStride = nStride * n;
stride = AscendC::MakeStride(bStride, nStride, gStride, s1Stride, s2Stride);
}
};
template <>
struct GmLayout<GmFormat::SBNGD> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t g, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, g, s, d);
uint64_t dStride = 1;
uint64_t gStride = dStride * d;
uint64_t nStride = gStride * g;
uint64_t bStride = nStride * n;
uint64_t sStride = bStride * b;
stride = AscendC::MakeStride(bStride, nStride, gStride, sStride, dStride);
}
};
template <>
struct GmLayout<GmFormat::SBND> {
AscendC::Shape<uint32_t, uint32_t, uint32_t, uint32_t> shape;
AscendC::Stride<uint64_t, uint64_t, uint64_t, uint64_t> stride;
__aicore__ inline GmLayout() = default;
__aicore__ inline void MakeLayout(uint32_t b, uint32_t n, uint32_t s, uint32_t d) {
shape = AscendC::MakeShape(b, n, s, d);
uint64_t dStride = 1;
uint64_t nStride = dStride * d;
uint64_t bStride = nStride * n;
uint64_t sStride = bStride * b;
stride = AscendC::MakeStride(bStride, nStride, sStride, dStride);
}
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/**
 * 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 parser.h
* \brief
*/
#ifndef PARSER_H
#define PARSER_H
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_vec_intf.h"
#include "kernel_cube_intf.h"
#else
#include "kernel_operator.h"
#endif
using AscendC::GlobalTensor;
// ----------------------------------------------ActualSeqLensParser--------------------------------
enum class ActualSeqLensMode
{
BY_BATCH = 0,
ACCUM = 1,
};
template <ActualSeqLensMode MODE, typename ACTLEN_T = uint64_t>
class ActualSeqLensParser {
};
template <typename ACTLEN_T>
class ActualSeqLensParser<ActualSeqLensMode::ACCUM, ACTLEN_T> {
public:
__aicore__ inline ActualSeqLensParser() = default;
__aicore__ inline void Init(GlobalTensor<ACTLEN_T> actualSeqLengthsGm, uint32_t actualLenDims,
uint64_t defaultVal = 0)
{
this->actualSeqLengthsGm = actualSeqLengthsGm;
this->actualLenDims = actualLenDims;
}
__aicore__ inline uint64_t GetTBase(uint32_t bIdx) const
{
if (bIdx == 0) {
return 0;
}
return actualSeqLengthsGm.GetValue(bIdx - 1);
}
__aicore__ inline uint64_t GetMxVscaleTBase(uint32_t bIdx) const
{
if (bIdx == 0) {
return 0;
}
uint64_t vScaleTBaseOffset = 0;
for (uint32_t idx = 0; idx < bIdx; idx++) {
vScaleTBaseOffset += ((GetActualSeqLength(idx) + 63) >> 6);
}
return vScaleTBaseOffset;
}
__aicore__ inline uint64_t GetActualSeqLength(uint32_t bIdx) const
{
if (bIdx == 0) {
return actualSeqLengthsGm.GetValue(0);
}
return (actualSeqLengthsGm.GetValue(bIdx) - actualSeqLengthsGm.GetValue(bIdx - 1));
}
__aicore__ inline uint64_t GetTSize() const
{
return actualSeqLengthsGm.GetValue(actualLenDims - 1);
}
private:
GlobalTensor<ACTLEN_T> actualSeqLengthsGm;
uint32_t actualLenDims;
};
template <typename ACTLEN_T>
class ActualSeqLensParser<ActualSeqLensMode::BY_BATCH, ACTLEN_T> {
public:
__aicore__ inline ActualSeqLensParser() = default;
__aicore__ inline void Init(GlobalTensor<ACTLEN_T> actualSeqLengthsGm, uint32_t actualLenDims, uint64_t defaultVal)
{
this->actualSeqLengthsGm = actualSeqLengthsGm;
this->actualLenDims = actualLenDims;
this->defaultVal = defaultVal;
}
__aicore__ inline uint64_t GetActualSeqLength(uint32_t bIdx) const
{
if (actualLenDims == 0) {
return defaultVal;
}
if (actualLenDims == 1) {
return actualSeqLengthsGm.GetValue(0);
}
return actualSeqLengthsGm.GetValue(bIdx);
}
__aicore__ inline uint32_t GetActualLenDims() const
{
return actualLenDims;
}
private:
GlobalTensor<ACTLEN_T> actualSeqLengthsGm;
uint32_t actualLenDims = 0;
uint64_t defaultVal = 0;
};
// ----------------------------------------------BlockTableParser--------------------------------
class BlockTableParser {
public:
__aicore__ inline BlockTableParser() = default;
__aicore__ inline void Init(GlobalTensor<int32_t> blockTableGm, uint32_t maxblockNumPerBatch)
{
this->blockTableGm = blockTableGm;
this->maxblockNumPerBatch = maxblockNumPerBatch;
}
__aicore__ inline int32_t GetBlockIdx(uint32_t bIdx, uint32_t blockIdxInBatch) const
{
return blockTableGm.GetValue(bIdx * maxblockNumPerBatch + blockIdxInBatch);
}
private:
GlobalTensor<int32_t> blockTableGm;
uint32_t maxblockNumPerBatch;
};
#endif

View File

@@ -0,0 +1,31 @@
/**
 * 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 offset_calculator.h
* \brief
*/
#ifndef OFFSET_CALCULATOR_H
#define OFFSET_CALCULATOR_H
#if ASC_DEVKIT_MAJOR >= 9
#include "kernel_basic_intf.h"
#else
#include "kernel_operator.h"
#endif
#include "memcopy/gm_layout.h"
#include "memcopy/parser.h"
#include "memcopy/offset_calculator_v2.h"
#include "memcopy/fa_gm_tensor.h"
#include "memcopy/fa_l1_tensor.h"
#include "memcopy/gm_coord.h"
#endif

View File

@@ -0,0 +1,19 @@
# -----------------------------------------------------------------------------------------------------------
# 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(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
if(NOT ENABLE_TEST AND NOT BENCHMARK)
list(REMOVE_ITEM CURRENT_DIRS tests)
endif()
foreach(SUB_DIR ${CURRENT_DIRS})
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
add_subdirectory(${SUB_DIR})
endif()
endforeach()

View File

@@ -0,0 +1,499 @@
# Compressor
## 产品支持情况
| 产品 | 是否支持 |
| ------------------------------------------------------------ | :------: |
|<term>Ascend 950PR/Ascend 950DT</term>| √ |
|<term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>| √ |
|<term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term>| × |
|<term>Atlas 200I/500 A2 推理产品</term>| × |
|<term>Atlas 推理系列加速卡产品</term>| × |
|<term>Atlas 训练系列产品</term>| × |
## 功能说明
- API功能Compressor是推理场景下SAS和QLI的前处理算子用于将每4或128个token的KV cache压缩成一个然后每个token与这些压缩的KV cache进行DSA计算。在长序列的情况下Compressor可以有效地减少计算开销。
- 计算公式:
压缩阶段:
1. 计算矩阵乘法:
- C4A: $\left[kv\_state^a, score\_state^a\right] = X @ \left[W^{aKV}, W^{aGate}\right], \left[kv\_state^b, score\_state^b\right] = X @ \left[W^{bKV}, W^{bGate}\right];$
- C128A: $\left[kv\_state, score\_state\right] = X @ \left[W^{KV}, W^{Gate}\right]$
2. 计算分组加法:
- C4A: $score\_state_i^\prime = \left[score\_state_{\left[4(i-1)+1:4i,:\right]}^a; score\_state_{\left[4i+1:4(i+1),:\right]}^b\right] + Ape,~i=1,2,\cdots, \frac{s}{4};$
- C128A: $score\_state_i^\prime = score\_state_{\left[128(i-1)+1:128i,:\right]} + Ape,~i=1,2,\cdots, \frac{s}{128};$
3. 计算分组Softmax
- C4A: $S_i^\prime = softmax(score\_state_i^\prime),~i=1,2,\cdots, \frac{s}{4};$
- C128A: $S_i^\prime = softmax(score\_state_i^\prime),~i=1,2,\cdots, \frac{s}{128};$
4. 计算Hadamard乘积
- C4A: $(S_H)_i = S_i^\prime \odot \left[kv\_state^a_{\left[4(i-1)+1:4i,:\right]} ; kv\_state^b_{\left[4i+1:4(i+1),:\right]}\right],~i=1,2,\cdots, \frac{s}{4};$
- C128A: $S_H = S_i^\prime \odot kv\_state;$
5. 沿着压缩轴分组求和:
- C4A: $C_{i}^{\text{Comp}} = \left[1\right]_{1\times8} @ (S_H)_i, ~i=1,2,\cdots, \frac{s}{4};$
- C128A: $C_{i}^{\text{Comp}} = \left[1\right]_{1\times128} @ (S_H)_i, ~i=1,2,\cdots, \frac{s}{128};$
后处理阶段:
6. 计算RMSNorm
- $\text{RMS}(C^{\text{Comp}}) = \sqrt{\frac{1}{N} \sum_{i=j* N}^{(j+1)* N} {(C_{i}^{\text{Comp}})}^{\text{2}} + norm\_eps} ,N=head\_dim, ~j=1,2,\cdots, \frac{s}{cmp\_ratio}$
- $\text{RmsNorm}(C^{\text{Comp}}) = norm\_weight \cdot \frac{C_{i}^{\text{Comp}}}{\text{RMS}(C^{\text{Comp}})}$
7. 计算Rope
- 主要计算过程为:
1. 将输入$X$与$W^{KV}$做Matmul运算得到$kv\_state$,将输入$X$与$W^{Gate}$做Matmul运算后再与$Ape$做Add运算得到$score\_state$$kv\_state$与$score\_state$根据输入的start_pos及cu_seqlens完成更新。
2. 在coff为2的情况下对$kv\_state$和$score\_state$进行数据重排。
3. 对$score\_state$进行softmax运算将softmax结果与$kv\_state$做Mul计算后进行ReduceSum运算。
4. 根据输入数据norm_weight、rope_sin、rope_cos进行RMSNorm和Rope运算得到$cmp\_kv$结果输出。
## 参数说明
| 参数名 | 输入/输出/属性 | 描述 | 数据类型 | 数据格式 |
|----------------------------|-----------|----------------------------------------------------------------------|----------------|------------|
| x | 输入 | 公式中的$X$,表示原始不经压缩的数据。 | FLOAT16、BFLOAT16 | ND |
| wkv | 输入 | 公式中的$W^{KV}$表示kv压缩权重。 | FLOAT16、BFLOAT16 | ND |
| wgate | 输入 | 公式中的$W^{Gate}$表示gate压缩权重。 | FLOAT16、BFLOAT16 | ND |
| kv_state | 输入 | 公式中的$kv\_state$表示kv\_state的历史数据。 | FLOAT32 | ND |
| score_state | 输入 | 公式中的$score\_state$表示score\_state中的历史数据。 | FLOAT32 | ND |
| ape | 输入 | 公式中的$Ape$表示positional biases。 | FLOAT32 | ND |
| norm\_weight | 输入 | 表示计算RmsNorm时的权重系数。 | FLOAT16、BFLOAT16 | ND |
| rope\_sin | 输入 | 表示Rope计算时sin的权重系数。 | FLOAT16、BFLOAT16 | ND |
| rope\_cos | 输入 | 表示Rope计算时cos的权重系数。 | FLOAT16、BFLOAT16 | ND |
| rope\_head\_dim | 属性 | 表示rope_cos和rope_sin的hidden层最小单元大小当前仅支持64。 | INT32 | - |
| cmp\_ratio | 属性 | 用于稀疏计算,表示数据压缩率。 | INT32 | - |
| kv\_block\_table | 可选输入 | 表示kv\_state存储使用的block映射表。当其中元素的值为0时表示当前位置无需进行更新kv\_state操作。 | INT32 | ND |
| score\_block\_table | 可选输入 | 表示score\_state存储使用的block映射表。当其中元素的值为0时表示当前位置无需进行更新score\_state操作。 | INT32 | ND |
| cu\_seqlens | 可选输入 | 表示不同Batch中的有效token数。 | INT32 | ND |
| seqused | 可选输入 | 表示不同Batch中实际参与压缩的token数如果指定为None时表示和每个Batch上的Sequence Length长度相同。 | INT32 | ND |
| start\_pos | 可选输入 | 表示计算起始位置。 | INT32 | ND |
| coff | 可选属性 | 默认值1支持1/2。当coff=1时无需进行overlap数据重排。当coff=2时需要进行overlap数据重排。 | INT32 | - |
| norm\_eps | 可选属性 | 表示RmsNorm计算的权重系数。默认值1e-6。 | FLOAT32 | - |
| rotary\_mode | 可选属性 | 表示Rop计算的模式。默认值1支持1/2。rotary\_mode为1时代表half模式。rotary\_mode为2时代表interleave模式。 | INT32 | - |
| enabled\_grad | 可选属性 | 训练场景使用表示是否参与反向更新。默认值false支持false/true。**目前暂不支持输入true**。 | BOOL | - |
| cmp\_kv | 输出 | 表示压缩后的数据。 | FLOAT16、BFLOAT16 | ND |
| wkv\_proj | 可选输出 | 训练反向使用表示wkv权重Matmul的计算结果**目前暂不支持返回wkv\_proj**。 | FLOAT16、BFLOAT16 | ND |
| softmax\_res | 可选输出 | 训练反向使用表示Softmax计算结果**目前暂不支持返回softmax\_res**。 | FLOAT16、BFLOAT16 | ND |
| norm\_x | 可选输出 | 训练反向使用表示Rms计算的输入**目前暂不支持返回norm\_x**。 | FLOAT16、BFLOAT16 | ND |
| norm\_rstd | 可选输出 | 训练反向使用表示Rms计算的中间结果**目前暂不支持返回norm\_rstd**。 | FLOAT16、BFLOAT16 | ND |
## 约束说明
- x参数维度含义BBatch Size表示输入样本批量大小、SSequence Length表示输入样本序列长度、HHead Size表示hidden层的大小、DHead Dim表示hidden层的最小单元大小、T表示所有Batch输入样本序列长度的累加和。
- 输入shape限制
- wkv支持输入shape[coff* D,H]
- wgate支持输入shape[coff* D,H]
- kv\_state、score\_state支持输入shape[block_num,block_size,coff* D]要求block_num>0。
- ape支持输入shape[cmp_ratio,coff* D]
- norm\_weight支持输入shape[D,]
- start\_pos支持输入shape[B,]
- 若x的维度采用BS合轴即x的输入shape为[T,H]
- rope_sin、rope_cos要求输入shape为[min(T,T//cmp_ratio+B),rope_head_dim]。
- cu\_seqlens输入shape必须为[B+1,]。该参数中每个元素的值表示当前batch与之前所有batch的token数总和即前缀和因此后一个元素的值必须大于等于前一个元素的值且第一位必须位0。
- seqused支持输入shape[B,]要求每个Batch的有效token数要求小于等于对应Sequence Length长度即seqused[n] <= cu\_seqlens[n+1] - cu\_seqlens[n]且不小于0。
- kv\_block\_table、score\_block\_table支持输入shape[B,ceil(Smax/block_size)]。Smax为每个Batch中最大的Sequence Length即Smax=max(start\_pos)+max(cu\_seqlens[n+1] - cu\_seqlens[n])。
- cmp\_kv输出shape为[min(T,T//cmp_ratio+B),D]<batch0>compressed_tokens + <batch1>compressed_tokens + ... + <batchN>compressed_tokens + pad。
- wkv\_proj输出shape为[T,coff* D]。
- norm\_x输出shape为[min(T,T//cmp_ratio+B),D]。
- norm\_rstd输出shape为[min(T,T//cmp_ratio+B)]。
- 若x的维度不采用BS合轴即x的输入shape为[B,S,H]
- rope_sin、rope_cos要求输入shape为[B,ceil(S/cmp_ratio),rope_head_dim]。
- cu\_seqlens参数必须为空。
- seqused支持输入shape[B,]要求每个Batch的有效token数要求小于等于对应Sequence Length长度即要求seqused[n] <= S且不小于0。
- kv\_block\_table、score\_block\_table支持输入shape[B,ceil(Smax/block_size)]。Smax为每个Batch中最大的Sequence Length即Smax=max(start\_pos)+S。
- cmp\_kv输出shape为[B,ceil(S/cmp_ratio),D](<batch0>compressed_tokens+pad0) + (<batch1>compressed_tokens+pad1) + ... + (<batchN>compressed_tokens+padN)。
- wkv\_proj输出shape为[B,S,coff* D]。
- norm\_x输出shape为[B,ceil(S/cmp_ratio),D]。
- norm\_rstd输出shape为[B,ceil(S/cmp_ratio)]。
- 输入值域限制:
- 该接口支持B、S泛化且存在如下场景限制
- 部分长序列场景下如果计算量过大可能会导致出现超过NPU内存的报错这里计算量会受x输入shape的影响值越大计算量越大。典型的长序列即B、S的乘积或T较大场景包括但不限于
<div style="overflow-x: auto;">
<table style="undefined;table-layout: fixed; width: 400px"><colgroup>
<col style="width: 100px">
<col style="width: 100px">
</colgroup><thead>
<tr>
<th>B</th>
<th>S</th>
<th>H</th>
</tr></thead>
<tbody>
<tr>
<td>100</td>
<td>65525</td>
<td>4096</td>
</tr>
<tr>
<td>25</td>
<td>261120</td>
<td>4096</td>
</tr>
<tr>
<td>100</td>
<td>131072</td>
<td>4096</td>
</tr>
<tr>
<td>100</td>
<td>261120</td>
<td>4096</td>
</tr>
</tbody>
</table>
</div>
- 输入属性限制:
- 支持D为128/512。
- 支持H为1K~10K512对齐。
- 泛化支持block_size小于等于102416对齐。
- 支持cmp_ratio为4/128。支持如下三种情况
- C4A: D=512, coff=2, cmp_ratio=4
- C4Li: D=128, coff=2, cmp_ratio=4
- C128A: D=512, coff=1, cmp_ratio=128。
- 支持rotary_mode为2Rope计算模式为interleave。
## Atlas A3 推理系列产品 调用说明
- 单算子模式调用
```python
import torch
import torch_npu
import numpy as np
import custom_ops
import torch.nn as nn
import math
def get_seq_used_by_batch(batch_idx, S, seqused, cu_seqlens):
if seqused is not None:
return seqused[batch_idx]
else:
if cu_seqlens is not None:
return cu_seqlens[batch_idx + 1] - cu_seqlens[batch_idx]
else:
return S
data_type = torch.bfloat16
hidden_size = 4096
rope_head_dim = 64
norm_eps = 1e-6
coff = 1 # 1:no overlap 2:overlap
cmp_ratio = 128
rotary_mode = 2
head_dim = 512
cu_seqlens = [0, 1]
# -------------
B = 1
S = 1
S_max = 0
block_size = 128
start_pos = [8191] * B # (B,)
start_p=8191
seqused = None # (B,), None时cu_seqlens的数据全部参与计算否则按传参实际值计算
# BS是否合轴
bs_combine_flag = True
update_flag = 1
if seqused is not None:
seqused = torch.tensor(seqused).to(torch.int32)
if start_pos is not None:
start_pos = torch.tensor(start_pos).to(torch.int32)
else:
start_pos = torch.full((B,), start_p, dtype=torch.int32)
if bs_combine_flag:
if cu_seqlens is None:
T = B * S
if T !=0:
cu_seqlens = torch.arange(0, T + 1, S, dtype=torch.int32)
else:
cu_seqlens = torch.zeros((B+1), dtype=torch.int32)
else:
cu_seqlens = torch.tensor(cu_seqlens).to(torch.int32)
for i in range(B):
if start_pos[i] + cu_seqlens[i + 1] - cu_seqlens[i] > S_max:
S_max = start_pos[i] + cu_seqlens[i + 1] - cu_seqlens[i]
else:
cu_seqlens = None
S_max = max(start_pos) + S
### ======================== gen input data start =============================
# page state
max_block_num_per_batch = (S_max + block_size - 1) // block_size
block_num = B * max_block_num_per_batch
next_block_id = 1
print(f"max_block_num_per_batch: {max_block_num_per_batch}")
block_table = torch.zeros(size=(B, max_block_num_per_batch), dtype=torch.int32)
for i in range(B):
# 需要读取state的范围
cur_start = start_pos[i] // cmp_ratio * cmp_ratio - cmp_ratio
cur_end = start_pos[i] // cmp_ratio * cmp_ratio + cmp_ratio
if start_pos[i] % cmp_ratio == 0:
cur_end = start_pos[i]
cur_end = min(cur_end, start_pos[i] + S)
cur_start_block_id = (cur_start // block_size) if cur_start >= 0 else 0
cur_end_block_id = (cur_end - 1) // block_size
for j in range(cur_start_block_id, cur_end_block_id + 1):
block_table[i][j] = next_block_id
next_block_id = next_block_id + 1
# 需要写入state的范围
end_pos = get_seq_used_by_batch(i, S, seqused, cu_seqlens)
next_start = (start_pos[i] + end_pos) // cmp_ratio * cmp_ratio - cmp_ratio
next_end = (start_pos[i] + end_pos) // cmp_ratio * cmp_ratio + cmp_ratio
if (start_pos[i] + end_pos) % cmp_ratio == 0:
next_end = start_pos[i] + end_pos
next_end = min(next_end, start_pos[i] + end_pos)
next_start_block_id = (next_start // block_size) if next_start >= 0 else 0
next_end_block_id = (next_end - 1) // block_size
for j in range(next_start_block_id, next_end_block_id + 1):
if block_table[i][j] == 0:
block_table[i][j] = next_block_id
next_block_id = next_block_id + 1
if B==0:
kv_state = torch.tensor(np.random.uniform(-10, 10, (0, block_size, coff * head_dim))).to(torch.float32)
score_state = torch.tensor(np.random.uniform(-10, 10, (0, block_size, coff * head_dim))).to(torch.float32)
else:
kv_state = torch.tensor(np.random.uniform(-10, 10, (torch.max(block_table) + 1, block_size, coff * head_dim))).to(torch.float32)
score_state = torch.tensor(np.random.uniform(-10, 10, (torch.max(block_table) + 1, block_size, coff * head_dim))).to(torch.float32)
# other input
if bs_combine_flag:
x_shape = (cu_seqlens[-1], hidden_size)
rope_sin_shape = (min(x_shape[0], x_shape[0] // cmp_ratio + B), rope_head_dim)
rope_cos_shape = rope_sin_shape
else:
x_shape = (B, S, hidden_size)
rope_sin_shape = (B, (S + cmp_ratio - 1) // cmp_ratio, rope_head_dim)
rope_cos_shape = rope_sin_shape
x = torch.tensor(np.random.uniform(-10.0, 10.0, x_shape)).to(data_type).npu()
wkv = torch.tensor(np.random.uniform(-10, 10, (coff * head_dim, hidden_size))).to(data_type).npu()
wgate = torch.tensor(np.random.uniform(-10, 10, (coff * head_dim, hidden_size))).to(data_type).npu()
ape = torch.tensor(np.random.uniform(-10, 10, (cmp_ratio, coff * head_dim))).to(torch.float32).npu()
norm_weight = torch.tensor(np.random.uniform(-10, 10, (head_dim))).to(data_type).npu()
rope_sin = torch.tensor(np.random.uniform(-1, 1, rope_sin_shape)).to(data_type).npu()
rope_cos = torch.tensor(np.random.uniform(-1, 1, rope_cos_shape)).to(data_type).npu()
kv_state = kv_state.npu()
score_state = score_state.npu()
block_table = block_table.npu()
start_pos = torch.tensor(start_pos).to(torch.int32).npu()
if cu_seqlens is not None:
cu_seqlens = torch.tensor(cu_seqlens).to(torch.int32).npu()
if seqused is not None:
seqused = torch.tensor(seqused).to(torch.int32).npu()
cmp_kv,_ ,_ ,_ ,_ = (
torch.ops.custom.compressor(
x,
wkv,
wgate,
kv_state,
score_state,
ape,
norm_weight,
rope_sin,
rope_cos,
kv_block_table = block_table,
score_block_table = block_table,
cu_seqlens = cu_seqlens,
seqused = seqused,
start_pos = start_pos,
rope_head_dim = rope_head_dim,
cmp_ratio = cmp_ratio,
coff = coff,
norm_eps = norm_eps,
rotary_mode = rotary_mode
)
)
```
- aclgraph调用
```python
import torch
import torch_npu
import numpy as np
import torch.nn as nn
import torchair
import custom_ops
import math
def get_seq_used_by_batch(batch_idx, S, seqused, cu_seqlens):
if seqused is not None:
return seqused[batch_idx]
else:
if cu_seqlens is not None:
return cu_seqlens[batch_idx + 1] - cu_seqlens[batch_idx]
else:
return S
data_type = torch.bfloat16
hidden_size = 4096
rope_head_dim = 64
norm_eps = 1e-6
coff = 1 # 1:no overlap 2:overlap
cmp_ratio = 128
rotary_mode = 2
head_dim = 512
cu_seqlens = [0, 1]
# -------------
B = 1
S = 1
S_max = 0
block_size = 128
start_pos = [8191] * B # (B,)
start_p=8191
seqused = None # (B,), None时cu_seqlens的数据全部参与计算否则按传参实际值计算
# BS是否合轴
bs_combine_flag = True
update_flag = 1
if seqused is not None:
seqused = torch.tensor(seqused).to(torch.int32)
if start_pos is not None:
start_pos = torch.tensor(start_pos).to(torch.int32)
else:
start_pos = torch.full((B,), start_p, dtype=torch.int32)
if bs_combine_flag:
if cu_seqlens is None:
T = B * S
if T !=0:
cu_seqlens = torch.arange(0, T + 1, S, dtype=torch.int32)
else:
cu_seqlens = torch.zeros((B+1), dtype=torch.int32)
else:
cu_seqlens = torch.tensor(cu_seqlens).to(torch.int32)
for i in range(B):
if start_pos[i] + cu_seqlens[i + 1] - cu_seqlens[i] > S_max:
S_max = start_pos[i] + cu_seqlens[i + 1] - cu_seqlens[i]
else:
cu_seqlens = None
S_max = max(start_pos) + S
### ======================== gen input data start =============================
# page state
max_block_num_per_batch = (S_max + block_size - 1) // block_size
block_num = B * max_block_num_per_batch
next_block_id = 1
print(f"max_block_num_per_batch: {max_block_num_per_batch}")
block_table = torch.zeros(size=(B, max_block_num_per_batch), dtype=torch.int32)
for i in range(B):
# 需要读取state的范围
cur_start = start_pos[i] // cmp_ratio * cmp_ratio - cmp_ratio
cur_end = start_pos[i] // cmp_ratio * cmp_ratio + cmp_ratio
if start_pos[i] % cmp_ratio == 0:
cur_end = start_pos[i]
cur_end = min(cur_end, start_pos[i] + S)
cur_start_block_id = (cur_start // block_size) if cur_start >= 0 else 0
cur_end_block_id = (cur_end - 1) // block_size
for j in range(cur_start_block_id, cur_end_block_id + 1):
block_table[i][j] = next_block_id
next_block_id = next_block_id + 1
# 需要写入state的范围
end_pos = get_seq_used_by_batch(i, S, seqused, cu_seqlens)
next_start = (start_pos[i] + end_pos) // cmp_ratio * cmp_ratio - cmp_ratio
next_end = (start_pos[i] + end_pos) // cmp_ratio * cmp_ratio + cmp_ratio
if (start_pos[i] + end_pos) % cmp_ratio == 0:
next_end = start_pos[i] + end_pos
next_end = min(next_end, start_pos[i] + end_pos)
next_start_block_id = (next_start // block_size) if next_start >= 0 else 0
next_end_block_id = (next_end - 1) // block_size
for j in range(next_start_block_id, next_end_block_id + 1):
if block_table[i][j] == 0:
block_table[i][j] = next_block_id
next_block_id = next_block_id + 1
if B==0:
kv_state = torch.tensor(np.random.uniform(-10, 10, (0, block_size, coff * head_dim))).to(torch.float32)
score_state = torch.tensor(np.random.uniform(-10, 10, (0, block_size, coff * head_dim))).to(torch.float32)
else:
kv_state = torch.tensor(np.random.uniform(-10, 10, (torch.max(block_table) + 1, block_size, coff * head_dim))).to(torch.float32)
score_state = torch.tensor(np.random.uniform(-10, 10, (torch.max(block_table) + 1, block_size, coff * head_dim))).to(torch.float32)
# other input
if bs_combine_flag:
x_shape = (cu_seqlens[-1], hidden_size)
rope_sin_shape = (min(x_shape[0], x_shape[0] // cmp_ratio + B), rope_head_dim)
rope_cos_shape = rope_sin_shape
else:
x_shape = (B, S, hidden_size)
rope_sin_shape = (B, (S + cmp_ratio - 1) // cmp_ratio, rope_head_dim)
rope_cos_shape = rope_sin_shape
x = torch.tensor(np.random.uniform(-10.0, 10.0, x_shape)).to(data_type).npu()
wkv = torch.tensor(np.random.uniform(-10, 10, (coff * head_dim, hidden_size))).to(data_type).npu()
wgate = torch.tensor(np.random.uniform(-10, 10, (coff * head_dim, hidden_size))).to(data_type).npu()
ape = torch.tensor(np.random.uniform(-10, 10, (cmp_ratio, coff * head_dim))).to(torch.float32).npu()
norm_weight = torch.tensor(np.random.uniform(-10, 10, (head_dim))).to(data_type).npu()
rope_sin = torch.tensor(np.random.uniform(-1, 1, rope_sin_shape)).to(data_type).npu()
rope_cos = torch.tensor(np.random.uniform(-1, 1, rope_cos_shape)).to(data_type).npu()
kv_state = kv_state.npu()
score_state = score_state.npu()
block_table = block_table.npu()
start_pos = torch.tensor(start_pos).to(torch.int32).npu()
if cu_seqlens is not None:
cu_seqlens = torch.tensor(cu_seqlens).to(torch.int32).npu()
if seqused is not None:
seqused = torch.tensor(seqused).to(torch.int32).npu()
class CompressorNetwork(nn.Module):
def __init__(self):
super(CompressorNetwork, self).__init__()
def forward(self, x, wkv, wgate, kv_state, score_state, ape, norm_weight, rope_sin,
rope_cos, rope_head_dim, cmp_ratio, kv_block_table = None, score_block_table = None, cu_seqlens = None,
seqused = None, start_pos = None, coff = 1, norm_eps = 1e-6, rotary_mode = 1):
cmp_kv,_ ,_ ,_ ,_ = (
torch.ops.custom.compressor(
x,
wkv,
wgate,
kv_state,
score_state,
ape,
norm_weight,
rope_sin,
rope_cos,
kv_block_table = kv_block_table,
score_block_table = score_block_table,
cu_seqlens = cu_seqlens,
seqused = seqused,
start_pos = start_pos,
rope_head_dim = rope_head_dim,
cmp_ratio = cmp_ratio,
coff = coff,
norm_eps = norm_eps,
rotary_mode = rotary_mode
)
)
return cmp_kv
from torchair.configs.compiler_config import CompilerConfig
config = CompilerConfig()
npu_backend = torchair.get_npu_backend(compiler_config=config)
torch._dynamo.reset()
npu_mode = torch.compile(CompressorNetwork(), fullgraph=True, backend=npu_backend, dynamic=False)
cmp_kv = npu_mode(
x,
wkv,
wgate,
kv_state,
score_state,
ape,
norm_weight,
rope_sin,
rope_cos,
kv_block_table = block_table,
score_block_table = block_table,
cu_seqlens = cu_seqlens,
seqused = seqused,
start_pos = start_pos,
rope_head_dim = rope_head_dim,
cmp_ratio = cmp_ratio,
coff = coff,
norm_eps = norm_eps,
rotary_mode = rotary_mode)
```
更多使用示例见[pytest示例](./tests/pytest/README.md)。

View File

@@ -0,0 +1,40 @@
# -----------------------------------------------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------------------------------------------
add_op_to_compiled_list()
if (BUILD_OPEN_PROJECT)
target_sources(op_host_aclnn PRIVATE
compressor_def.cpp
)
endif()
add_ops_compile_options(
OP_NAME Compressor
OPTIONS --cce-auto-sync=off
-Wno-deprecated-declarations
-mllvm -cce-aicore-hoist-movemask=false
--op_relocatable_kernel_binary=true
)
if (NOT BUILD_OPS_RTY_KERNEL)
set(SUPPORTED_ARCHS arch32 arch35)
add_modules_sources(OPTYPE compressor ACLNNTYPE aclnn)
add_tiling_modules()
foreach(ARCH ${ARCH_DIRECTORY})
if(ARCH IN_LIST SUPPORTED_ARCHS)
target_sources(${OPHOST_NAME}_tiling_obj PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/${ARCH}/compressor_tiling.cpp
)
endif()
endforeach()
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,381 @@
/**
 * 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 compressor_tiling.h
* \brief
*/
#ifndef COMPRESSOR_TILING_H
#define COMPRESSOR_TILING_H
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <sstream>
#include "register/tilingdata_base.h"
#include "tiling/tiling_api.h"
#include "exe_graph/runtime/tiling_context.h"
#include "register/op_def_registry.h"
#include "../../op_kernel/arch32/compressor_template_tiling_key.h"
#include "../../op_kernel/arch32/compressor_tiling_data.h"
#include "platform/platform_info.h"
#ifdef ASCENDC_OP_TEST
#define CMP_EXTERN_C extern "C"
#else
#define CMP_EXTERN_C
#endif
// #define DAY0_SCOPE
namespace optiling {
// INPUT
constexpr uint32_t TOKEN_X_INPUT_INDEX = 0;
constexpr uint32_t WEIGHT_KV_INPUT_INDEX = 1;
constexpr uint32_t WEIGHT_WGATE_INPUT_INDEX = 2;
constexpr uint32_t STATE_CACHE_INPUT_INDEX = 3;
constexpr uint32_t APE_INPUT_INDEX = 4;
constexpr uint32_t NORM_WEIGHT_INPUT_INDEX = 5;
constexpr uint32_t ROPE_SIN_INPUT_INDEX = 6;
constexpr uint32_t ROPE_COS_INPUT_INDEX = 7;
// INPUT(OPTION)
constexpr uint32_t STATE_BLOCK_TABLE_INPUT_INDEX = 8;
constexpr uint32_t CU_SEQ_LEN_INPUT_INDEX = 9;
constexpr uint32_t SEQ_USED_INPUT_INDEX = 10;
constexpr uint32_t START_POS_INPUT_INDEX = 11;
// ATTR
constexpr uint32_t ROPE_HEAD_DIM_ATTR_INDEX = 0;
constexpr uint32_t CMP_RATIO_ATTR_INDEX = 1;
constexpr uint32_t COFF_ATTR_INDEX = 2;
constexpr uint32_t NORM_EPS_ATTR_INDEX = 3;
constexpr uint32_t ROTARY_MODE_ATTR_INDEX = 4;
constexpr uint32_t CACHE_MODE_ATTR_INDEX = 5;
constexpr uint32_t STATE_CACHE_STRIDE_DIM0_ATTR_INDEX = 6;
// OUTPUT
constexpr uint32_t CMP_KV_OUTPUT_INDEX = 0;
constexpr uint32_t COMPRESSOR_DIM_NUM_1 = 1;
constexpr uint32_t COMPRESSOR_DIM_NUM_2 = 2;
constexpr uint32_t COMPRESSOR_DIM_NUM_3 = 3;
constexpr uint32_t COMPRESSOR_DIM_NUM_4 = 4;
constexpr uint32_t COMPRESSOR_DIM_INDEX_0 = 0;
constexpr uint32_t COMPRESSOR_DIM_INDEX_1 = 1;
constexpr uint32_t COMPRESSOR_DIM_INDEX_2 = 2;
constexpr uint32_t COMPRESSOR_DIM_INDEX_3 = 3;
// CONSTRAINTS
constexpr uint32_t MAX_HIDDEN_SIZE = 10240;
constexpr uint32_t MIN_HIDDEN_SIZE = 1024;
constexpr uint32_t ALIGN_FACTOR_HIDDEN_SIZE = 512;
constexpr uint32_t MIN_BLOCK_SIZE = 1;
constexpr uint32_t BATCH_MODE_SCHEDULE = 1;
static const std::string X_NAME = "query";
static const std::string WKV_NAME = "wkv";
static const std::string WGATE_NAME = "wgate";
static const std::string STATE_CACHE_NAME = "state_cache";
static const std::string APE_NAME = "ape";
static const std::string NORM_WEIGHT_NAME = "norm_weight";
static const std::string ROPE_SIN_NAME = "rope_sin";
static const std::string ROPE_COS_NAME = "rope_cos";
static const std::string STATE_BLOCK_TABLE_NAME = "state_block_table";
static const std::string CU_SEQLENS_NAME = "cu_seqlens";
static const std::string SEQUSED_NAME = "seq_used";
static const std::string START_POS_NAME = "start_pos";
static const std::string ROPE_HEAD_DIM_NAME = "rope_head_dim";
static const std::string CMP_RATIO_NAME = "cmp_ratio";
static const std::string COFF_NAME = "coff";
static const std::string NORM_EPS_NAME = "nrom_eps";
static const std::string ROTARY_MODE_NAME = "rotary_mode";
static const std::string CACHE_MODE_NAME = "cache_mode";
static const std::string CMP_KV_NAME = "cmp_kv";
static std::string DataTypeToSerialString(ge::DataType type);
const std::map<std::string, std::vector<ge::DataType>> DTYPE_SUPPORT_MAP = {
{X_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{WKV_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{WGATE_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{STATE_CACHE_NAME, {ge::DT_FLOAT}},
{APE_NAME, {ge::DT_FLOAT}},
{NORM_WEIGHT_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{ROPE_SIN_NAME, {ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT}},
{ROPE_COS_NAME, {ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT}},
{STATE_BLOCK_TABLE_NAME, {ge::DT_INT32}},
{CU_SEQLENS_NAME, {ge::DT_INT32}},
{SEQUSED_NAME, {ge::DT_INT32}},
{START_POS_NAME, {ge::DT_INT32}},
{CMP_KV_NAME, {ge::DT_BF16, ge::DT_FLOAT16}}
};
const std::map<std::string, std::vector<uint32_t>> DIM_NUM_MAP = {
{X_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}},
{WKV_NAME, {COMPRESSOR_DIM_NUM_2}},
{WGATE_NAME, {COMPRESSOR_DIM_NUM_2}},
{STATE_CACHE_NAME, {COMPRESSOR_DIM_NUM_3}},
{APE_NAME, {COMPRESSOR_DIM_NUM_2}},
{NORM_WEIGHT_NAME, {COMPRESSOR_DIM_NUM_1}},
{ROPE_SIN_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}},
{ROPE_COS_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}},
{STATE_BLOCK_TABLE_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_1}},
{CU_SEQLENS_NAME, {COMPRESSOR_DIM_NUM_1}},
{SEQUSED_NAME, {COMPRESSOR_DIM_NUM_1}},
{START_POS_NAME, {COMPRESSOR_DIM_NUM_1}},
{CMP_KV_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}}
};
static const std::map<std::string, uint32_t> LAYOUT_DIM_MAP = {
{"BSH", COMPRESSOR_DIM_NUM_3},
{"TH", COMPRESSOR_DIM_NUM_2},
};
const std::map<ge::DataType, std::string> DATATYPE_TO_STRING_MAP = {
{ge::DT_UNDEFINED, "DT_UNDEFINED"}, // Used to indicate a DataType field has not been set.
{ge::DT_FLOAT, "DT_FLOAT"}, // float type
{ge::DT_FLOAT16, "DT_FLOAT16"}, // fp16 type
{ge::DT_INT8, "DT_INT8"}, // int8 type
{ge::DT_INT16, "DT_INT16"}, // int16 type
{ge::DT_UINT16, "DT_UINT16"}, // uint16 type
{ge::DT_UINT8, "DT_UINT8"}, // uint8 type
{ge::DT_INT32, "DT_INT32"}, // uint32 type
{ge::DT_INT64, "DT_INT64"}, // int64 type
{ge::DT_UINT32, "DT_UINT32"}, // unsigned int32
{ge::DT_UINT64, "DT_UINT64"}, // unsigned int64
{ge::DT_BOOL, "DT_BOOL"}, // bool type
{ge::DT_DOUBLE, "DT_DOUBLE"}, // double type
{ge::DT_DUAL, "DT_DUAL"}, // dual output type
{ge::DT_DUAL_SUB_INT8, "DT_DUAL_SUB_INT8"}, // dual output int8 type
{ge::DT_DUAL_SUB_UINT8, "DT_DUAL_SUB_UINT8"}, // dual output uint8 type
{ge::DT_COMPLEX32, "DT_COMPLEX32"}, // complex32 type
{ge::DT_COMPLEX64, "DT_COMPLEX64"}, // complex64 type
{ge::DT_COMPLEX128, "DT_COMPLEX128"}, // complex128 type
{ge::DT_QINT8, "DT_QINT8"}, // qint8 type
{ge::DT_QINT16, "DT_QINT16"}, // qint16 type
{ge::DT_QINT32, "DT_QINT32"}, // qint32 type
{ge::DT_QUINT8, "DT_QUINT8"}, // quint8 type
{ge::DT_QUINT16, "DT_QUINT16"}, // quint16 type
{ge::DT_RESOURCE, "DT_RESOURCE"}, // resource type
{ge::DT_STRING_REF, "DT_STRING_REF"}, // string ref type
{ge::DT_STRING, "DT_STRING"}, // string type
{ge::DT_VARIANT, "DT_VARIANT"}, // dt_variant type
{ge::DT_BF16, "DT_BFLOAT16"}, // dt_bfloat16 type
{ge::DT_INT4, "DT_INT4"}, // dt_variant type
{ge::DT_UINT1, "DT_UINT1"}, // dt_variant type
{ge::DT_INT2, "DT_INT2"}, // dt_variant type
{ge::DT_UINT2, "DT_UINT2"} // dt_variant type
};
struct CompressorCompileInfo {
int64_t core_num;
};
struct RequiredParaInfo {
const gert::CompileTimeTensorDesc *desc;
const gert::StorageShape *shape;
};
struct OptionalParaInfo {
const gert::CompileTimeTensorDesc *desc;
const gert::StorageShape *shape;
const gert::Tensor *tensor;
};
enum class LayoutType {
LAYOUT_BSH,
LAYOUT_TH
};
enum class TemplateId:uint8_t {
NORMAL = 0,
EMPTY_X = 1,
PERF = 2
};
CMP_EXTERN_C ge::graphStatus TilingCompressor(gert::TilingContext *context);
struct CompressorBaseShapeInfo {
uint32_t bSize = 0; // B
uint32_t sSize = 0; // S
uint32_t hSize = 0; // Hidden size
uint32_t tSize = 0; // T
uint32_t nSize = 0; // N
uint32_t dSize = 0; // D
uint32_t coffSize = 0; // Coff: 1 or 2
uint32_t csSize = 0; // Compress sequence len
uint32_t rSize = 0; // Compress ratio
uint32_t cgSize = 0; // Compress group size
uint32_t drSize = 0; // Dr
};
const std::vector<int> ROPE_HEAD_DIM {64};
const std::vector<int> COFF {1, 2};
#ifdef DAY0_SCOPE
const std::vector<int> CMP_RATIO {4, 128};
const std::vector<int> ROTARY_MODE {2};
#else
const std::vector<int> CMP_RATIO {2, 4, 8, 16, 32, 64, 128};
const std::vector<int> ROTARY_MODE {1, 2};
#endif
const std::vector<uint32_t> HEAD_DIM {128, 512};
const std::vector<int> CACHE_MODE {1};
enum class ROTARY_MODE:uint8_t {
HALF = 1,
INTERLEAVE = 2
};
enum class CACHE_MODE:uint8_t {
CONTINUOUS = 1,
CYCLE = 2
};
struct CompressorContext {
const char *opName;
const char *opType;
fe::PlatFormInfos *platformInfo;
RequiredParaInfo x;
RequiredParaInfo wkv;
RequiredParaInfo wgate;
RequiredParaInfo stateCache;
RequiredParaInfo ape;
RequiredParaInfo normWeight;
RequiredParaInfo ropeSin;
RequiredParaInfo ropeCos;
OptionalParaInfo stateBlockTable;
OptionalParaInfo cuSeqlens;
OptionalParaInfo seqUsed;
OptionalParaInfo startPos;
RequiredParaInfo cmpKv;
const int *ropeHeadDim;
const int *coff;
const int *cmpRatio;
const float *normEps;
const int *rotaryMode;
const int *cacheMode;
const int *stateCacheStrideDim0;
TemplateId templateId;
ge::DataType dtype = ge::DT_BF16;
LayoutType layout = LayoutType::LAYOUT_BSH;
size_t *workSpaces;
uint64_t tilingKey;
uint32_t blockDim;
};
class CompressorTiling {
public:
explicit CompressorTiling(CompressorContext *context) : context_(context) {}
~CompressorTiling() = default;
static ge::graphStatus ConvertContext(gert::TilingContext &context, CompressorContext &compressorContext);
ge::graphStatus RunBigKernelTiling(CompressorTilingData* tilingData);
private:
static void ConvertRequiredParams(gert::TilingContext &context, CompressorContext &compressorContext);
static void ConvertOptionalParams(gert::TilingContext &context, CompressorContext &compressorContext);
ge::graphStatus GetNpuInfo();
ge::graphStatus SetBaseInfo();
ge::graphStatus SetPageAttentionInfo();
ge::graphStatus SetWorkSpaceInfo();
ge::graphStatus SetScenarioInfo();
ge::graphStatus SetTemplateId();
ge::graphStatus SetInnerSplitInfo();
ge::graphStatus CalcWorkSpace();
ge::graphStatus CheckSinglePara() const;
ge::graphStatus GenTilingKey() const;
template <typename T>
ge::graphStatus CheckFeatureValueSupport(const T *featureValue, const std::vector<T> &expectFeatureValList,
const std::string &name) const;
template <typename T>
ge::graphStatus CheckAttrValueSupport(const T *attrValue, const std::vector<T> &expectAttrValList,
const std::string &name) const;
template <typename T>
void LogErrorNumberSupport(const std::vector<T> &expectNumberList, const T &actualValue, const std::string &name,
const std::string subName) const;
ge::graphStatus CheckDimNumInLayoutSupport(const std::string &layout, const gert::StorageShape *shape,
const std::string &name) const;
ge::graphStatus CheckDtypeSupport(const gert::CompileTimeTensorDesc *desc, const std::string &name) const;
void LogErrorDtypeSupport(const std::vector<ge::DataType> &expectDtypeList, const ge::DataType &actualDtype,
const std::string &name) const;
ge::graphStatus CheckDimNumSupport(const gert::StorageShape *shape, const std::string &name) const;
ge::graphStatus LogErrorShapeConsistency(const std::string &name, const gert::StorageShape *shape,
const uint32_t &dimNum, const std::string &subName,
const uint32_t &expectNum) const;
ge::graphStatus CheckSingleParaX() const;
ge::graphStatus CheckSingleParaWkv() const;
ge::graphStatus CheckSingleParaWgate() const;
ge::graphStatus CheckSingleParaStateCache() const;
ge::graphStatus CheckSingleParaApe() const;
ge::graphStatus CheckSingleParaNormWeight() const;
ge::graphStatus CheckSingleParaRopeSin() const;
ge::graphStatus CheckSingleParaRopeCos() const;
ge::graphStatus CheckSingleParaStateBlockTable() const;
ge::graphStatus CheckSingleParaCuSeqlens() const;
ge::graphStatus CheckSingleParaSeqused() const;
ge::graphStatus CheckSingleParaStartPos() const;
ge::graphStatus CheckSingleParaCmpKv() const;
ge::graphStatus CheckSingleParaRopeHeadDim() const;
ge::graphStatus CheckSingleParaCmpRatio() const;
ge::graphStatus CheckSingleParaCoff() const;
ge::graphStatus CheckSingleParaNormEps() const;
ge::graphStatus CheckSingleParaRotaryMode() const;
ge::graphStatus CheckSingleParaCacheMode() const;
ge::graphStatus CheckRequiredParaExistence() const;
ge::graphStatus CheckRequiredInOutExistence() const;
ge::graphStatus CheckRequiredAttrExistence() const;
ge::graphStatus CheckFeature() const;
ge::graphStatus CheckShapeConsistency() const;
ge::graphStatus CheckShapeConsistencyRope() const;
ge::graphStatus CheckDtypeConsistencyX(const gert::CompileTimeTensorDesc *desc, const std::string &name) const;
ge::graphStatus CheckDtypeConsistencyRope() const;
ge::graphStatus CheckDtypeConsistency() const;
ge::graphStatus CheckMultiParaConsistency() const;
ge::graphStatus CheckDimNumConsistency() const;
ge::graphStatus CheckEmptyTensor() const;
ge::graphStatus CheckScenarioConsistency() const;
ge::graphStatus CheckBlockDimConstrain() const;
size_t ubSize_ = 0;
size_t l1Size_ = 0;
size_t l0cSize_ = 0;
size_t l0bSize_ = 0;
uint32_t coreNum_ = 0;
uint32_t aicNum_ = 0;
uint32_t aivNum_ = 0;
platform_ascendc::SocVersion socVersion_ = platform_ascendc::SocVersion::ASCEND910B;
size_t libapiSize_ = 0;
size_t workspaceSize_ = 0;
uint8_t coff = 1;
uint32_t mBaseSize = 0;
uint32_t dbaseSize = 0;
CompressorBaseShapeInfo baseShapeInfo_;
CompressorContext *context_ = nullptr;
CompressorBaseParams *baseParams_ = nullptr;
CompressorPageAttentionParams *pageAttentionParams_ = nullptr;
CompressorInnerSplitParams *innerSplitParams_ = nullptr;
CompressorWorkspaceParams *workspaceParams_ = nullptr;
};
} // optiling
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,375 @@
/**
 * 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 compressor_tiling.h
* \brief
*/
#ifndef COMPRESSOR_TILING_H
#define COMPRESSOR_TILING_H
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <sstream>
#include "register/tilingdata_base.h"
#include "tiling/tiling_api.h"
#include "exe_graph/runtime/tiling_context.h"
#include "register/op_def_registry.h"
#include "../../op_kernel/arch35/compressor_template_tiling_key.h"
#include "../../op_kernel/arch35/compressor_tiling_data.h"
#include "platform/platform_info.h"
#ifdef ASCENDC_OP_TEST
#define CMP_EXTERN_C extern "C"
#else
#define CMP_EXTERN_C
#endif
namespace optiling {
// INPUT
constexpr uint32_t TOKEN_X_INPUT_INDEX = 0;
constexpr uint32_t WEIGHT_KV_INPUT_INDEX = 1;
constexpr uint32_t WEIGHT_WGATE_INPUT_INDEX = 2;
constexpr uint32_t STATE_CACHE_INPUT_INDEX = 3;
constexpr uint32_t APE_INPUT_INDEX = 4;
constexpr uint32_t NORM_WEIGHT_INPUT_INDEX = 5;
constexpr uint32_t ROPE_SIN_INPUT_INDEX = 6;
constexpr uint32_t ROPE_COS_INPUT_INDEX = 7;
// INPUT(OPTION)
constexpr uint32_t STATE_BLOCK_TABLE_INPUT_INDEX = 8;
constexpr uint32_t CU_SEQ_LEN_INPUT_INDEX = 9;
constexpr uint32_t SEQ_USED_INPUT_INDEX = 10;
constexpr uint32_t START_POS_INPUT_INDEX = 11;
// ATTR
constexpr uint32_t ROPE_HEAD_DIM_ATTR_INDEX = 0;
constexpr uint32_t CMP_RATIO_ATTR_INDEX = 1;
constexpr uint32_t COFF_ATTR_INDEX = 2;
constexpr uint32_t NORM_EPS_ATTR_INDEX = 3;
constexpr uint32_t ROTARY_MODE_ATTR_INDEX = 4;
constexpr uint32_t CACHE_MODE_ATTR_INDEX = 5;
constexpr uint32_t STATE_CACHE_STRIDE_DIM0_ATTR_INDEX = 6;
// OUTPUT
constexpr uint32_t CMP_KV_OUTPUT_INDEX = 0;
constexpr uint32_t COMPRESSOR_DIM_NUM_1 = 1;
constexpr uint32_t COMPRESSOR_DIM_NUM_2 = 2;
constexpr uint32_t COMPRESSOR_DIM_NUM_3 = 3;
constexpr uint32_t COMPRESSOR_DIM_NUM_4 = 4;
constexpr uint32_t COMPRESSOR_DIM_INDEX_0 = 0;
constexpr uint32_t COMPRESSOR_DIM_INDEX_1 = 1;
constexpr uint32_t COMPRESSOR_DIM_INDEX_2 = 2;
constexpr uint32_t COMPRESSOR_DIM_INDEX_3 = 3;
// CONSTRAINTS
constexpr uint32_t MAX_HIDDEN_SIZE = 10240;
constexpr uint32_t MIN_HIDDEN_SIZE = 1024;
constexpr uint32_t ALIGN_FACTOR_HIDDEN_SIZE = 512;
constexpr uint32_t MIN_BLOCK_SIZE = 1;
constexpr uint32_t BATCH_MODE_SCHEDULE = 1;
static const std::string X_NAME = "query";
static const std::string WKV_NAME = "wkv";
static const std::string WGATE_NAME = "wgate";
static const std::string STATE_CACHE_NAME = "state_cache";
static const std::string APE_NAME = "ape";
static const std::string NORM_WEIGHT_NAME = "norm_weight";
static const std::string ROPE_SIN_NAME = "rope_sin";
static const std::string ROPE_COS_NAME = "rope_cos";
static const std::string STATE_BLOCK_TABLE_NAME = "state_block_table";
static const std::string CU_SEQLENS_NAME = "cu_seqlens";
static const std::string SEQUSED_NAME = "seq_used";
static const std::string START_POS_NAME = "start_pos";
static const std::string ROPE_HEAD_DIM_NAME = "rope_head_dim";
static const std::string CMP_RATIO_NAME = "cmp_ratio";
static const std::string COFF_NAME = "coff";
static const std::string NORM_EPS_NAME = "nrom_eps";
static const std::string ROTARY_MODE_NAME = "rotary_mode";
static const std::string CACHE_MODE_NAME = "cache_mode";
static const std::string CMP_KV_NAME = "cmp_kv";
static std::string DataTypeToSerialString(ge::DataType type);
const std::map<std::string, std::vector<ge::DataType>> DTYPE_SUPPORT_MAP = {
{X_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{WKV_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{WGATE_NAME, {ge::DT_BF16, ge::DT_FLOAT16}},
{STATE_CACHE_NAME, {ge::DT_FLOAT}},
{APE_NAME, {ge::DT_FLOAT}},
{NORM_WEIGHT_NAME, {ge::DT_FLOAT}},
{ROPE_SIN_NAME, {ge::DT_FLOAT}},
{ROPE_COS_NAME, {ge::DT_FLOAT}},
{STATE_BLOCK_TABLE_NAME, {ge::DT_INT32}},
{CU_SEQLENS_NAME, {ge::DT_INT32}},
{SEQUSED_NAME, {ge::DT_INT32}},
{START_POS_NAME, {ge::DT_INT32}},
{CMP_KV_NAME, {ge::DT_BF16, ge::DT_FLOAT16}}
};
const std::map<std::string, std::vector<uint32_t>> DIM_NUM_MAP = {
{X_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}},
{WKV_NAME, {COMPRESSOR_DIM_NUM_2}},
{WGATE_NAME, {COMPRESSOR_DIM_NUM_2}},
{STATE_CACHE_NAME, {COMPRESSOR_DIM_NUM_3}},
{APE_NAME, {COMPRESSOR_DIM_NUM_2}},
{NORM_WEIGHT_NAME, {COMPRESSOR_DIM_NUM_1}},
{ROPE_SIN_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}},
{ROPE_COS_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}},
{STATE_BLOCK_TABLE_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_1}},
{CU_SEQLENS_NAME, {COMPRESSOR_DIM_NUM_1}},
{SEQUSED_NAME, {COMPRESSOR_DIM_NUM_1}},
{START_POS_NAME, {COMPRESSOR_DIM_NUM_1}},
{CMP_KV_NAME, {COMPRESSOR_DIM_NUM_2, COMPRESSOR_DIM_NUM_3}}
};
static const std::map<std::string, uint32_t> LAYOUT_DIM_MAP = {
{"BSH", COMPRESSOR_DIM_NUM_3},
{"TH", COMPRESSOR_DIM_NUM_2},
};
const std::map<ge::DataType, std::string> DATATYPE_TO_STRING_MAP = {
{ge::DT_UNDEFINED, "DT_UNDEFINED"}, // Used to indicate a DataType field has not been set.
{ge::DT_FLOAT, "DT_FLOAT"}, // float type
{ge::DT_FLOAT16, "DT_FLOAT16"}, // fp16 type
{ge::DT_INT8, "DT_INT8"}, // int8 type
{ge::DT_INT16, "DT_INT16"}, // int16 type
{ge::DT_UINT16, "DT_UINT16"}, // uint16 type
{ge::DT_UINT8, "DT_UINT8"}, // uint8 type
{ge::DT_INT32, "DT_INT32"}, // uint32 type
{ge::DT_INT64, "DT_INT64"}, // int64 type
{ge::DT_UINT32, "DT_UINT32"}, // unsigned int32
{ge::DT_UINT64, "DT_UINT64"}, // unsigned int64
{ge::DT_BOOL, "DT_BOOL"}, // bool type
{ge::DT_DOUBLE, "DT_DOUBLE"}, // double type
{ge::DT_DUAL, "DT_DUAL"}, // dual output type
{ge::DT_DUAL_SUB_INT8, "DT_DUAL_SUB_INT8"}, // dual output int8 type
{ge::DT_DUAL_SUB_UINT8, "DT_DUAL_SUB_UINT8"}, // dual output uint8 type
{ge::DT_COMPLEX32, "DT_COMPLEX32"}, // complex32 type
{ge::DT_COMPLEX64, "DT_COMPLEX64"}, // complex64 type
{ge::DT_COMPLEX128, "DT_COMPLEX128"}, // complex128 type
{ge::DT_QINT8, "DT_QINT8"}, // qint8 type
{ge::DT_QINT16, "DT_QINT16"}, // qint16 type
{ge::DT_QINT32, "DT_QINT32"}, // qint32 type
{ge::DT_QUINT8, "DT_QUINT8"}, // quint8 type
{ge::DT_QUINT16, "DT_QUINT16"}, // quint16 type
{ge::DT_RESOURCE, "DT_RESOURCE"}, // resource type
{ge::DT_STRING_REF, "DT_STRING_REF"}, // string ref type
{ge::DT_STRING, "DT_STRING"}, // string type
{ge::DT_VARIANT, "DT_VARIANT"}, // dt_variant type
{ge::DT_BF16, "DT_BFLOAT16"}, // dt_bfloat16 type
{ge::DT_INT4, "DT_INT4"}, // dt_variant type
{ge::DT_UINT1, "DT_UINT1"}, // dt_variant type
{ge::DT_INT2, "DT_INT2"}, // dt_variant type
{ge::DT_UINT2, "DT_UINT2"} // dt_variant type
};
struct CompressorCompileInfo {
int64_t core_num;
};
struct RequiredParaInfo {
const gert::CompileTimeTensorDesc *desc;
const gert::StorageShape *shape;
};
struct OptionalParaInfo {
const gert::CompileTimeTensorDesc *desc;
const gert::StorageShape *shape;
const gert::Tensor *tensor;
};
enum class LayoutType {
LAYOUT_BSH,
LAYOUT_TH
};
enum class TemplateId:uint8_t {
NORMAL = 0,
EMPTY_X = 1,
FULL_LOAD = 2
};
CMP_EXTERN_C ge::graphStatus TilingCompressor(gert::TilingContext *context);
struct CompressorBaseShapeInfo {
uint32_t bSize = 0; // B
uint32_t sSize = 0; // S
uint32_t hSize = 0; // Hidden size
uint32_t tSize = 0; // T
uint32_t nSize = 0; // N
uint32_t dSize = 0; // D
uint32_t coffSize = 0; // Coff: 1 or 2
uint32_t csSize = 0; // Compress sequence len
uint32_t rSize = 0; // Compress ratio
uint32_t cgSize = 0; // Compress group size
uint32_t drSize = 0; // Dr
};
const std::vector<int> ROPE_HEAD_DIM {64};
const std::vector<int> COFF {1, 2};
const std::vector<int> CMP_RATIO {2, 4, 8, 16, 32, 64, 128};
const std::vector<int> ROTARY_MODE {1, 2};
const std::vector<uint32_t> HEAD_DIM {128, 512};
const std::vector<int> CACHE_MODE {1, 2};
enum class ROTARY_MODE:uint8_t {
HALF = 1,
INTERLEAVE = 2
};
enum class CACHE_MODE:uint8_t {
CONTINUOUS = 1,
CYCLE = 2
};
struct CompressorContext {
const char *opName;
const char *opType;
fe::PlatFormInfos *platformInfo;
RequiredParaInfo x;
RequiredParaInfo wkv;
RequiredParaInfo wgate;
RequiredParaInfo stateCache;
RequiredParaInfo ape;
RequiredParaInfo normWeight;
RequiredParaInfo ropeSin;
RequiredParaInfo ropeCos;
OptionalParaInfo stateBlockTable;
OptionalParaInfo cuSeqlens;
OptionalParaInfo seqUsed;
OptionalParaInfo startPos;
RequiredParaInfo cmpKv;
const int *ropeHeadDim;
const int *coff;
const int *cmpRatio;
const float *normEps;
const int *rotaryMode;
const int *cacheMode;
const int *stateCacheStrideDim0;
TemplateId templateId;
ge::DataType dtype = ge::DT_BF16;
LayoutType layout = LayoutType::LAYOUT_BSH;
size_t *workSpaces;
uint64_t tilingKey;
uint32_t blockDim;
};
class CompressorTiling {
public:
explicit CompressorTiling(CompressorContext *context) : context_(context) {}
~CompressorTiling() = default;
static ge::graphStatus ConvertContext(gert::TilingContext &context, CompressorContext &compressorContext);
ge::graphStatus RunBigKernelTiling(CompressorTilingData* tilingData);
private:
static void ConvertRequiredParams(gert::TilingContext &context, CompressorContext &compressorContext);
static void ConvertOptionalParams(gert::TilingContext &context, CompressorContext &compressorContext);
ge::graphStatus GetNpuInfo();
ge::graphStatus SetBaseInfo();
ge::graphStatus SetPageAttentionInfo();
ge::graphStatus SetWorkSpaceInfo();
ge::graphStatus SetScenarioInfo();
ge::graphStatus SetTemplateId();
ge::graphStatus SetInnerSplitInfo();
ge::graphStatus CalcWorkSpace();
ge::graphStatus CheckSinglePara() const;
ge::graphStatus GenTilingKey() const;
template <typename T>
ge::graphStatus CheckFeatureValueSupport(const T *featureValue, const std::vector<T> &expectFeatureValList,
const std::string &name) const;
template <typename T>
ge::graphStatus CheckAttrValueSupport(const T *attrValue, const std::vector<T> &expectAttrValList,
const std::string &name) const;
template <typename T>
void LogErrorNumberSupport(const std::vector<T> &expectNumberList, const T &actualValue, const std::string &name,
const std::string subName) const;
ge::graphStatus CheckDimNumInLayoutSupport(const std::string &layout, const gert::StorageShape *shape,
const std::string &name) const;
ge::graphStatus CheckDtypeSupport(const gert::CompileTimeTensorDesc *desc, const std::string &name) const;
void LogErrorDtypeSupport(const std::vector<ge::DataType> &expectDtypeList, const ge::DataType &actualDtype,
const std::string &name) const;
ge::graphStatus CheckDimNumSupport(const gert::StorageShape *shape, const std::string &name) const;
ge::graphStatus LogErrorShapeConsistency(const std::string &name, const gert::StorageShape *shape,
const uint32_t &dimNum, const std::string &subName,
const uint32_t &expectNum) const;
ge::graphStatus CheckSingleParaX() const;
ge::graphStatus CheckSingleParaWkv() const;
ge::graphStatus CheckSingleParaWgate() const;
ge::graphStatus CheckSingleParaStateCache() const;
ge::graphStatus CheckSingleParaApe() const;
ge::graphStatus CheckSingleParaNormWeight() const;
ge::graphStatus CheckSingleParaRopeSin() const;
ge::graphStatus CheckSingleParaRopeCos() const;
ge::graphStatus CheckSingleParaStateBlockTable() const;
ge::graphStatus CheckSingleParaCuSeqlens() const;
ge::graphStatus CheckSingleParaSeqused() const;
ge::graphStatus CheckSingleParaStartPos() const;
ge::graphStatus CheckSingleParaCmpKv() const;
ge::graphStatus CheckSingleParaRopeHeadDim() const;
ge::graphStatus CheckSingleParaCmpRatio() const;
ge::graphStatus CheckSingleParaCoff() const;
ge::graphStatus CheckSingleParaNormEps() const;
ge::graphStatus CheckSingleParaRotaryMode() const;
ge::graphStatus CheckSingleParaCacheMode() const;
ge::graphStatus CheckRequiredParaExistence() const;
ge::graphStatus CheckRequiredInOutExistence() const;
ge::graphStatus CheckRequiredAttrExistence() const;
ge::graphStatus CheckFeature() const;
ge::graphStatus CheckShapeConsistency() const;
ge::graphStatus CheckShapeConsistencyRope() const;
ge::graphStatus CheckDtypeConsistencyX(const gert::CompileTimeTensorDesc *desc, const std::string &name) const;
ge::graphStatus CheckDtypeConsistencyFp32(const gert::CompileTimeTensorDesc *desc, const std::string &name) const;
ge::graphStatus CheckDtypeConsistency() const;
ge::graphStatus CheckMultiParaConsistency() const;
ge::graphStatus CheckDimNumConsistency() const;
ge::graphStatus CheckEmptyTensor() const;
ge::graphStatus CheckScenarioConsistency() const;
ge::graphStatus CheckBlockDimConstrain() const;
size_t ubSize_ = 0;
size_t l1Size_ = 0;
size_t l0cSize_ = 0;
size_t l0bSize_ = 0;
uint32_t coreNum_ = 0;
uint32_t aicNum_ = 0;
uint32_t aivNum_ = 0;
platform_ascendc::SocVersion socVersion_ = platform_ascendc::SocVersion::ASCEND910B;
size_t libapiSize_ = 0;
size_t workspaceSize_ = 0;
uint8_t coff = 1;
uint32_t mBaseSize = 0;
uint32_t dbaseSize = 0;
CompressorBaseShapeInfo baseShapeInfo_;
CompressorContext *context_ = nullptr;
CompressorBaseParams *baseParams_ = nullptr;
CompressorPageAttentionParams *pageAttentionParams_ = nullptr;
CompressorInnerSplitParams *innerSplitParams_ = nullptr;
CompressorWorkspaceParams *workspaceParams_ = nullptr;
};
} // optiling
#endif

View File

@@ -0,0 +1,191 @@
/**
 * 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.
 */
#include "register/op_def_registry.h"
namespace ops {
class Compressor : public OpDef {
public:
static constexpr uint32_t ROPE_HEAD_DIM_VALUE = 64;
static constexpr uint32_t CMP_RATIO_VALUE = 4;
static constexpr uint32_t COFF_VALUE = 1;
static constexpr uint32_t ROTARY_MODE_VALUE = 1;
static constexpr uint32_t CACHE_MODE_VALUE = 1;
static constexpr uint32_t STATE_CACHE_STRIDE_DIM0 = 0;
explicit Compressor(const char *name) : OpDef(name)
{
this->Input("x")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("wkv")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("wgate")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("state_cache")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.IgnoreContiguous();
this->Input("ape")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("norm_weight")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("rope_sin")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("rope_cos")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("state_block_table")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("cu_seqlens")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("seqused")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Input("start_pos")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
this->Output("cmp_kv")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND});
this->Output("state_cache")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND});
this->Attr("rope_head_dim").AttrType(REQUIRED).Int(ROPE_HEAD_DIM_VALUE);
this->Attr("cmp_ratio").AttrType(REQUIRED).Int(CMP_RATIO_VALUE);
this->Attr("coff").AttrType(OPTIONAL).Int(COFF_VALUE);
this->Attr("norm_eps").AttrType(OPTIONAL).Float(1e-6f);
this->Attr("rotary_mode").AttrType(OPTIONAL).Int(ROTARY_MODE_VALUE);
this->Attr("cache_mode").AttrType(OPTIONAL).Int(CACHE_MODE_VALUE);
this->Attr("state_cache_stride_dim0").AttrType(OPTIONAL).Int(STATE_CACHE_STRIDE_DIM0);
OpAICoreConfig aicore_config;
aicore_config.DynamicCompileStaticFlag(true)
.DynamicFormatFlag(true)
.DynamicRankSupportFlag(true)
.DynamicShapeSupportFlag(true)
.NeedCheckSupportFlag(false)
.PrecisionReduceFlag(true)
.ExtendCfgInfo("aclnnSupport.value", "support_aclnn"); // set value of aclnn support
OpAICoreConfig config910;
config910.Input("x")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("wkv")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("wgate")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("state_cache")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.IgnoreContiguous();
config910.Input("ape")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("norm_weight")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("rope_sin")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("rope_cos")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("state_block_table")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("cu_seqlens")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("seqused")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Input("start_pos")
.ParamType(OPTIONAL)
.DataTypeList({ge::DT_INT32})
.FormatList({ge::FORMAT_ND})
.AutoContiguous();
config910.Output("cmp_kv")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.FormatList({ge::FORMAT_ND});
config910.Output("state_cache")
.ParamType(REQUIRED)
.DataTypeList({ge::DT_FLOAT})
.FormatList({ge::FORMAT_ND});
config910.DynamicCompileStaticFlag(true)
.DynamicFormatFlag(true)
.DynamicRankSupportFlag(true)
.DynamicShapeSupportFlag(true)
.NeedCheckSupportFlag(false)
.PrecisionReduceFlag(true)
.ExtendCfgInfo("aclnnSupport.value", "support_aclnn");
this->AICore().AddConfig("ascend910b", config910);
this->AICore().AddConfig("ascend910_93", config910);
this->AICore().AddConfig("ascend950", aicore_config);
}
};
OP_ADD(Compressor, optiling::CompressorCompileInfo);
} // namespace ops

View File

@@ -0,0 +1,174 @@
/**
 * 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.
 */
#include <graph/utils/type_utils.h>
#include <register/op_impl_registry.h>
#include "log/ops_log.h"
using namespace ge;
namespace ops {
// INPUT
constexpr uint32_t TOKEN_X_INPUT_INDEX = 0;
constexpr uint32_t WEIGHT_KV_INPUT_INDEX = 1;
constexpr uint32_t WEIGHT_WGATE_INPUT_INDEX = 2;
constexpr uint32_t STATE_CACHE_INPUT_INDEX = 3;
constexpr uint32_t APE_INPUT_INDEX = 4;
constexpr uint32_t NORM_WEIGHT_INPUT_INDEX = 5;
constexpr uint32_t ROPE_SIN_INPUT_INDEX = 6;
constexpr uint32_t ROPE_COS_INPUT_INDEX = 7;
// INPUT(OPTION)
constexpr uint32_t STATE_BLOCK_TABLE_INPUT_INDEX = 8;
constexpr uint32_t CU_SEQ_LEN_INPUT_INDEX = 9;
constexpr uint32_t SEQ_USED_INPUT_INDEX = 10;
constexpr uint32_t START_POS_INPUT_INDEX = 11;
// ATTR
constexpr uint32_t ROPE_HEAD_DIM_ATTR_INDEX = 0;
constexpr uint32_t CMP_RATIO_ATTR_INDEX = 1;
constexpr uint32_t COFF_ATTR_INDEX = 2;
constexpr uint32_t NORM_EPS_ATTR_INDEX = 3;
constexpr uint32_t ROTARY_MODE_ATTR_INDEX = 4;
constexpr uint32_t CACHE_MODE_ATTR_INDEX = 5;
constexpr uint32_t STATE_CACHE_STRIDE_DIM0_ATTR_INDEX = 6;
// OUTPUT
constexpr uint32_t CMP_KV_OUTPUT_INDEX = 0;
// ATTR DEFAULT VALUE
constexpr uint32_t CMP_RATIO_VALUE = 4;
constexpr uint32_t COFF_VALUE = 1;
struct CompressorProtoShapeParam {
bool isBsMerge { false };
int64_t B { 0 };
int64_t T { 0 };
int64_t S { 0 };
int64_t Sr { 0 };
int64_t H { 0 };
int64_t D { 0 };
};
// tmp
constexpr uint32_t DIM_NUM_1 = 1;
constexpr uint32_t DIM_NUM_2 = 2;
constexpr uint32_t DIM_NUM_3 = 3;
constexpr uint32_t DIM_NUM_4 = 4;
constexpr uint32_t DIM_INDEX_0 = 0;
constexpr uint32_t DIM_INDEX_1 = 1;
constexpr uint32_t DIM_INDEX_2 = 2;
constexpr uint32_t DIM_INDEX_3 = 3;
ge::graphStatus GetCompressorShapeDim(const gert::InferShapeContext* context, CompressorProtoShapeParam &shapeParam)
{
auto xShape = context->GetRequiredInputShape(TOKEN_X_INPUT_INDEX); // (B, S, H) | (T, H)
OPS_LOG_E_IF_NULL(context, xShape, return ge::GRAPH_FAILED)
auto wkvShape = context->GetRequiredInputShape(WEIGHT_KV_INPUT_INDEX); // (coff * D, H)
OPS_LOG_E_IF_NULL(context, wkvShape, return ge::GRAPH_FAILED)
auto wgateShape = context->GetRequiredInputShape(WEIGHT_WGATE_INPUT_INDEX); // (coff * D, H)
OPS_LOG_E_IF_NULL(context, wgateShape, return ge::GRAPH_FAILED)
auto stateCacheShape = context->GetRequiredInputShape(STATE_CACHE_INPUT_INDEX); // (block_num, block_size, 2 * coff * D) | (B, tokrn_size, 2 * coff * D)
OPS_LOG_E_IF_NULL(context, stateCacheShape, return ge::GRAPH_FAILED)
auto apeShape = context->GetRequiredInputShape(APE_INPUT_INDEX); // (r, coff * D)
OPS_LOG_E_IF_NULL(context, apeShape, return ge::GRAPH_FAILED)
auto normWeightShape = context->GetRequiredInputShape(NORM_WEIGHT_INPUT_INDEX); // (D)
OPS_LOG_E_IF_NULL(context, normWeightShape, return ge::GRAPH_FAILED)
auto ropeSinShape = context->GetRequiredInputShape(ROPE_SIN_INPUT_INDEX); // (B, ceil(S / r), rD) | (min(T, T/r + B), rD)
OPS_LOG_E_IF_NULL(context, ropeSinShape, return ge::GRAPH_FAILED)
auto ropeCosShape = context->GetRequiredInputShape(ROPE_COS_INPUT_INDEX); // (B, ceil(S / r), rD) | (min(T, T/r + B), rD)
OPS_LOG_E_IF_NULL(context, ropeCosShape, return ge::GRAPH_FAILED)
auto stateBlockTableShape = context->GetRequiredInputShape(STATE_BLOCK_TABLE_INPUT_INDEX); // (B, sMax/block_size) | (B, )
OPS_LOG_E_IF_NULL(context, stateBlockTableShape, return ge::GRAPH_FAILED)
auto cuSeqlensShape = context->GetRequiredInputShape(CU_SEQ_LEN_INPUT_INDEX); // (B+1,)
OPS_LOG_E_IF_NULL(context, cuSeqlensShape, return ge::GRAPH_FAILED)
auto seqUsedShape = context->GetRequiredInputShape(SEQ_USED_INPUT_INDEX); // (B,)
OPS_LOG_E_IF_NULL(context, seqUsedShape, return ge::GRAPH_FAILED)
auto startPosShape = context->GetRequiredInputShape(START_POS_INPUT_INDEX); // (B,)
OPS_LOG_E_IF_NULL(context, startPosShape, return ge::GRAPH_FAILED)
if (xShape->GetDimNum() == DIM_NUM_3) { // BS
shapeParam.isBsMerge = false;
shapeParam.B = xShape->GetDim(DIM_INDEX_0);
shapeParam.S = xShape->GetDim(DIM_INDEX_1);
shapeParam.H = xShape->GetDim(DIM_INDEX_2);
shapeParam.T = shapeParam.B * shapeParam.S;
} else { // T
shapeParam.isBsMerge = true;
shapeParam.T = xShape->GetDim(DIM_INDEX_0);
shapeParam.H = xShape->GetDim(DIM_INDEX_1);
}
shapeParam.D = normWeightShape->GetDim(DIM_INDEX_0);
shapeParam.Sr = ropeSinShape->GetDim(DIM_INDEX_1);
return GRAPH_SUCCESS;
}
ge::graphStatus SetCompressorShapeDim(const CompressorProtoShapeParam &shapeParam, gert::InferShapeContext* context)
{
auto cmpKvShape = context->GetOutputShape(CMP_KV_OUTPUT_INDEX); // query: (B, S, N, Hckv) | (T, N, Hckv)
OPS_LOG_E_IF_NULL(context, cmpKvShape, return ge::GRAPH_FAILED)
auto attr = context->GetAttrs();
const uint32_t *cmpRatioPtr = attr->GetAttrPointer<uint32_t>(CMP_RATIO_ATTR_INDEX);
uint32_t cmpRatio = (cmpRatioPtr != nullptr) ? *cmpRatioPtr : CMP_RATIO_VALUE;
const uint32_t *coffPtr = attr->GetAttrPointer<uint32_t>(COFF_ATTR_INDEX);
uint32_t coff = (coffPtr != nullptr) ? *coffPtr : COFF_VALUE;
// Set output shape
if (!shapeParam.isBsMerge) {
cmpKvShape->SetDimNum(DIM_NUM_3); // (B, Sr, H)
cmpKvShape->SetDim(DIM_INDEX_0, shapeParam.B);
cmpKvShape->SetDim(DIM_INDEX_1, shapeParam.Sr);
cmpKvShape->SetDim(DIM_INDEX_2, shapeParam.H);
} else {
cmpKvShape->SetDimNum(DIM_NUM_2); // (T, N, Hckv)
cmpKvShape->SetDim(DIM_INDEX_0, shapeParam.Sr);
cmpKvShape->SetDim(DIM_INDEX_1, shapeParam.H);
}
return GRAPH_SUCCESS;
}
ge::graphStatus InferDataTypeCompressor(gert::InferDataTypeContext* context)
{
OP_CHECK_IF(context == nullptr, OPS_REPORT_VECTOR_INNER_ERR("Compressor", "Context is nullptr."),
return ge::GRAPH_FAILED);
OPS_LOG_I(context->GetNodeName(), "Enter Compressor inferDataType impl.");
context->SetOutputDataType(CMP_KV_OUTPUT_INDEX, context->GetRequiredInputDataType(TOKEN_X_INPUT_INDEX));
return GRAPH_SUCCESS;
}
ge::graphStatus InferShapeCompressor(gert::InferShapeContext* context)
{
OP_CHECK_IF(context == nullptr, OPS_REPORT_VECTOR_INNER_ERR("Compressor", "Context is nullptr."),
return ge::GRAPH_FAILED);
OPS_LOG_I(context->GetNodeName(), "Enter Compressor infershape impl.");
CompressorProtoShapeParam shapeParam {};
auto apiRet = GetCompressorShapeDim(context, shapeParam);
OPS_LOG_E_IF((apiRet != GRAPH_SUCCESS), context, return ge::GRAPH_FAILED, "Context get input shape failed");
apiRet = SetCompressorShapeDim(shapeParam, context);
OPS_LOG_E_IF((apiRet != GRAPH_SUCCESS), context, return ge::GRAPH_FAILED, "Context set output shape failed");
return GRAPH_SUCCESS;
}
IMPL_OP_INFERSHAPE(Compressor).InferShape(InferShapeCompressor).InferDataType(InferDataTypeCompressor);
} // namespace ops

View File

@@ -0,0 +1,408 @@
/**
 * 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 compressor_block_cube_perf.h
* \brief
*/
#ifndef COMPRESSOR_BLOCK_CUBE_PERF_H
#define COMPRESSOR_BLOCK_CUBE_PERF_H
#include "compressor_comm.h"
#include "compressor_tools.h"
using namespace AscendC;
namespace Compressor {
template<typename COMP> class CompressorBlockCubePerf {
using MM1_OUT_T = float;
public:
__aicore__ inline CompressorBlockCubePerf(){};
__aicore__ inline void InitParams(const ConstInfo &constInfo, const CompressorTools<COMP> &tools);
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut);
__aicore__ inline void InitBuffers(TPipe *pipe);
__aicore__ inline void InitGlobalBuffers(const GlobalTensor<MM1_OUT_T>& kvMm1ResGm, const GlobalTensor<MM1_OUT_T>& scoreMm1ResGm);
__aicore__ inline void AllocEventID(TPipe *pipe);
__aicore__ inline void FreeEventID(TPipe *pipe);
__aicore__ inline void ComputeMm1(const RunInfo &info);
private:
using T = float;
using X_T = typename AscendC::Conditional<COMP::xDtype == X_DTYPE::BF16, bfloat16_t, half>::type;
__aicore__ inline uint32_t GetMSize(const RunInfo &info, uint32_t coffId);
__aicore__ inline void CopyXGmToL1(const RunInfo &info, LocalTensor<X_T> xL1Tensor, uint32_t hIdx, uint32_t kBase);
__aicore__ inline void CopyWeightGmToL1(LocalTensor<X_T> wL1Tensor,
uint32_t hIdx, uint32_t kBase, uint32_t coffId);
__aicore__ inline void LoadAToL0(const RunInfo &info, LocalTensor<X_T> aL0Tensor, LocalTensor<X_T> xL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t mStart, uint32_t mDealSize);
__aicore__ inline void LoadBToL0(LocalTensor<X_T> bL0Tensor, LocalTensor<X_T> wL1Tensor,
uint32_t kStart, uint32_t kBase);
__aicore__ inline void MatrixMmad(LocalTensor<T> cL0Tensor, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> bL0Tensor, uint32_t mActSize, uint32_t nDealSize, uint32_t kActSize, bool isInitL0C);
__aicore__ inline void CopyOutMm1Res(const RunInfo &info, LocalTensor<T> cL0Tensor,
uint32_t coffId, uint32_t mStart, uint32_t mDealSize);
ConstInfo constInfo_ = {};
CompressorTools<COMP> tools_;
// GM
GlobalTensor<X_T> xGm_;
GlobalTensor<X_T> wkvGm_;
GlobalTensor<X_T> wgateGm_;
GlobalTensor<MM1_OUT_T>kvMm1ResGm;
GlobalTensor<MM1_OUT_T>scoreMm1ResGm;
GlobalTensor<int32_t> cuSeqlensGm_;
GlobalTensor<int32_t> sequsedGm_;
GlobalTensor<int32_t> startPosGm_;
bool isExistSeqUsed = false;
// =================================L1 Buffer=================================
static constexpr uint32_t L1_X_SIZE = 128 * 1024;
static constexpr uint32_t L1_W_SIZE = 64 * 1024;
// L1 Buffer
TBuf<TPosition::A1> xBufL1;
TBuf<TPosition::A1> wBufL1;
// =================================L0 Buffer=================================
// L0 buffer size
static constexpr uint32_t L0A_PP_SIZE = 32 * 1024; // 128 * 128 * 2 = 32k
static constexpr uint32_t L0B_PP_SIZE = 32 * 1024; // 128 * 128 * 2 = 32k
static constexpr uint32_t L0C_PP_SIZE = 64 * 1024; // (128 * 2) * 64 * 4 = 64k
// L0_A
TBuf<TPosition::A2> tmpBufL0A;
// L0_B
TBuf<TPosition::B2> tmpBufL0B;
// L0_C
TBuf<TPosition::CO1> tmpBufL0C;
// =================================Event&Buffer ID===========================
// mte2 <> mte1 EventID
static constexpr uint32_t X_EVENT0 = EVENT_ID0;
static constexpr uint32_t X_EVENT1 = EVENT_ID1;
uint32_t xBufId = 0; // 用于DB计数
static constexpr uint32_t W_EVENT0 = EVENT_ID4;
static constexpr uint32_t W_EVENT1 = EVENT_ID5;
static constexpr uint32_t W_EVENT2 = EVENT_ID6;
static constexpr uint32_t W_EVENT3 = EVENT_ID7;
uint32_t wBufId = 0; // 用于DB计数
// mte1 <> mmad EventID
static constexpr uint32_t L0AB_EVENT0 = EVENT_ID3;
static constexpr uint32_t L0AB_EVENT1 = EVENT_ID4;
uint32_t l0abBufId = 0;
// mmad <> fixpipe EventID
static constexpr uint32_t L0C_EVENT0 = EVENT_ID0; // 每块L0C单独分配EVENT_ID
static constexpr uint32_t L0C_EVENT1 = EVENT_ID1;
uint32_t l0cBufId = 0;
// =================================Loop======================================
uint32_t curBIdx_ = 0;
uint32_t curSIdx_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::InitParams(const ConstInfo &constInfo, const CompressorTools<COMP> &tools)
{
this->constInfo_ = constInfo;
this->tools_ = tools;
}
template <typename COMP> __aicore__ inline void CompressorBlockCubePerf<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut)
{
xGm_.SetGlobalBuffer((__gm__ X_T *)x);
wkvGm_.SetGlobalBuffer((__gm__ X_T *)wKv);
wgateGm_.SetGlobalBuffer((__gm__ X_T *)wGate);
startPosGm_.SetGlobalBuffer((__gm__ int32_t *)startPos);
isExistSeqUsed = (seqUsed != nullptr);
if (isExistSeqUsed) {
sequsedGm_.SetGlobalBuffer((__gm__ int32_t *)seqUsed);
}
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
cuSeqlensGm_.SetGlobalBuffer((__gm__ int32_t *)cuSeqlens);
}
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::InitBuffers(TPipe *pipe)
{
// L1
// 1. coff=1时, mBase=256, kL1=256, X单次拷贝到L1的数据量最大为mBase*kL1*sizeof(BF16/FP16)=256*256*2=128K
// 2. coff=2时, mBase=128, kL1=256, r最大为128, X单次拷贝到L1的最大数据量为(128+r)*kL1*sizeof(BF16/FP16)<=128K
pipe->InitBuffer(xBufL1, L1_X_SIZE * 2);
// dBaseSize<=64, wkv和wgate各一份, kL1=256, 右矩阵为dBaseSize*2*sizeof(BF16/FP16)<=64K
// cur和pre循环使用, 2份buffer就足够
pipe->InitBuffer(wBufL1, L1_W_SIZE * 4);
// L0
pipe->InitBuffer(tmpBufL0A, L0A_PP_SIZE * 2);
pipe->InitBuffer(tmpBufL0B, L0B_PP_SIZE * 2);
pipe->InitBuffer(tmpBufL0C, L0C_PP_SIZE * 2);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::InitGlobalBuffers(const GlobalTensor<MM1_OUT_T>& kvMm1ResGm, const GlobalTensor<MM1_OUT_T>& scoreMm1ResGm)
{
this->kvMm1ResGm = kvMm1ResGm;
this->scoreMm1ResGm = scoreMm1ResGm;
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::AllocEventID(TPipe *pipe)
{
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT0);
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT1);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT0);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT1);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT2);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT3);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT0);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT1);
SetFlag<HardEvent::FIX_M>(L0C_EVENT0);
SetFlag<HardEvent::FIX_M>(L0C_EVENT1);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::FreeEventID(TPipe *pipe)
{
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT0);
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT1);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT0);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT1);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT2);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT3);
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT0);
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT1);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT0);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT1);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::CopyXGmToL1(const RunInfo &info, LocalTensor<X_T> xL1Tensor,
uint32_t hIdx, uint32_t kBase)
{
uint32_t tStart = tools_.GetTIdxByBatch(info.bStart) + info.sStart; // 此基本块在整个序列中的位置
uint32_t copySeqCnt = info.dealSeqCnt; // 此基本块处理的长度
uint32_t xL1Offset = 0 * (32 / sizeof(X_T));
uint64_t sIdx = tStart; // 起始s在整个T的起始点
uint64_t gmOffset = sIdx * constInfo_.hSize + hIdx;
uint32_t nValue = copySeqCnt;
uint32_t dValue = kBase; // 拷贝的列数kBase
uint32_t srcDValue = constInfo_.hSize;
uint32_t dstNzC0Stride = (copySeqCnt + 15) / 16 * 16; // 1行变2行的行方向的偏移需要16对齐
CopySingleMatrixNDToNZ(xL1Tensor[xL1Offset], xGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::CopyWeightGmToL1(LocalTensor<X_T> wL1Tensor,
uint32_t hIdx, uint32_t kBase, uint32_t coffId)
{
// coffId=0, 搬运左矩阵的数据; coffId=1, 搬运右矩阵的数据
uint64_t gmOffset = coffId * constInfo_.headDim * constInfo_.hSize + constInfo_.dIdx * constInfo_.hSize + hIdx;
uint32_t wkvL1Offset = 0;
uint32_t wgateL1Offset = constInfo_.dBaseSize * (32 / sizeof(X_T)); // wgate与wkv的起始点相隔dBaseSize个32B
uint32_t nValue = constInfo_.dBaseSize;
uint32_t dValue = kBase;
uint32_t srcDValue = constInfo_.hSize;
uint32_t dstNzC0Stride = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
CopySingleMatrixNDToNZ(wL1Tensor[wkvL1Offset], wkvGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
CopySingleMatrixNDToNZ(wL1Tensor[wgateL1Offset], wgateGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::LoadAToL0(const RunInfo &info, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> xL1Tensor, uint32_t kStart, uint32_t kBase, uint32_t mStart, uint32_t mDealSize)
{
uint32_t mSize = info.dealSeqCnt;
uint32_t mSizeAlign = Align(mSize, 16U);
uint32_t xTensorOffset = kStart * mSizeAlign + mStart * (32 / sizeof(X_T));
uint32_t mLoop = Align(mDealSize, 16U) / 16;
for (uint32_t i = 0; i < mLoop; i++) {
LoadData2DParams loadData2DParams;
loadData2DParams.startIndex = i;
loadData2DParams.repeatTimes = kBase / (32 / sizeof(X_T));
loadData2DParams.srcStride = mSizeAlign / 16;
loadData2DParams.dstGap = 0;
loadData2DParams.ifTranspose = false;
LoadData(aL0Tensor[i * 16 * kBase], xL1Tensor[xTensorOffset], loadData2DParams); // 16: 一个分型的行数
}
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::LoadBToL0(LocalTensor<X_T> bL0Tensor, LocalTensor<X_T> wL1Tensor,
uint32_t kStart, uint32_t kBase)
{
uint32_t rowCnt = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
uint64_t wTensorOffset = rowCnt * kStart;
LoadData2DParams loadData2DParams;
loadData2DParams.startIndex = 0;
loadData2DParams.repeatTimes = (rowCnt / 16) * (kBase / (32 / sizeof(X_T)));
loadData2DParams.srcStride = 1;
loadData2DParams.dstGap = 0;
loadData2DParams.ifTranspose = false;
LoadData(bL0Tensor, wL1Tensor[wTensorOffset], loadData2DParams);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::MatrixMmad(LocalTensor<T> cL0Tensor, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> bL0Tensor, uint32_t mActSize, uint32_t nDealSize, uint32_t kActSize, bool isInitL0C)
{
MmadParams mmadParams;
mmadParams.m = (mActSize + 15) / 16 * 16;
mmadParams.n = nDealSize;
mmadParams.k = kActSize;
mmadParams.cmatrixInitVal = isInitL0C;
mmadParams.cmatrixSource = false;
Mmad(cL0Tensor, aL0Tensor, bL0Tensor, mmadParams);
PipeBarrier<PIPE_M>();
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::CopyOutMm1Res(const RunInfo &info, LocalTensor<T> cL0Tensor,
uint32_t coffId, uint32_t mStart, uint32_t mDealSize)
{
// coffId=0, 存左矩阵的数据; coffId=1, 存右矩阵的数据
FixpipeParamsV220 fixParams;
fixParams.mSize = mDealSize;
fixParams.nSize = constInfo_.dBaseSize;
fixParams.srcStride = (mDealSize + 15) / 16 * 16; // 需要16对齐
fixParams.dstStride = (uint32_t)COMP::coff * constInfo_.headDim;
fixParams.ndNum = 1;
uint64_t dbOffset = info.cubeDbIdx * constInfo_.dbSize;
uint64_t gmOffset = coffId * constInfo_.headDim + constInfo_.dIdx + mStart * fixParams.dstStride + dbOffset;
uint32_t kvOffset = 0;
uint32_t scoreOffset = (mDealSize + 15) / 16 * 16 * constInfo_.dBaseSize;
Fixpipe(kvMm1ResGm[gmOffset], cL0Tensor[kvOffset], fixParams);
Fixpipe(scoreMm1ResGm[gmOffset], cL0Tensor[scoreOffset], fixParams);
}
template <typename COMP>
__aicore__ inline uint32_t CompressorBlockCubePerf<COMP>::GetMSize(const RunInfo &info, uint32_t coffId)
{
return info.dealSeqCnt;
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubePerf<COMP>::ComputeMm1(const RunInfo &info)
{
static constexpr uint32_t K_SIZE = 512;
static constexpr uint32_t K_L1_BASE = 256;
static constexpr uint32_t M_L0_BASE = 128;
static constexpr uint32_t K_L0_BASE = 128;
uint32_t nCoff = (uint32_t)COMP::coff;
// hSize为K_SIZE=512的倍数
uint32_t hSize = constInfo_.hSize;
uint32_t hIdxStart = (constInfo_.aiCoreIdx % constInfo_.dBasicBlockNum) * K_L1_BASE; // 每组核内的h循环起始不同
for (uint32_t h = 0; h < hSize; h += K_SIZE) {
for (uint32_t k = 0; k < K_SIZE; k += K_L1_BASE) {
bool isFirst = (h == 0 && k == 0);
bool isLast = ((h + K_SIZE >= hSize) && (k + K_L1_BASE >= K_SIZE));
uint32_t hIdx = (h + k + hIdxStart) % hSize; // h方向错位搬运
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT0 + xBufId);
LocalTensor<X_T> xL1Tensor = xBufL1.GetWithOffset<X_T>(L1_X_SIZE / sizeof(X_T), xBufId * L1_X_SIZE);
CopyXGmToL1(info, xL1Tensor, hIdx, K_L1_BASE);
SetFlag<HardEvent::MTE2_MTE1>(X_EVENT0 + xBufId);
WaitFlag<HardEvent::MTE2_MTE1>(X_EVENT0 + xBufId);
for (uint32_t i = nCoff; i > 0; i--) {
// coffId=0, 计算pre数据; coffId=1, 计算cur数据
uint32_t coffId = i - 1;
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT0 + wBufId);
LocalTensor<X_T> wL1Tensor = wBufL1.GetWithOffset<X_T>(L1_W_SIZE / sizeof(X_T), wBufId * L1_W_SIZE);
CopyWeightGmToL1(wL1Tensor, hIdx, K_L1_BASE, coffId);
SetFlag<HardEvent::MTE2_MTE1>(W_EVENT0 + wBufId);
WaitFlag<HardEvent::MTE2_MTE1>(W_EVENT0 + wBufId);
uint32_t mSize = GetMSize(info, coffId);
uint32_t actMDealSize = M_L0_BASE;
for (uint32_t mL0 = 0; mL0 < mSize; mL0 += M_L0_BASE) {
if (mL0 + M_L0_BASE > mSize) {
actMDealSize = mSize - mL0;
}
l0cBufId = coffId + (mL0 / M_L0_BASE);
LocalTensor<T> cL0Tensor = tmpBufL0C.GetWithOffset<T>((L0C_PP_SIZE / sizeof(T)), l0cBufId * L0C_PP_SIZE);
if (isFirst) {
WaitFlag<HardEvent::FIX_M>(L0C_EVENT0 + l0cBufId);
}
uint32_t nDealSize = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
for (uint32_t kL0 = 0; kL0 < K_L1_BASE; kL0 += K_L0_BASE) {
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT0 + l0abBufId);
LocalTensor<X_T> aL0Tensor = tmpBufL0A.GetWithOffset<X_T>(L0A_PP_SIZE / sizeof(X_T), l0abBufId * L0A_PP_SIZE);
LocalTensor<X_T> bL0Tensor = tmpBufL0B.GetWithOffset<X_T>(L0B_PP_SIZE / sizeof(X_T), l0abBufId * L0B_PP_SIZE);
LoadAToL0(info, aL0Tensor, xL1Tensor, kL0, K_L0_BASE, mL0, actMDealSize);
LoadBToL0(bL0Tensor, wL1Tensor, kL0, K_L0_BASE);
SetFlag<HardEvent::MTE1_M>(L0AB_EVENT0 + l0abBufId);
WaitFlag<HardEvent::MTE1_M>(L0AB_EVENT0 + l0abBufId);
bool isInitL0C = isFirst && (kL0 == 0);
MatrixMmad(cL0Tensor, aL0Tensor, bL0Tensor, actMDealSize, nDealSize, K_L0_BASE, isInitL0C);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT0 + l0abBufId);
l0abBufId = (l0abBufId + 1) % 2;
}
if (isLast) {
SetFlag<HardEvent::M_FIX>(L0C_EVENT0 + l0cBufId);
WaitFlag<HardEvent::M_FIX>(L0C_EVENT0 + l0cBufId);
CopyOutMm1Res(info, cL0Tensor, coffId, mL0, actMDealSize);
SetFlag<HardEvent::FIX_M>(L0C_EVENT0 + l0cBufId);
}
}
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT0 + wBufId);
wBufId = (wBufId + 1) % 4;
}
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT0 + xBufId);
xBufId = (xBufId + 1) % 2;
}
}
}
} // namespace Compressor
#endif // COMPRESSOR_BLOCK_CUBE_PERF_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,341 @@
/**
 * 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 compressor_comm.h
* \brief
*/
#ifndef COMPRESSOR_COMM_H
#define COMPRESSOR_COMM_H
#include "kernel_operator.h"
#include "kernel_operator_list_tensor_intf.h"
#include "kernel_tiling/kernel_tiling.h"
#include "lib/matmul_intf.h"
#include "lib/matrix/matmul/tiling.h"
using namespace AscendC;
namespace Compressor {
template <typename T>
__aicore__ inline T CeilDivT(T num1, T num2)
{
if (num2 == 0) {
return static_cast<T>(0);
}
return (num1 + num2 - 1) / num2;
}
template <typename T>
__aicore__ inline T Align(T num, T rnd)
{
return (((rnd) == 0) ? 0 : (((num) + (rnd)-1) / (rnd) * (rnd)));
}
template <typename T>
__aicore__ inline T Trunc(T num, T rnd)
{
return ((rnd) == 0) ? 0 : (((num) / (rnd) * (rnd)));
}
template <typename T>
__aicore__ inline T FloorPow2(T num)
{
if (num == 0) return 1;
for(uint32_t i = 1; i < sizeof(T) * 8; i <<= 1) {
num |= (num >> i);
}
return num - (num >> 1);
}
template <typename T>
__aicore__ inline T CeilPow2(T num)
{
if (num <= 1) return 1;
num --;
for(uint32_t i = 1; i < sizeof(T) * 8; i <<= 1) {
num |= (num >> i);
}
num ++;
return num;
}
enum class X_LAYOUT : std::uint8_t {
BSH = static_cast<std::uint8_t>(0),
TH = static_cast<std::uint8_t>(1)
};
enum class X_DTYPE : std::uint8_t {
BF16 = static_cast<std::uint8_t>(0),
FP16 = static_cast<std::uint8_t>(1)
};
enum class ROPE_DTYPE : std::uint8_t {
SAME_AS_X = static_cast<std::uint8_t>(0),
FP32 = static_cast<std::uint8_t>(1)
};
enum class COFF : std::uint8_t {
DISABLE = static_cast<std::uint8_t>(1),
OVERLAP = static_cast<std::uint8_t>(2)
};
enum class ROTARY_MODE : std::uint8_t {
HALF = static_cast<std::uint8_t>(1),
INTERLEAVE = static_cast<std::uint8_t>(2)
};
enum class CACHE_MODE : std::uint8_t {
CONTINUOUS = static_cast<std::uint8_t>(1),
CYCLE = static_cast<std::uint8_t>(2)
};
enum class TEMPLATE_ID : uint8_t {
NORMAL = 0,
EMPTY_X = 1,
PERF = 2
};
template <X_LAYOUT X_L, X_DTYPE X_T, ROPE_DTYPE R_T, COFF C, ROTARY_MODE Rotary_Mode, typename... Args>
struct COMPType {
static constexpr X_LAYOUT xLayout = X_L;
static constexpr X_DTYPE xDtype = X_T;
static constexpr ROPE_DTYPE ropeDtype = R_T;
static constexpr COFF coff = C;
static constexpr ROTARY_MODE rotaryMode = Rotary_Mode;
};
struct ConstInfo {
// 整个AICORE的任务信息, 左闭右开区间[ (bStart, s2Start), (bEnd, s2End) )
uint32_t bStart = 0U;
uint32_t sStart = 0U;
uint32_t bEnd = 0U;
uint32_t sEnd = 0U;
// 分核相关
uint32_t usedCoreNum = 0;
uint32_t dBaseSize = 0;
uint32_t mBaseSize = 0;
uint32_t tcSize = 0;
uint32_t tcBaseSize = 0;
uint32_t tcBasicBlockNum = 0;
uint32_t dBasicBlockNum = 0;
uint32_t coreGroupNum = 0;
uint32_t singleCoreDealTcBasicNum = 0;
uint32_t dIdx = 0;
uint32_t bIdxOfLastTc = 0;
uint32_t sIdxOfLastTc = 0;
// shape及参数
uint32_t batchSize = 0;
uint32_t hSize = 0;
uint32_t sSize = 0;
uint32_t headDim = 0;
uint32_t ropeHeadDim = 0;
uint32_t cmpRatio = 0;
float normEps = 1e-6;
float reciprocalD = 0;
uint32_t curGroupIdx = 0;
uint32_t tailGroupIdx = 0;
uint32_t tailBasicBlockNum = 0;
uint32_t realDealBasicBlockNum = 0;
// pageAttention
uint32_t blockNum = 0;
uint32_t blockSize = 0;
uint32_t maxBlockNumPerBatch = 0;
uint64_t stateCacheStrideDim0 = 0;
// workSpace
uint32_t dbWorkspaceRatio = 1;
uint32_t mm1KvResSize = 0;
uint32_t mm1ScoreResSize = 0;
uint32_t vec1TailCacheSize = 0;
uint32_t vec1ResSize = 0;
uint32_t mm1ResSize = 0; // 所有cube输出kv/score结果的总大小
uint32_t aiCoreIdx = 0;
uint32_t nSize = 0;
uint32_t dbSize = 0;
};
struct RunInfo {
bool isValid = false;
uint32_t cubeDbIdx = 0; // kernel主循环索引
// 增加字段
uint32_t dealTcNum = 0;
// 右边相关信息
uint32_t bStart = 0;
uint32_t sStart = 0;
uint32_t dealSeqCnt = 0;
// 左边相关信息
uint32_t preBStart = 0;
uint32_t preSStart = 0;
uint32_t preDealSeqCnt = 0; // 左边需要处理的s大小
uint32_t preFirstSeqCnt = 0; // 左边首块大小
uint32_t bEnd = 0;
uint32_t sEnd = 0;
uint32_t bStartSeqIdx = 0;
uint32_t bEndSeqIdx = 0;
// v2分核信息 sc是左闭右开
uint32_t scStart = 0;
uint32_t scEnd = 0;
uint32_t dealScSize = 0;
// vec1Res offset
uint64_t vec1ResOffset = 0;
};
struct Vec1RunInfo {
// vec相关信息一次syncAll需处理数据的起始索引
bool resetResFlag = false; // v1积攒N轮 是否是N轮的起始轮
uint32_t c1v1DbIdx = 0; // vec1 doubleBuffer索引
uint32_t v1v2DbIdx = 0; // v1v2 doubleBuffer索引
uint32_t bStart = 0;
uint32_t sStart = 0;
uint32_t dealTcNum = 0;
uint32_t dealScSize = 0;
};
struct Vec2RunInfo {
// uint32_t bStart = 0;
uint32_t v2DbIdx = 0; // v2 doubleBuffer索引
uint32_t sStart = 0;
uint32_t bEnd = 0;
uint32_t sEnd = 0;
// v2分核信息 sc是左闭右开
uint32_t scStart = 0;
uint32_t scEnd = 0;
// uint32_t dealScSize = 0;
// 增加字段
uint32_t bStart = 0;
uint32_t compressedId = 0;
uint32_t bCompressedId = 0;
uint32_t dealScSize = 0;
};
struct MSplitInfo {
uint32_t vecStartB = 0U;
uint32_t vecStartS = 0U;
uint32_t vecEndB = 0U;
uint32_t vecEndS = 0U;
uint32_t dealTcNum = 0U;
// vec1Res offset
uint64_t vec1StartOffset = 0;
uint64_t vec1ResOffset = 0;
};
struct BlockInfo {
__aicore__ inline BlockInfo(uint32_t bIdx, uint32_t sIdx, uint32_t dealSeqSize) :
bIdx(bIdx), sIdx(sIdx), dealSeqSize(dealSeqSize) {};
uint32_t bIdx = 0U;
uint32_t sIdx = 0U;
uint32_t dealSeqSize = 0;
uint32_t isFirst = true;
uint32_t bSeqUsed = 0U;
uint32_t bStartPos = 0U;
uint32_t headHolderSeqCnt = 0U;
uint32_t validSeqCnt = 0U;
uint32_t tailHolderSeqCnt = 0U;
uint32_t dealTcSize = 0U;
uint32_t tailValidSeqCnt = 0U;
uint32_t compressTcSize = 0U;
};
// BUFFER的字节数
inline constexpr uint32_t BUFFER_SIZE_BYTE_32B = 32;
inline constexpr uint32_t BUFFER_SIZE_BYTE_64B = 64;
inline constexpr uint32_t BUFFER_SIZE_BYTE_256B = 256;
inline constexpr uint32_t BUFFER_SIZE_BYTE_512B = 512;
inline constexpr uint32_t BUFFER_SIZE_BYTE_1K = 1024;
inline constexpr uint32_t BUFFER_SIZE_BYTE_2K = 2048;
inline constexpr uint32_t BUFFER_SIZE_BYTE_4K = 4096;
inline constexpr uint32_t BUFFER_SIZE_BYTE_8K = 8192;
inline constexpr uint32_t BUFFER_SIZE_BYTE_16K = 16384;
inline constexpr uint32_t BUFFER_SIZE_BYTE_32K = 32768;
inline constexpr uint32_t BUFFER_SIZE_BYTE_64K = 65536;
// BLOCK和REPEAT的字节数
inline constexpr uint64_t BYTE_BLOCK = 32UL;
inline constexpr uint32_t REPEAT_BLOCK_BYTE = 256U;
// BLOCK和REPEAT的FP32元素数
inline constexpr uint32_t FP32_BLOCK_ELEMENT_NUM = BYTE_BLOCK / sizeof(float); // 8
inline constexpr uint32_t FP32_REPEAT_ELEMENT_NUM = REPEAT_BLOCK_BYTE / sizeof(float); // 64
inline constexpr uint32_t REPEAT_STRIDE_NUM = REPEAT_BLOCK_BYTE / BYTE_BLOCK; // 8
inline constexpr uint32_t REPEAT_MAX_NUM = 255;
inline constexpr uint32_t BRCB_NUM = 8;
inline constexpr uint32_t MAX_R = 256;
template <typename T>
__aicore__ inline void CopySingleMatrixNDToNZ(LocalTensor<T> l1Tensor, const GlobalTensor<T> gmTensor,
uint32_t nValue, uint32_t dValue, uint32_t srcDValue, uint32_t dstNzC0Stride)
{
Nd2NzParams nd2nzPara;
nd2nzPara.ndNum = 1;
nd2nzPara.nValue = nValue; // nd矩阵的行数
if constexpr (IsSameType<T, int4b_t>::value) {
constexpr uint32_t HALF_SIZE_DIVISOR = 2;
nd2nzPara.dValue = dValue / HALF_SIZE_DIVISOR;
nd2nzPara.srcDValue = srcDValue / HALF_SIZE_DIVISOR;
} else {
nd2nzPara.dValue = dValue; // nd矩阵的列数
nd2nzPara.srcDValue = srcDValue; // 同一nd矩阵相邻行起始地址间的偏移
}
nd2nzPara.dstNzC0Stride = dstNzC0Stride;
nd2nzPara.dstNzNStride = 1;
nd2nzPara.srcNdMatrixStride = 0;
nd2nzPara.dstNzMatrixStride = 0;
DataCopy(l1Tensor, gmTensor, nd2nzPara);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(GlobalTensor<T> tensor, uint32_t desc, uint32_t dumpSize, uint32_t row, uint32_t col)
{
uint32_t array2[] = {static_cast<uint32_t>(row), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(LocalTensor<T> tensor, uint32_t desc, uint32_t dumpSize, uint32_t row, uint32_t col)
{
uint32_t array2[] = {static_cast<uint32_t>(row), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(LocalTensor<T> tensor, uint32_t desc, uint32_t dumpSize)
{
uint32_t col = 32 / sizeof(T);
uint32_t array2[] = {static_cast<uint32_t>(dumpSize / col), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(GlobalTensor<T> tensor, uint32_t desc, uint32_t dumpSize)
{
uint32_t col = 32 / sizeof(T);
uint32_t array2[] = {static_cast<uint32_t>(dumpSize / col), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
} // namespace Compressor
#endif

View File

@@ -0,0 +1,86 @@
/**
 * 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 compressor_kernel.h
* \brief
*/
#ifndef COMPRESSOR_KERNEL
#define COMPRESSOR_KERNEL
#include "compressor_comm.h"
#include "compressor_template_tiling_key.h"
#include "compressor_kernel_perf.h"
#include "compressor_tiling_data.h"
#include "compressor_tools.h"
using namespace AscendC;
namespace Compressor {
template <typename COMP>
class CompressorKernel {
public:
__aicore__ inline CompressorKernel(TPipe* pipe, const optiling::CompressorTilingData* __restrict tilingData)
: pipe_(pipe), tilingData_(tilingData) {}
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace);
__aicore__ inline void Process();
// ==============================TilingData&TPipe==============================
TPipe* pipe_;
const optiling::CompressorTilingData* __restrict tilingData_;
};
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace)
{
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::Process()
{
}
} // namespace Compressor
#endif // COMPRESSOR_KERNEL

View File

@@ -0,0 +1,695 @@
/**
 * 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 compressor_kernel_perf.h
* \brief
*/
#ifndef COMPRESSOR_KERNEL_PERF_H
#define COMPRESSOR_KERNEL_PERF_H
#include "compressor_comm.h"
#include "compressor_template_tiling_key.h"
#include "compressor_tiling_data.h"
#include "compressor_tools.h"
#include "compressor_block_cube_perf.h"
#include "compressor_block_vec_perf.h"
using namespace AscendC;
namespace Compressor {
struct CmpBlockInfo {
__aicore__ inline CmpBlockInfo() {};
__aicore__ inline CmpBlockInfo(uint32_t bIdx, uint32_t sIdx, bool needReset = false) : bIdx(bIdx), sIdx(sIdx), needReset(needReset) {};
uint32_t bIdx = 0U;
uint32_t sIdx = 0U;
uint32_t bSeqUsed = 0U;
uint32_t bStartPos = 0U;
bool needReset = false;
bool isFirst = true;
uint32_t headSeqCnt = 0U;
uint32_t validSeqCnt = 0U;
uint32_t tailSeqCnt = 0U;
bool isCompress = 0U;
};
struct BasicBlockInfo {
uint32_t bIdx = 0;
uint32_t sIdx = 0;
uint32_t compressedTcNum = 0;
uint32_t dealSeqCnt = 0;
uint32_t dealTcNum = 0;
};
struct BatchInfo {
uint32_t tcNum = 0;
uint32_t compressedTcNum = 0;
uint32_t remSeqCnt = 0;
uint32_t seqCnt = 0;
uint32_t seqUsedCnt = 0;
uint32_t headHolderSeq = 0;
uint32_t bStartPos = 0;
uint32_t bIdx = 0;
uint32_t sIdx = 0;
};
template <typename COMP>
class CompressorKernelPerf {
public:
__aicore__ inline CompressorKernelPerf(TPipe* pipe, const optiling::CompressorTilingData* __restrict tilingData)
: pipe_(pipe), tilingData_(tilingData) {}
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace);
__aicore__ inline void Process();
private:
// ================================Init functions==================================
__aicore__ inline void InitWorkspace(__gm__ uint8_t *workspace);
// ================================Process functions================================
__aicore__ inline void InitTilingData();
__aicore__ inline void SetBaseSize();
// 获取基本块数量
__aicore__ inline uint32_t GetLoopTimes();
__aicore__ inline void SkipInvalidBatch(BatchInfo &batchInfo);
__aicore__ inline void UpdateCurGroup(BasicBlockInfo &basicBlockInfo, BatchInfo batchInfo, uint32_t &curGroupQuota, uint32_t curDealSeq);
__aicore__ inline BasicBlockInfo SkipOneLoop(BatchInfo &batchInfo);
// 计算分核基本信息
__aicore__ inline void CalcSplitCoreInfo();
__aicore__ inline void AllocEventID();
__aicore__ inline void FreeEventID();
__aicore__ inline void ComputeMm1(const RunInfo &info, bool isNeedExcute);
__aicore__ inline void ComputeVec1(const Vec1RunInfo &info);
__aicore__ inline void ComputeVec2(const Vec2RunInfo &info);
__aicore__ inline bool IsNeedExcuteC1(RunInfo info);
__aicore__ inline bool IsNeedSyncAll(uint32_t curBasicBlockIdx);
__aicore__ inline void CalcC1V1Params(RunInfo &info, Vec1RunInfo &vec1Info, BatchInfo &batchInfo, uint32_t loopIdx);
__aicore__ inline void UpdateVec2Info(Vec2RunInfo &vec2Info, uint32_t curBasicBlockIdx, const Vec1RunInfo &info);
__aicore__ inline bool IsNeedExcuteV2(Vec2RunInfo &vec2Info);
using X_T = typename AscendC::Conditional<COMP::xDtype == X_DTYPE::BF16, bfloat16_t, half>::type;
using T = float;
using MM1_OUT_T = T;
using VEC1_OUT_T = T;
// 常量
static constexpr uint64_t SYNC_MODE0 = 0;
static constexpr uint64_t SYNC_MODE2 = 2;
static constexpr uint32_t SYNC_C1_FLAG = 3;
static constexpr uint32_t SYNC_V1_FLAG = 4;
static constexpr uint32_t SYNC_V1_FLAG2 = 5;
static constexpr uint32_t SYNC_C1_V1_FLAG = 6;
static constexpr uint32_t SYNC_V1_C1_FLAG = 8;
// ==============================TilingData&TPipe==============================
TPipe* pipe_;
const optiling::CompressorTilingData* __restrict tilingData_;
// ===========================Workspace Global Tensor===========================
GlobalTensor<MM1_OUT_T> mm1KvResGm;
GlobalTensor<MM1_OUT_T> mm1ScoreResGm;
GlobalTensor<MM1_OUT_T> vec1KvCacheGm;
GlobalTensor<MM1_OUT_T> vec1ScoreCacheGm;
GlobalTensor<MM1_OUT_T> Vec1InputKvGm;
GlobalTensor<MM1_OUT_T> Vec1InputScoreGm;
GlobalTensor<VEC1_OUT_T> vec1ResGm;
GlobalTensor<VEC1_OUT_T> vec2InputGm;
// ================================Task Info====================================
CompressorTools<COMP> tools_;
ConstInfo constInfo{};
uint32_t aiCoreIdx = 0;
// ==============================Service Define==============================
CompressorBlockCubePerf<COMP> blockCube_;
CompressorBlockVectorPerf<COMP> blockVec_;
uint32_t allCompressedTcNum_ = 0;
uint32_t curCompressedTcNum_ = 0;
uint32_t accDealSize = 0;
uint32_t loopTimes = 0;
uint32_t cubeLoop = 0;
uint32_t vec1Loop = 0;
uint32_t vec2Loop = 0;
bool isFirstUpdateCurGroup = true;
};
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace)
{
if ASCEND_IS_AIV {
constInfo.aiCoreIdx = GetBlockIdx() / 2;
} else {
constInfo.aiCoreIdx = GetBlockIdx();
}
InitTilingData();
// init tools
tools_.toolParams_.seqSize = tilingData_->baseParams.seqSize;
tools_.toolParams_.cmpRatio = tilingData_->baseParams.cmpRatio;
tools_.Init(startPos, seqUsed, cuSeqlens);
// 剔除尾部的无效batch
for (; constInfo.batchSize > 0; --constInfo.batchSize) {
uint32_t bSeqUsed = tools_.GetSeqLength(constInfo.batchSize - 1);
if (bSeqUsed > 0) {
break;
}
}
// 所有batch的有效序列都为0时, 直接退出
if (constInfo.batchSize == 0) {
return;
}
// 0. 计算最后一个Tc块的起始位置
constInfo.bIdxOfLastTc = constInfo.batchSize - 1;
// 1. 计算head_dim的切分大小, 构建ConstInfo的其他信息
SetBaseSize(); // 设置基本块大小
CalcSplitCoreInfo();
// 2. 计算循环次数
loopTimes = GetLoopTimes();
// 3. 初始化workspace
InitWorkspace(workspace);
// 4. 初始化block层
if ASCEND_IS_AIC {
#if __CCE_AICORE__ == 310
blockCube_.InitParams(constInfo, tools_);
#else
blockCube_.InitParams(constInfo, tools_);
#endif
blockCube_.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos,
stateBlockTable, cuSeqlens, seqUsed, startPos, cmpKvOut);
blockCube_.InitBuffers(pipe_);
#if __CCE_AICORE__ == 310
blockCube_.InitGlobalBuffers(mm1KvResGm, mm1ScoreResGm);
#else
blockCube_.InitGlobalBuffers(mm1KvResGm, mm1ScoreResGm);
#endif
} else {
blockVec_.InitParams(constInfo, tools_);
blockVec_.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos, stateBlockTable,
cuSeqlens, seqUsed, startPos, cmpKvOut);
blockVec_.InitBuffers(pipe_);
#if __CCE_AICORE__ == 310
blockVec_.InitVec1GlobalTensor(Vec1InputKvGm, Vec1InputScoreGm, vec1KvCacheGm, vec1ScoreCacheGm, vec1ResGm, vec2InputGm);
#else
blockVec_.InitVec1GlobalTensor(Vec1InputKvGm, Vec1InputScoreGm, vec1KvCacheGm, vec1ScoreCacheGm, vec1ResGm, vec2InputGm);
#endif
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::InitTilingData() {
constInfo.cmpRatio = tilingData_->baseParams.cmpRatio;
constInfo.batchSize = tilingData_->baseParams.batchSize;
constInfo.mBaseSize = tilingData_->innerSplitParams.mBaseSize;
constInfo.headDim = tilingData_->baseParams.headDim;
constInfo.hSize = tilingData_->baseParams.hiddenSize;
constInfo.sSize = tilingData_->baseParams.seqSize;
constInfo.ropeHeadDim = tilingData_->baseParams.ropeHeadDim;
constInfo.normEps = tilingData_->baseParams.normEps;
constInfo.reciprocalD = tilingData_->baseParams.reciprocalD;
constInfo.usedCoreNum = tilingData_->baseParams.usedCoreNum;
constInfo.blockNum = tilingData_->pageAttentionParams.blockNum;
constInfo.blockSize = tilingData_->pageAttentionParams.blockSize;
constInfo.maxBlockNumPerBatch = tilingData_->pageAttentionParams.maxBlockNumPerBatch;
constInfo.stateCacheStrideDim0 = tilingData_->baseParams.stateCacheStrideDim0;
constInfo.nSize = tilingData_->baseParams.nSize;
constInfo.vec1TailCacheSize = tilingData_->workspaceParams.vec1TailCacheSize;
constInfo.dbWorkspaceRatio = tilingData_->workspaceParams.dbWorkspaceRatio;
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::SetBaseSize()
{
uint32_t mSize = 0;
uint32_t minMBaseSize = 0;
bool sameSeqUsed = true;
uint32_t firstBatchSeqUsed = tools_.GetSeqLength(0);
for (uint32_t i = 0; i < constInfo.batchSize; i++) {
uint32_t bSeqUsed = tools_.GetSeqLength(i);
uint32_t bStartPos = tools_.GetStartPos(i);
// 获取m大小
mSize += bSeqUsed;
// 获取是否等长
if (sameSeqUsed && (bSeqUsed != firstBatchSeqUsed)) {
sameSeqUsed = false;
}
// 获取m轴最小切分大小
if (minMBaseSize != constInfo.cmpRatio) {
uint32_t startCmpIdx = bStartPos / constInfo.cmpRatio;
uint32_t endCmpIdx = (bStartPos + bSeqUsed) / constInfo.cmpRatio;
if (startCmpIdx == endCmpIdx) {
if (bSeqUsed > minMBaseSize) {
minMBaseSize = bSeqUsed;
}
} else if (startCmpIdx + 1 == endCmpIdx) {
uint32_t startCmpValidSeqCnt = constInfo.cmpRatio - (bStartPos % constInfo.cmpRatio);
uint32_t endCmpValidSeqCnt = (bStartPos + bSeqUsed) % constInfo.cmpRatio;
if (startCmpValidSeqCnt > minMBaseSize) {
minMBaseSize = startCmpValidSeqCnt;
}
if (endCmpValidSeqCnt > minMBaseSize) {
minMBaseSize = endCmpValidSeqCnt;
}
} else {
minMBaseSize = constInfo.cmpRatio;
}
}
}
uint32_t aiCoreNum = constInfo.usedCoreNum;
constInfo.dBaseSize = 64;
uint32_t dBaseBlockNum = constInfo.headDim / constInfo.dBaseSize;
if (sameSeqUsed && mSize <= (constInfo.mBaseSize * (aiCoreNum / dBaseBlockNum))) {
if constexpr (COMP::coff == COFF::OVERLAP) {
if (constInfo.headDim == 128) {
dBaseBlockNum = 8;
} else if (constInfo.headDim == 512) {
dBaseBlockNum = 16;
}
} else {
if (constInfo.headDim == 128) {
dBaseBlockNum = 8;
} else if (constInfo.headDim == 512) {
dBaseBlockNum = 16;
}
}
// 核数足够时, 修改才生效
if (aiCoreNum >= dBaseBlockNum) {
constInfo.dBaseSize = constInfo.headDim / dBaseBlockNum;
// 开启全核
uint32_t coreGroupNum = aiCoreNum / dBaseBlockNum;
uint32_t newMBaseSize = (constInfo.batchSize + coreGroupNum - 1) / coreGroupNum * firstBatchSeqUsed;
if (newMBaseSize > minMBaseSize && newMBaseSize < constInfo.mBaseSize) {
constInfo.mBaseSize = newMBaseSize;
}
}
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::SkipInvalidBatch(BatchInfo &batchInfo)
{
for (; batchInfo.bIdx < constInfo.batchSize; ++batchInfo.bIdx) {
batchInfo.seqCnt = tools_.GetSeqLength(batchInfo.bIdx);
if (batchInfo.seqCnt > 0) {
break;
}
}
batchInfo.remSeqCnt = batchInfo.seqCnt;
if (tools_.isExistSeqUsed_) {
batchInfo.seqUsedCnt = tools_.GetSeqUsed(batchInfo.bIdx);
} else {
batchInfo.seqUsedCnt = batchInfo.seqCnt;
}
if (batchInfo.bIdx < constInfo.batchSize) {
batchInfo.bStartPos = tools_.GetStartPos(batchInfo.bIdx);
batchInfo.sIdx = 0;
batchInfo.headHolderSeq = batchInfo.bStartPos & (constInfo.cmpRatio - 1);
batchInfo.tcNum = (batchInfo.bStartPos + batchInfo.seqCnt + constInfo.cmpRatio - 1) / constInfo.cmpRatio - batchInfo.bStartPos / constInfo.cmpRatio;
batchInfo.compressedTcNum = (batchInfo.bStartPos + batchInfo.seqUsedCnt) / constInfo.cmpRatio - batchInfo.bStartPos / constInfo.cmpRatio;
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::UpdateCurGroup(BasicBlockInfo &basicBlockInfo,
BatchInfo batchInfo, uint32_t &curGroupQuota, uint32_t curDealSeq)
{
// 更新当前组的信息
if (curGroupQuota == 0 && !isFirstUpdateCurGroup) {
return;
}
isFirstUpdateCurGroup = false;
basicBlockInfo.bIdx = batchInfo.bIdx;
uint32_t curGroupDealSeq = curGroupQuota < curDealSeq ? curGroupQuota : curDealSeq;
basicBlockInfo.sIdx = batchInfo.sIdx + curGroupDealSeq;
basicBlockInfo.dealSeqCnt += curGroupDealSeq;
curGroupQuota -= curGroupDealSeq;
// 结尾需要跳batch需要考虑在当前组起始为末尾或者当前组起始大于整个M轴
if ((curGroupQuota == 0 || basicBlockInfo.bIdx == constInfo.batchSize - 1) && basicBlockInfo.sIdx == batchInfo.seqCnt) {
basicBlockInfo.sIdx = 0;
for (basicBlockInfo.bIdx++; basicBlockInfo.bIdx < constInfo.batchSize; ++basicBlockInfo.bIdx) {
uint32_t seqCnt = tools_.GetSeqLength(basicBlockInfo.bIdx);
if (seqCnt > 0) {
break;
}
}
}
}
template <typename COMP>
__aicore__ inline BasicBlockInfo CompressorKernelPerf<COMP>::SkipOneLoop(BatchInfo &batchInfo)
{
BasicBlockInfo basicBlockInfo{};
isFirstUpdateCurGroup = true;
uint32_t curGroupQuota = constInfo.mBaseSize * constInfo.curGroupIdx; // m轴当前组起始
bool curGroupStartFlag = false;
uint32_t quota = constInfo.coreGroupNum * constInfo.mBaseSize;
for (; batchInfo.bIdx < constInfo.batchSize;) {
uint32_t curDealSeq = 0;
uint32_t curDealTcNum = 0;
uint32_t curDealCompressedTcNum = 0;
// 无法处理完当前整个batch
if (quota < batchInfo.remSeqCnt) {
// 向下对齐r
if (quota > constInfo.cmpRatio - batchInfo.headHolderSeq) {
uint32_t delta = (batchInfo.bStartPos + batchInfo.sIdx + quota) & (constInfo.cmpRatio - 1); // 超出对齐的部分
curDealSeq = quota - delta;
quota -= curDealSeq;
curDealTcNum = (curDealSeq + constInfo.cmpRatio - 1) / constInfo.cmpRatio;
curDealCompressedTcNum = min(curDealTcNum, batchInfo.compressedTcNum);
// 更新当前组所需信息
UpdateCurGroup(basicBlockInfo, batchInfo, curGroupQuota, curDealSeq);
// 更新batch信息
batchInfo.remSeqCnt = batchInfo.remSeqCnt - curDealSeq;
batchInfo.sIdx = batchInfo.sIdx + curDealSeq;
batchInfo.compressedTcNum -= curDealCompressedTcNum;
batchInfo.tcNum -= curDealTcNum;
// 更新loop信息
basicBlockInfo.dealTcNum += curDealTcNum;
basicBlockInfo.compressedTcNum += curDealCompressedTcNum;
}
break;
} else {
// 处理整个batch
quota -= batchInfo.remSeqCnt;
curDealSeq = batchInfo.remSeqCnt;
curDealTcNum = batchInfo.tcNum;
// 更新当前组所需信息
UpdateCurGroup(basicBlockInfo, batchInfo, curGroupQuota, curDealSeq);
// 更新batch和loop信息
batchInfo.remSeqCnt = 0;
basicBlockInfo.dealTcNum += batchInfo.tcNum;
basicBlockInfo.compressedTcNum += batchInfo.compressedTcNum;
batchInfo.bIdx++;
SkipInvalidBatch(batchInfo);
}
}
uint32_t totalDataSize = constInfo.coreGroupNum * constInfo.mBaseSize - quota;
// 2. 当前组的起始偏移
uint32_t currentGroupStart = constInfo.curGroupIdx * constInfo.mBaseSize;
// 3. 安全判断
if (currentGroupStart >= totalDataSize) {
// 超出尾块
basicBlockInfo.dealSeqCnt = 0;
} else {
// 还在有效范围内,计算剩余量
uint32_t remaining = totalDataSize - currentGroupStart;
basicBlockInfo.dealSeqCnt = (remaining < constInfo.mBaseSize) ? remaining : constInfo.mBaseSize;
}
return basicBlockInfo;
}
template <typename COMP>
__aicore__ inline uint32_t CompressorKernelPerf<COMP>::GetLoopTimes()
{
// 计算主循环次数
uint32_t loopTimes = 0;
BatchInfo batchInfo{};
SkipInvalidBatch(batchInfo);
for (;batchInfo.bIdx < constInfo.batchSize; ++loopTimes) {
SkipOneLoop(batchInfo);
}
return loopTimes;
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::CalcSplitCoreInfo()
{
// D方向的基本块数量
constInfo.dBasicBlockNum = constInfo.headDim / constInfo.dBaseSize;
// 核的组数
constInfo.coreGroupNum = constInfo.usedCoreNum / constInfo.dBasicBlockNum;
// 每个核处理的d方向的索引
constInfo.dIdx = (constInfo.aiCoreIdx % constInfo.dBasicBlockNum) * constInfo.dBaseSize;
// 当前组id
constInfo.curGroupIdx = constInfo.aiCoreIdx / constInfo.dBasicBlockNum;
constInfo.mm1ResSize = constInfo.mBaseSize * constInfo.headDim * constInfo.coreGroupNum;
uint32_t coff = (uint32_t)COMP::coff;
constInfo.mm1KvResSize = constInfo.mBaseSize * constInfo.headDim * coff;
constInfo.mm1ScoreResSize = constInfo.mBaseSize * constInfo.headDim * coff;
constInfo.vec1ResSize = constInfo.mBaseSize * constInfo.headDim * constInfo.nSize;
constInfo.dbSize = constInfo.coreGroupNum * constInfo.mm1KvResSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::InitWorkspace(__gm__ uint8_t *workspace) {
uint64_t offset = 0;
uint64_t mm1KvResStartOffset = offset;
// mm1KvResGm
mm1KvResGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + offset +
constInfo.curGroupIdx * constInfo.mm1KvResSize * sizeof(MM1_OUT_T)));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.mm1KvResSize * sizeof(MM1_OUT_T);
uint64_t mm1ScoreResStartOffset = offset;
// mm1ScoreResGm
mm1ScoreResGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + offset +
constInfo.curGroupIdx * constInfo.mm1ScoreResSize * sizeof(MM1_OUT_T)));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.mm1ScoreResSize * sizeof(MM1_OUT_T);
Vec1InputKvGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + mm1KvResStartOffset));
Vec1InputScoreGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + mm1ScoreResStartOffset));
vec1KvCacheGm.SetGlobalBuffer((__gm__ MM1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.vec1TailCacheSize * sizeof(MM1_OUT_T);
vec1ScoreCacheGm.SetGlobalBuffer((__gm__ MM1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.vec1TailCacheSize * sizeof(MM1_OUT_T);
uint64_t beforeVecOffset = offset;
// vec1Res
vec1ResGm.SetGlobalBuffer(
(__gm__ VEC1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.vec1ResSize * sizeof(VEC1_OUT_T);
// vec2Input
vec2InputGm.SetGlobalBuffer(
(__gm__ VEC1_OUT_T *)(workspace + beforeVecOffset));
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::ComputeMm1(const RunInfo &info, bool isNeedExcute) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_FIX>(SYNC_V1_C1_FLAG + info.cubeDbIdx);
if (isNeedExcute) {
blockCube_.ComputeMm1(info);
}
CrossCoreSetFlag<SYNC_MODE0, PIPE_FIX>(SYNC_C1_FLAG);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_FIX>(SYNC_C1_FLAG);
CrossCoreSetFlag<SYNC_MODE2, PIPE_FIX>(SYNC_C1_V1_FLAG + info.cubeDbIdx);
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::ComputeVec1(const Vec1RunInfo &info) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_C1_V1_FLAG + info.c1v1DbIdx);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG2 + info.c1v1DbIdx);
blockVec_.ComputeVec1(info);
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG);
CrossCoreSetFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_V1_C1_FLAG + info.c1v1DbIdx);
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE3>(SYNC_V1_FLAG2 + (info.c1v1DbIdx + 1) % constInfo.dbWorkspaceRatio);
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::ComputeVec2(const Vec2RunInfo &info) {
blockVec_.ComputeVec2(info);
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::AllocEventID()
{
if ASCEND_IS_AIC {
blockCube_.AllocEventID(pipe_);
} else {
blockVec_.AllocEventID();
for (int i = 0; i < constInfo.dbWorkspaceRatio; ++i) {
CrossCoreSetFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_V1_C1_FLAG + i);
}
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE3>(SYNC_V1_FLAG2);
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::FreeEventID()
{
if ASCEND_IS_AIC {
for (int i = 0; i < constInfo.dbWorkspaceRatio; ++i) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_FIX>(SYNC_V1_C1_FLAG + i);
}
blockCube_.FreeEventID(pipe_);
} else {
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG2 + loopTimes % constInfo.dbWorkspaceRatio);
blockVec_.FreeEventID();
}
}
template <typename COMP>
__aicore__ inline bool CompressorKernelPerf<COMP>::IsNeedExcuteC1(RunInfo info)
{
// B超出范围则cube不执行
return info.bStart < constInfo.batchSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::CalcC1V1Params(RunInfo &info, Vec1RunInfo &vec1Info, BatchInfo &batchInfo, uint32_t loopIdx)
{
vec1Info.bStart = batchInfo.bIdx;
vec1Info.sStart = batchInfo.sIdx;
vec1Info.resetResFlag = (loopIdx & (constInfo.nSize - 1)) == 0;
vec1Info.c1v1DbIdx = (vec1Loop++ & (constInfo.dbWorkspaceRatio - 1));
vec1Info.v1v2DbIdx = (vec2Loop & (constInfo.dbWorkspaceRatio - 1));
BasicBlockInfo basicBlockInfo = SkipOneLoop(batchInfo);
info.cubeDbIdx = (cubeLoop++ & (constInfo.dbWorkspaceRatio - 1));
info.dealSeqCnt = basicBlockInfo.dealSeqCnt;
info.dealTcNum = basicBlockInfo.dealTcNum;
info.bStart = basicBlockInfo.bIdx;
info.sStart = basicBlockInfo.sIdx;
vec1Info.dealTcNum = basicBlockInfo.dealTcNum;
vec1Info.dealScSize = basicBlockInfo.compressedTcNum;
allCompressedTcNum_ += basicBlockInfo.compressedTcNum;
}
template <typename COMP>
__aicore__ inline bool CompressorKernelPerf<COMP>::IsNeedExcuteV2(Vec2RunInfo &vec2Info)
{
return (vec2Info.dealScSize > 0);
}
template <typename COMP>
__aicore__ inline bool CompressorKernelPerf<COMP>::IsNeedSyncAll(uint32_t curBasicBlockIdx)
{
if (allCompressedTcNum_ == 0) {
return false;
}
uint32_t cnt = curBasicBlockIdx + 1;
if ((cnt == loopTimes) || (cnt % constInfo.nSize == 0)) {
return true;
}
return false;
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::UpdateVec2Info(
Vec2RunInfo &vec2Info, uint32_t curBasicBlockIdx, const Vec1RunInfo &info)
{
// nSize轮起始先重置v2Info信息
if (curBasicBlockIdx % constInfo.nSize == 0) {
vec2Info.v2DbIdx = (vec2Loop & (constInfo.dbWorkspaceRatio - 1));
vec2Info.bStart = info.bStart;
vec2Info.sStart = info.sStart;
// 将sStart转成bCompressedId
uint32_t startPos = tools_.GetStartPos(info.bStart);
if (tools_.isExistSeqUsed_) {
uint32_t seqUsed = tools_.GetSeqUsed(info.bStart);
if (vec2Info.sStart >= seqUsed) {
vec2Info.bStart++;
vec2Info.sStart = 0;
}
}
vec2Info.bCompressedId = (startPos + vec2Info.sStart) / constInfo.cmpRatio - startPos / constInfo.cmpRatio;
vec2Info.dealScSize = 0;
} else if ((curBasicBlockIdx + 1) % constInfo.nSize == 0) {
vec2Loop++;
}
vec2Info.dealScSize += info.dealScSize;
vec2Info.compressedId += info.dealScSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelPerf<COMP>::Process()
{
// 所有batch的有效序列都为0时, 直接退出
if (constInfo.batchSize == 0) {
return;
}
AllocEventID();
BatchInfo batchInfo{};
RunInfo extraInfo[1];
Vec1RunInfo vec1Info{};
Vec2RunInfo vec2Info{};
SkipInvalidBatch(batchInfo);
for (uint32_t i = 0; i < loopTimes; ++i) {
RunInfo &extraInfo0 = extraInfo[0];
CalcC1V1Params(extraInfo0, vec1Info, batchInfo, i);
bool isNeedExcuteC1 = IsNeedExcuteC1(extraInfo0);
if ASCEND_IS_AIC {
ComputeMm1(extraInfo0, isNeedExcuteC1);
} else {
ComputeVec1(vec1Info);
UpdateVec2Info(vec2Info, i, vec1Info);
if (IsNeedSyncAll(i)) {
SyncAll();
if (IsNeedExcuteV2(vec2Info)) {
ComputeVec2(vec2Info);
}
}
}
}
FreeEventID();
}
} // namespace Compressor
#endif // COMPRESSOR_KERNEL_PERF_H

View File

@@ -0,0 +1,56 @@
/**
 * 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 COMPRESSOR_template_tiling_key.h
* \brief
*/
#ifndef COMPRESSOR_TEMPLATE_TILING_KEY_H
#define COMPRESSOR_TEMPLATE_TILING_KEY_H
#include "ascendc/host_api/tiling/template_argument.h"
#define ASCENDC_TPL_1_BW 1 // 每个参数占用1个bit位
#define ASCENDC_TPL_2_BW 2 // 每个参数占用2个bit位
#define ASCENDC_TPL_4_BW 4 // 每个参数占用4个bit位
// 可表示的tilingkey范围为64bit注意不可超过限制
ASCENDC_TPL_ARGS_DECL(compressor, // 算子唯一标识与opType保持一致
// 可能需要切分之后的headdim
// bit:0 LAYOUT 0:BSH 1:TH
ASCENDC_TPL_UINT_DECL(X_LAYOUT, ASCENDC_TPL_1_BW, ASCENDC_TPL_UI_LIST, 0, 1),
// bit:1-4 x的dtype 0:BF16 1:FP16
ASCENDC_TPL_UINT_DECL(X_DTYPE, ASCENDC_TPL_4_BW, ASCENDC_TPL_UI_LIST, 0, 1),
// bit:5-6 coff 1:无需overlap 2:需要overlap
ASCENDC_TPL_UINT_DECL(COFF, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 1, 2),
// bit:7-8 rotary_mode 1:half 2:interleave
ASCENDC_TPL_UINT_DECL(ROTARY_MODE, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 1, 2),
// bit:9-10 cache_mode 1:CONTINUOUS 2:cycle
ASCENDC_TPL_UINT_DECL(CACHE_MODE, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 1, 2),
// bit:11-12 template_id 0:empty_tensor 1:normal 2:full load
ASCENDC_TPL_UINT_DECL(TEMPLATE_ID, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 0, 1, 2),
// bit:13 rope dtype 0:same as x 1:fp32
ASCENDC_TPL_UINT_DECL(ROPE_DTYPE, ASCENDC_TPL_1_BW, ASCENDC_TPL_UI_LIST, 0, 1),
);
ASCENDC_TPL_SEL(
ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_UINT_SEL(X_LAYOUT, ASCENDC_TPL_UI_LIST, 0, 1),
ASCENDC_TPL_UINT_SEL(X_DTYPE, ASCENDC_TPL_UI_LIST, 0, 1),
ASCENDC_TPL_UINT_SEL(COFF, ASCENDC_TPL_UI_LIST, 1, 2),
ASCENDC_TPL_UINT_SEL(ROTARY_MODE, ASCENDC_TPL_UI_LIST, 1, 2),
ASCENDC_TPL_UINT_SEL(CACHE_MODE, ASCENDC_TPL_UI_LIST, 1, 2),
ASCENDC_TPL_UINT_SEL(TEMPLATE_ID, ASCENDC_TPL_UI_LIST, 0, 1, 2),
ASCENDC_TPL_UINT_SEL(ROPE_DTYPE, ASCENDC_TPL_UI_LIST, 0, 1),
ASCENDC_TPL_TILING_STRUCT_SEL(optiling::CompressorTilingData)),
);
#endif // COMPRESSOR_TEMPLATE_TILING_KEY_H

View File

@@ -0,0 +1,69 @@
/**
 * 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 COMPRESSOR_tiling_datay.h
* \brief
*/
#ifndef COMPRESSOR_TILING_DATA_H
#define COMPRESSOR_TILING_DATA_H
#include <cstdint>
#include "kernel_tiling/kernel_tiling.h"
const uint32_t CMP_MAX_AIC_CORE_NUM = 26; // 25 + 1 保证数组8字节对齐
namespace optiling {
// 1. 基础参数结构体
struct CompressorBaseParams {
uint32_t batchSize = 0; // bastch size批大小
uint32_t seqSize = 0; // sequence sizekvs大小
uint32_t hiddenSize = 0; // hidden size隐藏层大小
uint32_t tokenSize = 0; // token size = batchSize * seqSize(token总数批大小x序列1长度)
uint32_t headDim = 0; // head size of kv
uint32_t ropeHeadDim = 64; // dim size per rope head 64单个带RoPE头的维度
uint32_t csSize = 0; // Compress sequence len
uint32_t cmpRatio = 4; // Compress ratio
uint32_t cgSize = 0; // Compress group size
float normEps = 1e-6; // RMSNorm eps
float reciprocalD = 0; // 1分之D
uint32_t usedCoreNum = 0; // 使用核数
uint32_t nSize = 0; // 控制v2积攒的轮数
uint64_t stateCacheStrideDim0 = 0; // stateCache第0维的stride
};
struct CompressorPageAttentionParams {
uint32_t blockNum = 0;
uint32_t blockSize = 1;
uint32_t maxBlockNumPerBatch = 1;
};
struct CompressorInnerSplitParams {
uint32_t mBaseSize;
uint32_t dBaseSize;
};
struct CompressorWorkspaceParams {
uint32_t mm1KvResSize;
uint32_t mm1ScoreResSize;
uint32_t vec1ResSize;
uint32_t vec1TailCacheSize;
uint32_t dbWorkspaceRatio = 1;
};
struct CompressorTilingData {
CompressorBaseParams baseParams;
CompressorPageAttentionParams pageAttentionParams;
CompressorInnerSplitParams innerSplitParams;
CompressorWorkspaceParams workspaceParams;
};
} // optiling
#endif // COMPRESSOR_TILING_DATA_H

View File

@@ -0,0 +1,761 @@
/**
 * 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 compressor_tools.h
* \brief 放算子都需要、与算子联系紧密、但是又不方便单独独立出来的公共工具
*/
#ifndef COMPRESSOR_TOOLS_H
#define COMPRESSOR_TOOLS_H
#include "compressor_comm.h"
using namespace AscendC;
namespace Compressor {
struct ToolsParams {
uint32_t seqSize = 0U;
uint32_t cmpRatio = 0U;
};
template <typename COMP>
class CompressorTools {
public:
__aicore__ inline CompressorTools()
{
}
__aicore__ inline void Init(__gm__ uint8_t *cuSeqlens, __gm__ uint8_t *seqUsed, __gm__ uint8_t *startPos);
__aicore__ inline uint32_t GetSeqUsed(uint32_t bIdx);
__aicore__ inline uint32_t GetStartPos(uint32_t bIdx);
__aicore__ inline uint32_t GetSeqLength(uint32_t bIdx);
__aicore__ inline uint32_t GetTIdxByBatch(uint32_t bIdx);
public:
ToolsParams toolParams_{};
bool isExistSeqUsed_ = false;
private:
bool isExistStartPos_ = false;
GlobalTensor<int32_t> cuSeqlensGm_;
GlobalTensor<int32_t> sequsedGm_;
GlobalTensor<int32_t> startPosGm_;
};
template <typename COMP>
__aicore__ inline void CompressorTools<COMP>::Init(__gm__ uint8_t *startPos, __gm__ uint8_t *seqUsed,
__gm__ uint8_t *cuSeqlens)
{
isExistStartPos_ = (startPos != nullptr);
if (isExistStartPos_) {
startPosGm_.SetGlobalBuffer((__gm__ int32_t *)startPos);
}
isExistSeqUsed_ = (seqUsed != nullptr);
if (isExistSeqUsed_) {
sequsedGm_.SetGlobalBuffer((__gm__ int32_t *)seqUsed);
}
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
cuSeqlensGm_.SetGlobalBuffer((__gm__ int32_t *)cuSeqlens);
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetSeqUsed(uint32_t bIdx)
{
if (isExistSeqUsed_) {
return (uint32_t)sequsedGm_.GetValue(bIdx);
} else {
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
return (uint32_t)(cuSeqlensGm_.GetValue(bIdx + 1) - cuSeqlensGm_.GetValue(bIdx));
} else {
return toolParams_.seqSize;
}
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetStartPos(uint32_t bIdx)
{
if (isExistStartPos_) {
return (uint32_t)startPosGm_.GetValue(bIdx);
} else {
return 0;
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetSeqLength(uint32_t bIdx)
{
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
return cuSeqlensGm_.GetValue(bIdx + 1) - cuSeqlensGm_.GetValue(bIdx);
} else {
return toolParams_.seqSize;
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetTIdxByBatch(uint32_t bIdx)
{
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
return (uint32_t)(cuSeqlensGm_.GetValue(bIdx));
} else {
return toolParams_.seqSize * bIdx;
}
}
// iterator
struct SliceInfo {
__aicore__ inline SliceInfo(){};
__aicore__ inline SliceInfo(uint32_t bIdx, uint32_t sIdx) : bIdx(bIdx), sIdx(sIdx){};
uint32_t bIdx = 0U;
uint32_t sIdx = 0U;
uint32_t bSeqUsed = 0U;
uint32_t bStartPos = 0U;
uint32_t headHolderSeqCnt = 0U;
uint32_t validSeqCnt = 0U;
uint32_t tailHolderSeqCnt = 0U;
uint32_t dealSeqCnt = 0;
uint32_t dealTcSize = 0U;
uint32_t compressTcSize = 0U;
};
template <typename COMP>
class CompressorSliceIterator {
public:
__aicore__ inline CompressorSliceIterator(CompressorTools<COMP> &tools) : tools_(tools)
{
}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetMaxDealSeqCnt(uint32_t maxDealSeqCnt);
__aicore__ inline bool IsEnd();
__aicore__ inline void IteratorSlice();
__aicore__ inline SliceInfo &GetSlice();
__aicore__ inline SliceInfo &GetSliceByCmp();
bool isFirst_ = true;
SliceInfo sliceInfo_{};
private:
CompressorTools<COMP> &tools_;
// iterator
uint32_t maxDealSeqCnt_ = 0;
uint32_t batch_size_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx)
{
sliceInfo_.bIdx = bIdx;
sliceInfo_.sIdx = sIdx;
isFirst_ = true;
}
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
}
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::SetMaxDealSeqCnt(uint32_t maxDealSeqCnt)
{
this->maxDealSeqCnt_ = maxDealSeqCnt;
}
template <typename COMP>
__aicore__ inline bool CompressorSliceIterator<COMP>::IsEnd()
{
return (sliceInfo_.bIdx >= batch_size_) || (maxDealSeqCnt_ == 0);
}
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::IteratorSlice()
{
bool isUpdateBatchInfo = false;
if (!isFirst_) {
// 更新剩余未处理的行数
maxDealSeqCnt_ -= sliceInfo_.dealSeqCnt;
// 更新sIdx和bIdx、以及与bIdx相关的bStartPos和bSeqUsed
sliceInfo_.sIdx += sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == sliceInfo_.bSeqUsed) {
sliceInfo_.sIdx = 0;
sliceInfo_.bIdx++;
isUpdateBatchInfo = true;
}
} else {
isUpdateBatchInfo = true;
isFirst_ = false;
}
// 更新与bIdx相关的bStartPos和bSeqUsed
if (isUpdateBatchInfo) {
// SkipInvalidBatch
while (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
if (sliceInfo_.bSeqUsed > 0) {
break;
}
sliceInfo_.bIdx++;
}
if (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
}
}
}
template <typename COMP>
__aicore__ inline SliceInfo &CompressorSliceIterator<COMP>::GetSliceByCmp()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
// 头和尾处理否则需要处理的seq等于cmpRatio
if (sliceInfo_.validSeqCnt < cmpRatio) {
sliceInfo_.dealSeqCnt = sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == 0) {
sliceInfo_.dealSeqCnt = cmpRatio - sliceInfo_.headHolderSeqCnt;
}
} else {
sliceInfo_.dealSeqCnt = cmpRatio;
}
sliceInfo_.validSeqCnt = sliceInfo_.dealSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = (sliceInfo_.dealSeqCnt + cmpRatio - 1) / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
return sliceInfo_;
}
template <typename COMP>
__aicore__ inline SliceInfo &CompressorSliceIterator<COMP>::GetSlice()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
sliceInfo_.dealSeqCnt = sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt + sliceInfo_.tailHolderSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = sliceInfo_.dealSeqCnt / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
return sliceInfo_;
}
struct SplitCoreSliceInfo : public SliceInfo {
__aicore__ inline SplitCoreSliceInfo(){};
__aicore__ inline SplitCoreSliceInfo(uint32_t bIdx, uint32_t sIdx) : SliceInfo(bIdx, sIdx){};
uint32_t preFirstSeqCnt = 0U; // 左边每次迭代基本块的第一个seqCnt大小
};
template <typename COMP>
class CompressorSplitCoreSliceIterator {
public:
__aicore__ inline CompressorSplitCoreSliceIterator(CompressorTools<COMP> &tools) : tools_(tools)
{
}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetMaxDealSeqCnt(uint32_t maxDealSeqCnt);
__aicore__ inline bool IsEnd();
__aicore__ inline void IteratorSlice();
__aicore__ inline SplitCoreSliceInfo &GetSlice();
__aicore__ inline SplitCoreSliceInfo &GetSliceByCmp();
__aicore__ inline uint32_t GetBIdx();
__aicore__ inline SplitCoreSliceInfo &GetLeftNextCmpSeqCnt();
__aicore__ inline SplitCoreSliceInfo &GetRightNextCmpSeqCnt();
bool isFirst_ = true;
bool isLeftFirstBath = false;
bool isMaxDealSeqCntFirst = false;
SplitCoreSliceInfo sliceInfo_{};
private:
CompressorTools<COMP> &tools_;
// iterator
uint32_t maxDealSeqCnt_ = 0;
uint32_t batch_size_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx)
{
sliceInfo_.bIdx = bIdx;
sliceInfo_.sIdx = sIdx;
isFirst_ = true;
}
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
isMaxDealSeqCntFirst = true;
}
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::SetMaxDealSeqCnt(uint32_t maxDealSeqCnt)
{
this->maxDealSeqCnt_ = maxDealSeqCnt;
}
template <typename COMP>
__aicore__ inline bool CompressorSplitCoreSliceIterator<COMP>::IsEnd()
{
return (sliceInfo_.bIdx >= batch_size_) || (maxDealSeqCnt_ == 0);
}
template <typename COMP>
__aicore__ inline uint32_t CompressorSplitCoreSliceIterator<COMP>::GetBIdx()
{
return sliceInfo_.bIdx;
}
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::IteratorSlice()
{
bool isUpdateBatchInfo = false;
if (isMaxDealSeqCntFirst) {
isMaxDealSeqCntFirst = false;
}
if (!isFirst_) {
// 更新剩余未处理的行数
maxDealSeqCnt_ -= sliceInfo_.dealSeqCnt;
// 更新sIdx和bIdx、以及与bIdx相关的bStartPos和bSeqUsed
sliceInfo_.sIdx += sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == sliceInfo_.bSeqUsed) {
sliceInfo_.sIdx = 0;
// 左边最后一块跳到b=0 s=0处理
if (isLeftFirstBath) {
isLeftFirstBath = false;
} else {
sliceInfo_.bIdx++;
}
isUpdateBatchInfo = true;
}
} else {
isUpdateBatchInfo = true;
isFirst_ = false;
}
// 更新与bIdx相关的bStartPos和bSeqUsed
if (isUpdateBatchInfo) {
// SkipInvalidBatch
while (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
if (sliceInfo_.bSeqUsed > 0) {
break;
}
sliceInfo_.bIdx++;
}
if (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
}
}
}
template <typename COMP>
__aicore__ inline SplitCoreSliceInfo &CompressorSplitCoreSliceIterator<COMP>::GetLeftNextCmpSeqCnt()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
// 左边 T轴首次减去T轴最后一块
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(batch_size_ - 1);
sliceInfo_.bStartPos = tools_.GetStartPos(batch_size_ - 1);
// 处理最后一块是中间整块或者尾块的情况
uint32_t lastSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.bSeqUsed) % cmpRatio == 0 ?
cmpRatio :
(sliceInfo_.bStartPos + sliceInfo_.bSeqUsed) % cmpRatio;
// 处理最后一块是头块的情况
if (sliceInfo_.bSeqUsed < cmpRatio) {
lastSeqCnt = sliceInfo_.bSeqUsed;
}
sliceInfo_.sIdx = sliceInfo_.bSeqUsed - lastSeqCnt;
isLeftFirstBath = true;
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
// 头和尾处理否则需要处理的seq等于cmpRatio
if (sliceInfo_.validSeqCnt < cmpRatio) {
sliceInfo_.dealSeqCnt = sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == 0) {
sliceInfo_.dealSeqCnt = cmpRatio - sliceInfo_.headHolderSeqCnt;
}
} else {
sliceInfo_.dealSeqCnt = cmpRatio;
}
sliceInfo_.validSeqCnt = sliceInfo_.dealSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = (sliceInfo_.dealSeqCnt + cmpRatio - 1) / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
// 记录左边第一个块
if (isMaxDealSeqCntFirst) {
sliceInfo_.preFirstSeqCnt = sliceInfo_.dealSeqCnt;
}
return sliceInfo_;
}
template <typename COMP>
__aicore__ inline SplitCoreSliceInfo &CompressorSplitCoreSliceIterator<COMP>::GetRightNextCmpSeqCnt()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
// 头和尾处理否则需要处理的seq等于cmpRatio
if (sliceInfo_.validSeqCnt < cmpRatio) {
sliceInfo_.dealSeqCnt = sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == 0) {
sliceInfo_.dealSeqCnt = cmpRatio - sliceInfo_.headHolderSeqCnt;
}
} else {
sliceInfo_.dealSeqCnt = cmpRatio;
}
sliceInfo_.validSeqCnt = sliceInfo_.dealSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = (sliceInfo_.dealSeqCnt + cmpRatio - 1) / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
return sliceInfo_;
}
struct Vec1SliceInfo : public SliceInfo {
__aicore__ inline Vec1SliceInfo(){};
__aicore__ inline Vec1SliceInfo(uint32_t bIdx, uint32_t sIdx) : SliceInfo(bIdx, sIdx){};
__aicore__ inline Vec1SliceInfo(uint32_t bIdx, uint32_t sIdx, uint32_t dealedSeqCnt)
: SliceInfo(bIdx, sIdx), dealedSeqCnt(dealedSeqCnt){};
uint32_t dealedSeqCnt = 0U;
uint32_t dealedTcCnt = 0U;
uint32_t bSeqLength = 0U;
uint32_t compressoredScCnt = 0U;
bool isFirst = false;
bool isLast = false;
};
struct StatisticInfo {
__aicore__ inline StatisticInfo(){};
__aicore__ inline StatisticInfo(uint32_t actualTcCnt, uint32_t dealSeqCnt, uint32_t compressorScCnt)
: actualTcCnt(actualTcCnt), dealSeqCnt(dealSeqCnt), compressorScCnt(compressorScCnt){};
uint32_t actualTcCnt = 0U;
uint32_t dealSeqCnt = 0U;
uint32_t compressorScCnt = 0U;
};
template <typename COMP>
class CompressorVec1SliceIterator {
public:
__aicore__ inline CompressorVec1SliceIterator(CompressorTools<COMP> &tools) : tools_(tools)
{
}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx);
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx, uint32_t dealedSeqCnt, uint32_t compressoredScCnt);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetDealedSeqCnt(uint32_t dealedSeqCnt);
__aicore__ inline void SetDealedTcCnt(uint32_t dealedTcCnt);
__aicore__ inline void SetCompressoredScCnt(uint32_t compressoredScCnt);
__aicore__ inline void SetNeedDealTcSize(uint32_t needDealTcSize);
__aicore__ inline void SetNeedDealTcSize(uint32_t needDealTcSize, uint32_t canDealTcSize);
__aicore__ inline uint32_t GetNeedDealTcSize();
__aicore__ inline bool IsEnd();
template <bool IS_STATISTIC = false>
__aicore__ inline void IteratorSlice();
__aicore__ inline Vec1SliceInfo &GetSlice();
template <bool IS_STATISTIC = false>
__aicore__ inline StatisticInfo &FullIteratorSlice();
private:
CompressorTools<COMP> &tools_;
bool isFirst_ = true;
Vec1SliceInfo sliceInfo_{};
StatisticInfo statisticInfo_{};
uint32_t needDealTcSize_ = 0U;
uint32_t batch_size_ = 0U;
};
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx)
{
sliceInfo_.bIdx = bIdx;
sliceInfo_.sIdx = sIdx;
while (tools_.GetSeqLength(sliceInfo_.bIdx) == 0) {
sliceInfo_.bIdx++;
if (sliceInfo_.bIdx == batch_size_) {
sliceInfo_.bIdx = 0;
}
}
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
sliceInfo_.bSeqLength = tools_.GetSeqLength(sliceInfo_.bIdx);
isFirst_ = true;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx, uint32_t dealedSeqCnt,
uint32_t compressoredScCnt)
{
Reset(bIdx, sIdx);
SetDealedSeqCnt(dealedSeqCnt);
SetCompressoredScCnt(compressoredScCnt);
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetDealedSeqCnt(uint32_t dealedSeqCnt)
{
this->sliceInfo_.dealedSeqCnt = dealedSeqCnt;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetCompressoredScCnt(uint32_t compressoredScCnt)
{
this->sliceInfo_.compressoredScCnt = compressoredScCnt;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetDealedTcCnt(uint32_t dealedTcCnt)
{
this->sliceInfo_.dealedTcCnt = dealedTcCnt;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetNeedDealTcSize(uint32_t needDealTcSize)
{
this->needDealTcSize_ = needDealTcSize;
}
template <typename COMP>
template <bool IS_STATISTIC>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::IteratorSlice()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if constexpr (IS_STATISTIC) {
statisticInfo_.actualTcCnt += sliceInfo_.dealTcSize;
statisticInfo_.compressorScCnt += sliceInfo_.compressTcSize;
}
needDealTcSize_ -= sliceInfo_.dealTcSize;
sliceInfo_.dealedSeqCnt += sliceInfo_.validSeqCnt;
sliceInfo_.compressoredScCnt += sliceInfo_.compressTcSize;
sliceInfo_.sIdx += sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx >= sliceInfo_.bSeqUsed) {
do {
uint32_t seqLength = tools_.GetSeqLength(sliceInfo_.bIdx);
if (sliceInfo_.bSeqUsed < seqLength) {
uint32_t nextAlignSIdx = Align(sliceInfo_.bStartPos + sliceInfo_.sIdx, cmpRatio) - sliceInfo_.bStartPos;
sliceInfo_.dealedSeqCnt += nextAlignSIdx - sliceInfo_.sIdx;
uint32_t tcGap = CeilDivT(static_cast<int32_t>(seqLength - nextAlignSIdx),
static_cast<int32_t>(cmpRatio));
if (sliceInfo_.bSeqUsed == 0 && nextAlignSIdx > sliceInfo_.sIdx) {
// 此时bseqused所在压缩块未被纳入计算
tcGap++;
}
sliceInfo_.sIdx = nextAlignSIdx;
if (needDealTcSize_ < tcGap) {
sliceInfo_.dealedSeqCnt += needDealTcSize_ * cmpRatio;
sliceInfo_.sIdx += needDealTcSize_ * cmpRatio;
needDealTcSize_ = 0;
break;
}
sliceInfo_.dealedSeqCnt += seqLength - sliceInfo_.sIdx;
needDealTcSize_ -= tcGap;
}
sliceInfo_.bIdx++;
if (sliceInfo_.bIdx == batch_size_) {
sliceInfo_.bIdx = 0;
}
sliceInfo_.sIdx = 0;
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
} while (sliceInfo_.bSeqUsed == 0);
sliceInfo_.bSeqLength = tools_.GetSeqLength(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
}
if (isFirst_) {
isFirst_ = false;
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorVec1SliceIterator<COMP>::GetNeedDealTcSize()
{
return needDealTcSize_;
}
template <typename COMP>
__aicore__ inline bool CompressorVec1SliceIterator<COMP>::IsEnd()
{
return (needDealTcSize_ == 0);
}
template <typename COMP>
__aicore__ inline Vec1SliceInfo &CompressorVec1SliceIterator<COMP>::GetSlice()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (sliceInfo_.bSeqUsed < sliceInfo_.sIdx) {
sliceInfo_.headHolderSeqCnt = 0;
sliceInfo_.validSeqCnt = 0;
sliceInfo_.tailHolderSeqCnt = 0;
sliceInfo_.dealTcSize = 0;
sliceInfo_.compressTcSize = 0;
} else {
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (CeilDivT(sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt, cmpRatio) > needDealTcSize_) {
sliceInfo_.validSeqCnt = needDealTcSize_ * cmpRatio - sliceInfo_.headHolderSeqCnt;
}
uint32_t globalTotalSeqCnt = sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt;
sliceInfo_.tailHolderSeqCnt = Align(globalTotalSeqCnt, cmpRatio) - globalTotalSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize =
(sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt + sliceInfo_.tailHolderSeqCnt) / cmpRatio;
sliceInfo_.compressTcSize =
(sliceInfo_.headHolderSeqCnt + min(sliceInfo_.validSeqCnt, sliceInfo_.bSeqUsed - sliceInfo_.sIdx)) /
cmpRatio;
}
sliceInfo_.isFirst = isFirst_;
sliceInfo_.isLast =
sliceInfo_.bSeqUsed > sliceInfo_.sIdx &&
CeilDivT(sliceInfo_.headHolderSeqCnt + sliceInfo_.bSeqUsed - sliceInfo_.sIdx, cmpRatio) >= needDealTcSize_;
return sliceInfo_;
}
template <typename COMP>
template <bool IS_STATISTIC>
__aicore__ inline StatisticInfo &CompressorVec1SliceIterator<COMP>::FullIteratorSlice()
{
if constexpr (IS_STATISTIC) {
statisticInfo_ = {0U, 0U, 0U};
Vec1SliceInfo tempSliceInfo = GetSlice();
while (!IsEnd()) {
GetSlice();
IteratorSlice<IS_STATISTIC>();
}
Vec1SliceInfo sliceInfo = GetSlice();
statisticInfo_.dealSeqCnt = sliceInfo.dealedSeqCnt - tempSliceInfo.dealedSeqCnt;
} else {
while (!IsEnd()) {
GetSlice();
IteratorSlice<IS_STATISTIC>();
}
}
return statisticInfo_;
}
} // namespace Compressor
#endif

View File

@@ -0,0 +1,347 @@
/**
 * 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 compressor_vector_comm.h
* \brief 存放各种vector的公共组件
*/
#ifndef COMPRESSOR_VECTOR_COMM_H
#define COMPRESSOR_VECTOR_COMM_H
#include "compressor_comm.h"
namespace Compressor {
struct MatRpeatParam {
uint32_t row;
uint32_t col;
uint32_t dtypeMask;
uint32_t loopTimes;
uint32_t colRemain;
uint8_t repeatStride;
};
struct RmsNormParam {
float reciprocal;
float epsilon;
uint32_t row;
uint32_t col;
};
/**
* @brief ColumnSum 对矩阵按列进行求和
* @param dstLocal 输出tensor [1, col]支持和shareTmpUb是同一块空间
* @param srcLocal 输入tensor [row, col]
* @param shareTmpUb 临时buffer 内部需要的空间为 [ceil(row / 2) * col * sizeof(float)]
* @param row 行数
* @param col 列数
*/
__aicore__ inline void ColumnSum(const LocalTensor<float> &dstLocal, const LocalTensor<float> &srcLocal,
const LocalTensor<float> &shareTmpUb, uint32_t row, uint32_t col)
{
// 行数为1时直接将srcLocal复制到dstLocal
if (unlikely(row == 1)) {
DataCopy(dstLocal, srcLocal, row * col);
PipeBarrier<PIPE_V>();
return;
}
for (uint32_t mask = MAX_R << 1; mask > 1; mask >>= 1) {
if (row & mask) {
// 将输入对半求和后放进临时空间
Add(shareTmpUb, srcLocal, srcLocal[mask * col / 2], mask * col / 2); // 2:对矩阵按列做计算
PipeBarrier<PIPE_V>();
// 将余量加到前一半上
if (unlikely(row > mask)) {
if ((row - mask) > (mask >> 1)) {
Add(shareTmpUb, shareTmpUb, srcLocal[mask * col], mask * col / 2); // 2:对矩阵按列做计算
PipeBarrier<PIPE_V>();
Add(shareTmpUb, shareTmpUb, srcLocal[(mask + (mask >> 1)) * col], (row - mask - (mask >> 1)) * col);
PipeBarrier<PIPE_V>();
} else {
Add(shareTmpUb, shareTmpUb, srcLocal[mask * col], (row - mask) * col);
PipeBarrier<PIPE_V>();
}
}
// 每次将后一半行加到前一半上
for (uint32_t i = mask >> 2; i > 1; i >>= 1) {
Add(shareTmpUb, shareTmpUb, shareTmpUb[i * col], i * col);
PipeBarrier<PIPE_V>();
}
if (mask == 2) { // 2:最后一次矩阵运算处理
DataCopy(dstLocal, shareTmpUb, col);
} else {
Add(dstLocal, shareTmpUb, shareTmpUb[col], col);
}
PipeBarrier<PIPE_V>();
break;
}
}
}
/**
* @brief ColumnMax 对矩阵按列进行求最大值
* @param dstLocal 输出tensor [1, col]支持和shareTmpUb是同一块空间
* @param srcLocal 输入tensor [row, col]
* @param shareTmpUb 临时buffer 内部需要的空间为 [ceil(row / 2) * col * sizeof(float)]
* @param row 行数
* @param col 列数
*/
__aicore__ inline void ColumnMax(const LocalTensor<float> &dstLocal, const LocalTensor<float> &srcLocal,
const LocalTensor<float> &shareTmpUb, uint32_t row, uint32_t col)
{
// 行数为1时直接将srcLocal复制到dstLocal
if (unlikely(row == 1)) {
DataCopy(dstLocal, srcLocal, row * col);
PipeBarrier<PIPE_V>();
return;
}
for (uint32_t mask = MAX_R << 1; mask > 1; mask >>= 1) {
if (row & mask) {
// 将输入对半求最大值后放进临时空间
Max(shareTmpUb, srcLocal, srcLocal[mask * col / 2], mask * col / 2); // 2:对矩阵按列做计算
PipeBarrier<PIPE_V>();
// 将余量和前一半求最大值后加到前一半上
if (unlikely(row > mask)) {
if ((row - mask) > (mask >> 1)) {
Max(shareTmpUb, shareTmpUb, srcLocal[mask * col], mask * col / 2); // 2:对矩阵按列做计算
PipeBarrier<PIPE_V>();
Max(shareTmpUb, shareTmpUb, srcLocal[(mask + (mask >> 1)) * col], (row - mask - (mask >> 1)) * col);
PipeBarrier<PIPE_V>();
} else {
Max(shareTmpUb, shareTmpUb, srcLocal[mask * col], (row - mask) * col);
PipeBarrier<PIPE_V>();
}
}
// 每次将后一半行和前一半最大值后加到前一半上
for (uint32_t i = mask >> 2; i > 1; i >>= 1) {
Max(shareTmpUb, shareTmpUb, shareTmpUb[i * col], i * col);
PipeBarrier<PIPE_V>();
}
if (mask == 2) { // 2:最后一次矩阵运算处理
DataCopy(dstLocal, shareTmpUb, col);
} else {
Max(dstLocal, shareTmpUb, shareTmpUb[col], col);
}
PipeBarrier<PIPE_V>();
break;
}
}
}
/**
* @brief MatSubVec 矩阵逐行减向量
* @param dstLocal 输出tensor [row, col]
* @param src0Local 输入tensor [row, col]
* @param src1Local 输入tensor [1, col]
* @param repeatParam 描述待处理数据的排布,包括
row 行数
col 列数
dtypeMask 一次迭代参与计算元素数
loopTimes 循环次数
colRemain 剩余列数
repeatStride 循环步长(内存中实际列长度)
*/
__aicore__ inline void MatSubVec(const LocalTensor<float> &dstLocal, const LocalTensor<float> &src0Local,
const LocalTensor<float> &src1Local, const MatRpeatParam &repeatParam)
{
for (uint32_t row = 0; row < repeatParam.row; row += REPEAT_MAX_NUM) {
uint32_t repeatRowTimes = Std::min(repeatParam.row - row, REPEAT_MAX_NUM);
uint32_t offset = 0;
for (uint32_t i = 0; i < repeatParam.loopTimes; i++) {
Sub(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local[offset],
repeatParam.dtypeMask, repeatRowTimes,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, 0});
offset += repeatParam.dtypeMask;
}
if (repeatParam.colRemain > 0) {
Sub(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local[offset],
repeatParam.colRemain, repeatRowTimes,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, 0});
}
}
}
/**
* @brief MatDivVec 矩阵逐行除以向量
* @param dstLocal 输出tensor [row, col]
* @param src0Local 输入tensor [row, col]
* @param src1Local 输入tensor [1, col]
* @param repeatParam 描述待处理数据的排布,包括
row 行数
col 列数
dtypeMask 一次迭代参与计算元素数
loopTimes 循环次数
colRemain 剩余列数
repeatStride 循环步长(内存中实际列长度)
*/
__aicore__ inline void MatDivVec(const LocalTensor<float> &dstLocal, const LocalTensor<float> &src0Local,
const LocalTensor<float> &src1Local, const MatRpeatParam &repeatParam)
{
for (uint32_t row = 0; row < repeatParam.row; row += REPEAT_MAX_NUM) {
uint32_t repeatRowTimes = Std::min(repeatParam.row - row, REPEAT_MAX_NUM);
uint32_t offset = 0;
for (uint32_t i = 0; i < repeatParam.loopTimes; i++) {
Div(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local[offset],
repeatParam.dtypeMask, repeatRowTimes,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, 0});
offset += repeatParam.dtypeMask;
}
if (repeatParam.colRemain > 0) {
Div(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local[offset],
repeatParam.colRemain, repeatRowTimes,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, 0});
}
}
}
/**
* @brief MatMulVec 矩阵逐行乘以向量
* @param dstLocal 输出tensor [row, col]
* @param src0Local 输入tensor [row, col]
* @param src1Local 输入tensor [1, col]
* @param repeatParam 描述待处理数据的排布,包括
row 行数
col 列数
dtypeMask 一次迭代参与计算元素数
loopTimes 循环次数
colRemain 剩余列数
repeatStride 循环步长(内存中实际列长度)
*/
__aicore__ inline void MatMulVec(const LocalTensor<float> &dstLocal, const LocalTensor<float> &src0Local,
const LocalTensor<float> &src1Local, const MatRpeatParam &repeatParam)
{
for (uint32_t row = 0; row < repeatParam.row; row += REPEAT_MAX_NUM) {
uint32_t repeatRowTimes = Std::min(repeatParam.row - row, REPEAT_MAX_NUM);
uint32_t offset = 0;
for (uint32_t i = 0; i < repeatParam.loopTimes; i++) {
Mul(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local[offset],
repeatParam.dtypeMask, repeatRowTimes,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, 0});
offset += repeatParam.dtypeMask;
}
if (repeatParam.colRemain > 0) {
Mul(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local[offset],
repeatParam.colRemain, repeatRowTimes,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, 0});
}
}
}
/**
* @brief RowSum 矩阵对每行求和
* @param dstLocal 输出tensor [1, row]
* @param srcLocal 输入tensor [row, col]
* @param shareTmpUb 临时buffer 内部需要的空间为 [row, col]支持和srcLocal是同一块空间
* @param repeatParam 描述待处理数据的排布,包括
row 行数
col 列数
dtypeMask 一次迭代参与计算元素数
loopTimes 循环次数
colRemain 剩余列数
repeatStride 循环步长(内存中实际列长度)
*/
__aicore__ inline void RowSum(const LocalTensor<float> &dstLocal, const LocalTensor<float> &srcLocal,
const LocalTensor<float> &shareTmpUb, const MatRpeatParam &repeatParam)
{
uint32_t blockCount = repeatParam.loopTimes;
if (blockCount > 0 && repeatParam.colRemain > 0) {
Add(shareTmpUb, srcLocal, srcLocal[blockCount * repeatParam.dtypeMask], repeatParam.colRemain,
repeatParam.row,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, repeatParam.repeatStride});
AscendC::PipeBarrier<PIPE_V>();
}
for (uint32_t loopCount = blockCount >> 1; loopCount > 0; loopCount = blockCount >> 1) {
blockCount = (blockCount + 1) >> 1;
for (uint32_t i = 0; i < loopCount; i++) {
Add(shareTmpUb[i * repeatParam.dtypeMask], srcLocal[i * repeatParam.dtypeMask],
srcLocal[(i + blockCount) * repeatParam.dtypeMask], repeatParam.dtypeMask, repeatParam.row,
{1, 1, 1, repeatParam.repeatStride, repeatParam.repeatStride, repeatParam.repeatStride});
}
AscendC::PipeBarrier<PIPE_V>();
}
WholeReduceSum(dstLocal, shareTmpUb,
(repeatParam.col < repeatParam.dtypeMask) ? repeatParam.col :
repeatParam.dtypeMask,
repeatParam.row, 1, 1, repeatParam.repeatStride);
}
/**
* @brief RowDivs 矩阵每行除以对应元素
* @param dstLocal 输出tensor [row, col]
* @param src0Local 输入tensor [row, col]
* @param src1Local 输入tensor [row, 1]需要扩展到一个datablock中(实际内存需要为[row, FP32_BLOCK_ELEMENT_NUM])
* @param repeatParam 描述待处理数据的排布,包括
row 行数
col 列数
dtypeMask 一次迭代参与计算元素数
loopTimes 循环次数
colRemain 剩余列数
repeatStride 循环步长(内存中实际列长度)
*/
__aicore__ inline void RowDivs(const LocalTensor<float> &dstLocal, const LocalTensor<float> &src0Local,
const LocalTensor<float> &src1Local, const MatRpeatParam &repeatParam)
{
for (uint32_t row = 0; row < repeatParam.row; row += REPEAT_MAX_NUM) {
uint32_t repeatRowTimes = Std::min(repeatParam.row - row, REPEAT_MAX_NUM);
uint32_t offset = 0;
for (uint32_t i = 0; i < repeatParam.loopTimes; i++) {
Div(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local,
repeatParam.dtypeMask, repeatRowTimes,
{1, 1, 0, repeatParam.repeatStride, repeatParam.repeatStride, 1});
offset += repeatParam.dtypeMask;
}
if (repeatParam.colRemain > 0) {
Div(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local,
repeatParam.colRemain, repeatRowTimes,
{1, 1, 0, repeatParam.repeatStride, repeatParam.repeatStride, 1});
}
}
}
/**
* @brief RowMuls 矩阵每行乘以相同元素
* @param dstLocal 输出tensor [row, col]
* @param src0Local 输入tensor [row, col]
* @param src1Local 输入tensor [row, 1]需要扩展到一个datablock中(实际内存需要为[row, FP32_BLOCK_ELEMENT_NUM])
* @param repeatParam 描述待处理数据的排布,包括
row 行数
col 列数
dtypeMask 一次迭代参与计算元素数
loopTimes 循环次数
colRemain 剩余列数
repeatStride 循环步长(内存中实际列长度)
*/
__aicore__ inline void RowMuls(const LocalTensor<float> &dstLocal, const LocalTensor<float> &src0Local,
const LocalTensor<float> &src1Local, const MatRpeatParam &repeatParam)
{
for (uint32_t row = 0; row < repeatParam.row; row += REPEAT_MAX_NUM) {
uint32_t repeatRowTimes = Std::min(repeatParam.row - row, REPEAT_MAX_NUM);
uint32_t offset = 0;
for (uint32_t i = 0; i < repeatParam.loopTimes; i++) {
Mul(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local,
repeatParam.dtypeMask, repeatRowTimes,
{1, 1, 0, repeatParam.repeatStride, repeatParam.repeatStride, 1});
offset += repeatParam.dtypeMask;
}
if (repeatParam.colRemain > 0) {
Mul(dstLocal[row * repeatParam.col + offset], src0Local[row * repeatParam.col + offset], src1Local,
repeatParam.colRemain, repeatRowTimes,
{1, 1, 0, repeatParam.repeatStride, repeatParam.repeatStride, 1});
}
}
}
} // namespace Compressor
#endif // COMPRESSOR_VECTOR_COMM_H

View File

@@ -0,0 +1,87 @@
/**
 * 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 rms_norm.h
* \brief
*/
#ifndef RMS_NORM_H
#define RMS_NORM_H
#include "compressor_comm.h"
#include "compressor_vector_comm.h"
namespace Compressor {
/**
* @brief RmsNorm 对矩阵进行rmsnorm
* @param dstLocal 输出tensor [row, col]支持和srcLocal是同一块空间
* @param srcLocal 输入tensor [row, col]
* @param gammaLocal 系数gamma [1, col]
* @param shareTmpUb 临时buffer 内部需要的空间为 [(row * col + row) * sizeof(float)]
* @param rmsNormParams rms所需系数包括
reciprocal rmsnorm系数reciprocal
epsilon rmsnorm系数epsilon
row 处理的行数
col 列数
*/
template <typename GammaType>
__aicore__ inline void RmsNorm(const LocalTensor<float> &dstLocal, const LocalTensor<float> &srcLocal,
const LocalTensor<GammaType> &gammaLocal, const LocalTensor<float> &shareTmpUb,
const RmsNormParam &rmsNormParams)
{
uint64_t cnt = rmsNormParams.row * rmsNormParams.col;
LocalTensor<float> temp1Local = shareTmpUb.ReinterpretCast<float>();
LocalTensor<float> temp2Local = temp1Local[cnt];
// temp1Local = srcLocal ^ 2
Mul(temp1Local, srcLocal, srcLocal, cnt);
PipeBarrier<PIPE_V>();
MatRpeatParam repeatParams = {
rmsNormParams.row, // row
rmsNormParams.col, // col
FP32_REPEAT_ELEMENT_NUM, // dtypeMask
rmsNormParams.col / FP32_REPEAT_ELEMENT_NUM, // loopTimes
rmsNormParams.col % FP32_REPEAT_ELEMENT_NUM, // colsRemain
static_cast<uint8_t>(rmsNormParams.col / FP32_BLOCK_ELEMENT_NUM), // repeatStride
};
// temp2Local[row] = Sum(temp1Local)
RowSum(temp2Local, temp1Local, temp1Local, repeatParams);
PipeBarrier<PIPE_V>();
// temp2Local[row] = temp2Local[row] * reciprocal(1/N)
Muls(temp2Local, temp2Local, rmsNormParams.reciprocal, rmsNormParams.row);
PipeBarrier<PIPE_V>();
// temp2Local[row] = temp2Local[row] + epsilon
Adds(temp2Local, temp2Local, rmsNormParams.epsilon, rmsNormParams.row);
PipeBarrier<PIPE_V>();
// temp2Local[row] = Sqrt(temp2Local[row])
Sqrt(temp2Local, temp2Local, rmsNormParams.row);
PipeBarrier<PIPE_V>();
// temp1Local[row, 8] = brc(temp2Local[row, 1])
Brcb(temp1Local, temp2Local, CeilDivT(rmsNormParams.row, BRCB_NUM), {1, 8});
PipeBarrier<PIPE_V>();
// dstLocal = srcLocal / temp1Local(sum)
RowDivs(dstLocal, srcLocal, temp1Local, repeatParams);
PipeBarrier<PIPE_V>();
// dstLocal = dstLocal * gammaLocal
MatMulVec(dstLocal, dstLocal, gammaLocal, repeatParams);
}
} // namespace Compressor
#endif // MLA_PROLOG_RMS_NORM_H

View File

@@ -0,0 +1,130 @@
/**
 * 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 rope.h
* \brief
*/
#ifndef ROPE_H
#define ROPE_H
#include "compressor_comm.h"
#include "compressor_vector_comm.h"
namespace Compressor {
/**
* @brief SetGatherSrcOffset 计算用于interleave模式的offset
* @param gatherOffsetLocal 输出tensor [count]数据类型需要为int64_t使用时要转换
* @param count offset的元素个数一般为列数
*/
template <typename T>
__aicore__ inline void SetGatherSrcOffset(const LocalTensor<int32_t> &gatherOffsetLocal, uint32_t count)
{
for (uint32_t i = 0; i < 8; i++) {
gatherOffsetLocal.SetValue(i, i ^ 1);
}
event_t eventId_S_V = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::S_V));
SetFlag<HardEvent::S_V>(eventId_S_V);
WaitFlag<HardEvent::S_V>(eventId_S_V);
int32_t scalarValue = 8;
while (scalarValue < count) {
int32_t nextValue = scalarValue * 2;
PipeBarrier<PIPE_V>();
if (nextValue < count) {
Adds(gatherOffsetLocal[scalarValue], gatherOffsetLocal, scalarValue, scalarValue);
} else {
Adds(gatherOffsetLocal[scalarValue], gatherOffsetLocal, scalarValue, count - scalarValue);
break;
}
scalarValue = nextValue;
}
PipeBarrier<PIPE_V>();
Muls(gatherOffsetLocal, gatherOffsetLocal, static_cast<int32_t>(sizeof(T)), count);
}
/**
* @brief RotaryPosEmb 同时做row行的RotaryPosEmb每一行的元素为col
* @param dstLocal 输出tensor [row, actualCol]支持和srcLocal是同一块空间
* @param srcLocal 输入tensor [row, actualCol]
* @param cosLocal cos系数tensor [row, col]
* @param sinLocal sin系数tensor [row, col]
* @param shareTmpUb 临时buffer 内部需要的空间为 [row * col * sizeof(float)]
* @param gatherOffsetcastLocal 用于interleave模式的offset数据类型需要为uint64_t
* @param row 待处理的行数
* @param col 待处理的列数
* @param actualCol 实际列数
* @param baseAddr 计算基地址
*/
template <ROTARY_MODE MODE>
__aicore__ inline void RotaryPosEmb(const LocalTensor<float> &dstLocal, const LocalTensor<float> &srcLocal,
const LocalTensor<float> &cosLocal, const LocalTensor<float> &sinLocal,
const LocalTensor<float> &shareTmpUb,
const LocalTensor<uint32_t> &gatherOffsetcastLocal, uint32_t row, uint32_t col,
uint32_t actualCol, uint64_t baseAddr)
{
uint64_t cnt = row * col;
uint32_t half_col = col >> 1;
uint64_t rsvdCnt = 0;
LocalTensor<float> reArrLocal = shareTmpUb.ReinterpretCast<float>();
if constexpr (MODE == ROTARY_MODE::HALF) {
DataCopy(reArrLocal, srcLocal[baseAddr + half_col],
{static_cast<uint16_t>(row), static_cast<uint16_t>(CeilDivT(half_col, FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint16_t>(CeilDivT(actualCol - half_col, FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint16_t>(CeilDivT(half_col, FP32_BLOCK_ELEMENT_NUM))});
DataCopy(reArrLocal[half_col], srcLocal[baseAddr],
{static_cast<uint16_t>(row), static_cast<uint16_t>(CeilDivT(half_col, FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint16_t>(CeilDivT(actualCol - half_col, FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint16_t>(CeilDivT(half_col, FP32_BLOCK_ELEMENT_NUM))});
PipeBarrier<PIPE_V>();
Muls(reArrLocal, reArrLocal, float(-1), half_col, row,
{1, 1, static_cast<uint8_t>(CeilDivT(static_cast<uint32_t>(col), FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint8_t>(CeilDivT(static_cast<uint32_t>(col), FP32_BLOCK_ELEMENT_NUM))});
} else if constexpr (MODE == ROTARY_MODE::INTERLEAVE) {
for (uint32_t i = 0; i < row; i++) {
Gather(reArrLocal[i * col], srcLocal[i * actualCol + baseAddr], gatherOffsetcastLocal, 0, col);
}
PipeBarrier<PIPE_V>();
uint32_t repeatTimes = cnt / FP32_REPEAT_ELEMENT_NUM;
uint32_t remainder = cnt % FP32_REPEAT_ELEMENT_NUM;
uint64_t fullMask = 0x5555555555555555;
uint64_t partialMask = 0x55;
SetVectorMask<float, MaskMode::NORMAL>(0, fullMask);
Muls<float, false>(reArrLocal, reArrLocal, float(-1), MASK_PLACEHOLDER, repeatTimes,
{1, 1, FP32_BLOCK_ELEMENT_NUM, FP32_BLOCK_ELEMENT_NUM});
if (unlikely(remainder > 0)) {
SetVectorMask<float, MaskMode::NORMAL>(0, partialMask);
Muls<float, false>(reArrLocal[repeatTimes * FP32_REPEAT_ELEMENT_NUM],
reArrLocal[repeatTimes * FP32_REPEAT_ELEMENT_NUM], float(-1), MASK_PLACEHOLDER,
remainder / FP32_BLOCK_ELEMENT_NUM, {1, 1, 1, 1});
}
ResetMask();
}
PipeBarrier<PIPE_V>();
BinaryRepeatParams computeParams{1,
1,
1,
static_cast<uint8_t>(CeilDivT(actualCol, FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint8_t>(CeilDivT(actualCol, FP32_BLOCK_ELEMENT_NUM)),
static_cast<uint8_t>(CeilDivT(col, FP32_BLOCK_ELEMENT_NUM))};
Mul(dstLocal[baseAddr], srcLocal[baseAddr], cosLocal, col, row, computeParams);
Mul(reArrLocal, reArrLocal, sinLocal, cnt);
PipeBarrier<PIPE_V>();
Add(dstLocal[baseAddr], dstLocal[baseAddr], reArrLocal, col, row, computeParams);
}
}
#endif

View File

@@ -0,0 +1,52 @@
/**
 * 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 soft_max.h
* \brief
*/
#ifndef SOFT_MAX_H
#define SOFT_MAX_H
#include "compressor_comm.h"
#include "compressor_vector_comm.h"
namespace Compressor {
/**
* @brief ColumnSoftMax 对矩阵按列进行SoftMax
* @param dstLocal 输出tensor [row, col]支持和srcLocal是同一块空间
* @param srcLocal 输入tensor [row, col]
* @param shareTmpUb 临时buffer 内部需要的空间为 [floor(row / 2) * col * sizeof(float)]
* @param row 行数
* @param col 列数
*/
__aicore__ inline void ColumnSoftMax(const LocalTensor<float> &dstLocal, const LocalTensor<float> &srcLocal,
const LocalTensor<float> &shareTmpUb, uint32_t row, uint32_t col)
{
uint32_t dtypeMask = FP32_REPEAT_ELEMENT_NUM;
uint32_t dLoop = col / dtypeMask;
uint32_t dRemain = col % dtypeMask;
uint8_t repeatStride = col / FP32_BLOCK_ELEMENT_NUM;
ColumnMax(shareTmpUb, srcLocal, shareTmpUb, row, col);
PipeBarrier<PIPE_V>();
MatSubVec(dstLocal, srcLocal, shareTmpUb, {row, col, dtypeMask, dLoop, dRemain, repeatStride});
PipeBarrier<PIPE_V>();
Exp(dstLocal, dstLocal, row * col);
PipeBarrier<PIPE_V>();
ColumnSum(shareTmpUb, dstLocal, shareTmpUb, row, col);
PipeBarrier<PIPE_V>();
MatDivVec(dstLocal, dstLocal, shareTmpUb, {row, col, dtypeMask, dLoop, dRemain, repeatStride});
}
} // namespace Compressor
#endif

View File

@@ -0,0 +1,439 @@
/**
 * 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 compressor_block_cube.h
* \brief
*/
#ifndef COMPRESSOR_BLOCK_CUBE_H
#define COMPRESSOR_BLOCK_CUBE_H
#include "compressor_comm.h"
#include "compressor_tools.h"
using namespace AscendC;
namespace Compressor {
template<typename COMP> class CompressorBlockCube {
using MM1_OUT_T = float;
public:
__aicore__ inline CompressorBlockCube(){};
__aicore__ inline void InitParams(const ConstInfo &constInfo, const CompressorTools<COMP> &tools);
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut);
__aicore__ inline void InitBuffers(TPipe *pipe);
__aicore__ inline void InitGlobalBuffers(const GlobalTensor<MM1_OUT_T>& kvMm1ResGm, const GlobalTensor<MM1_OUT_T>& scoreMm1ResGm);
__aicore__ inline void AllocEventID(TPipe *pipe);
__aicore__ inline void FreeEventID(TPipe *pipe);
__aicore__ inline void ComputeMm1(const RunInfo &info);
private:
using T = float;
using X_T = typename AscendC::Conditional<COMP::xDtype == X_DTYPE::BF16, bfloat16_t, half>::type;
__aicore__ inline uint32_t GetMSize(const RunInfo &info, uint32_t coffId);
__aicore__ inline void CopyXGmToL1(const RunInfo &info, LocalTensor<X_T> xL1Tensor, uint32_t hIdx, uint32_t kBase);
__aicore__ inline void CopyWeightGmToL1(LocalTensor<X_T> wL1Tensor,
uint32_t hIdx, uint32_t kBase, uint32_t coffId);
__aicore__ inline void LoadAToL0(const RunInfo &info, LocalTensor<X_T> aL0Tensor, LocalTensor<X_T> xL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t mStart, uint32_t mDealSize);
__aicore__ inline void LoadBToL0(const RunInfo &info, LocalTensor<X_T> bL0Tensor, LocalTensor<X_T> wL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t nStart, uint32_t nDealSize);
__aicore__ inline void MatrixMmad(LocalTensor<T> cL0Tensor, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> bL0Tensor, uint32_t mActSize, uint32_t nDealSize, uint32_t kActSize, bool isInitL0C);
__aicore__ inline void CopyOutMm1Res(const RunInfo &info, LocalTensor<T> cL0Tensor,
uint32_t coffId, uint32_t mStart, uint32_t mDealSize, uint32_t nStart, uint32_t nDealSize);
ConstInfo constInfo_ = {};
CompressorTools<COMP> tools_;
// GM
GlobalTensor<X_T> xGm_;
GlobalTensor<X_T> wkvGm_;
GlobalTensor<X_T> wgateGm_;
GlobalTensor<MM1_OUT_T>kvMm1ResGm;
GlobalTensor<MM1_OUT_T>scoreMm1ResGm;
GlobalTensor<int32_t> cuSeqlensGm_;
GlobalTensor<int32_t> sequsedGm_;
GlobalTensor<int32_t> startPosGm_;
bool isExistSeqUsed = false;
// =================================L1 Buffer=================================
static constexpr uint32_t L1_X_SIZE = 128 * 1024;
static constexpr uint32_t L1_W_SIZE = 128 * 1024;
// L1 Buffer
TBuf<TPosition::A1> xBufL1;
TBuf<TPosition::A1> wBufL1;
// =================================L0 Buffer=================================
// L0 buffer size
static constexpr uint32_t L0A_PP_SIZE = 32 * 1024; // 128 * 128 * 2 = 32k
static constexpr uint32_t L0B_PP_SIZE = 32 * 1024; // 128 * 128 * 2 = 32k
static constexpr uint32_t L0C_PP_SIZE = 64 * 1024; // (128 * 2) * 64 * 4 = 64k
// L0_A
TBuf<TPosition::A2> tmpBufL0A;
// L0_B
TBuf<TPosition::B2> tmpBufL0B;
// L0_C
TBuf<TPosition::CO1> tmpBufL0C;
// =================================Event&Buffer ID===========================
// mte2 <> mte1 EventID
static constexpr uint32_t X_EVENT0 = EVENT_ID0;
static constexpr uint32_t X_EVENT1 = EVENT_ID1;
uint32_t xBufId = 0; // 用于DB计数
static constexpr uint32_t W_EVENT0 = EVENT_ID4;
static constexpr uint32_t W_EVENT1 = EVENT_ID5;
static constexpr uint32_t W_EVENT2 = EVENT_ID6;
static constexpr uint32_t W_EVENT3 = EVENT_ID7;
uint32_t wBufId = 0; // 用于DB计数
// mte1 <> mmad EventID
static constexpr uint32_t L0AB_EVENT0 = EVENT_ID3;
static constexpr uint32_t L0AB_EVENT1 = EVENT_ID4;
uint32_t l0abBufId = 0;
// mmad <> fixpipe EventID
static constexpr uint32_t L0C_EVENT0 = EVENT_ID0; // 每块L0C单独分配EVENT_ID
static constexpr uint32_t L0C_EVENT1 = EVENT_ID1;
static constexpr uint32_t L0C_EVENT2 = EVENT_ID2;
static constexpr uint32_t L0C_EVENT3 = EVENT_ID3;
uint32_t l0cBufId = 0;
// =================================Loop======================================
uint32_t curBIdx_ = 0;
uint32_t curSIdx_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::InitParams(const ConstInfo &constInfo, const CompressorTools<COMP> &tools)
{
this->constInfo_ = constInfo;
this->tools_ = tools;
}
template <typename COMP> __aicore__ inline void CompressorBlockCube<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut)
{
xGm_.SetGlobalBuffer((__gm__ X_T *)x);
wkvGm_.SetGlobalBuffer((__gm__ X_T *)wKv);
wgateGm_.SetGlobalBuffer((__gm__ X_T *)wGate);
startPosGm_.SetGlobalBuffer((__gm__ int32_t *)startPos);
isExistSeqUsed = (seqUsed != nullptr);
if (isExistSeqUsed) {
sequsedGm_.SetGlobalBuffer((__gm__ int32_t *)seqUsed);
}
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
cuSeqlensGm_.SetGlobalBuffer((__gm__ int32_t *)cuSeqlens);
}
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::InitBuffers(TPipe *pipe)
{
// L1
// 1. coff=1时, mBase=256, kL1=256, X单次拷贝到L1的数据量最大为mBase*kL1*sizeof(BF16/FP16)=256*256*2=128K
// 2. coff=2时, mBase=128, kL1=256, r最大为128, X单次拷贝到L1的最大数据量为(128+r)*kL1*sizeof(BF16/FP16)<=128K
pipe->InitBuffer(xBufL1, L1_X_SIZE * 2);
// dBaseSize<=64, wkv和wgate各一份, kL1=256, 右矩阵为dBaseSize*2*sizeof(BF16/FP16)<=64K
// cur和pre循环使用, 2份buffer就足够
pipe->InitBuffer(wBufL1, L1_W_SIZE * 2);
// L0
pipe->InitBuffer(tmpBufL0A, L0A_PP_SIZE * 2);
pipe->InitBuffer(tmpBufL0B, L0B_PP_SIZE * 2);
pipe->InitBuffer(tmpBufL0C, L0C_PP_SIZE * 4);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::InitGlobalBuffers(const GlobalTensor<MM1_OUT_T>& kvMm1ResGm, const GlobalTensor<MM1_OUT_T>& scoreMm1ResGm)
{
this->kvMm1ResGm = kvMm1ResGm;
this->scoreMm1ResGm = scoreMm1ResGm;
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::AllocEventID(TPipe *pipe)
{
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT0);
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT1);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT0);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT1);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT2);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT3);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT0);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT1);
SetFlag<HardEvent::FIX_M>(L0C_EVENT0);
SetFlag<HardEvent::FIX_M>(L0C_EVENT1);
SetFlag<HardEvent::FIX_M>(L0C_EVENT2);
SetFlag<HardEvent::FIX_M>(L0C_EVENT3);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::FreeEventID(TPipe *pipe)
{
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT0);
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT1);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT0);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT1);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT2);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT3);
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT0);
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT1);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT0);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT1);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT2);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT3);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::CopyXGmToL1(const RunInfo &info, LocalTensor<X_T> xL1Tensor,
uint32_t hIdx, uint32_t kBase)
{
uint32_t tStart = tools_.GetTIdxByBatch(info.bStart) + info.sStart; // 此基本块在整个序列中的位置
uint32_t copySeqCnt = info.dealSeqCnt; // 此基本块处理的长度
uint32_t xL1Offset = 0 * (32 / sizeof(X_T));
uint64_t sIdx = tStart; // 起始s在整个T的起始点
uint64_t gmOffset = sIdx * constInfo_.hSize + hIdx;
uint32_t nValue = copySeqCnt;
uint32_t dValue = kBase; // 拷贝的列数kBase
uint32_t srcDValue = constInfo_.hSize;
uint32_t dstNzC0Stride = (copySeqCnt + 15) / 16 * 16; // 1行变2行的行方向的偏移需要16对齐
CopySingleMatrixNDToNZ(xL1Tensor[xL1Offset], xGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::CopyWeightGmToL1(LocalTensor<X_T> wL1Tensor,
uint32_t hIdx, uint32_t kBase, uint32_t coffId)
{
// coffId=0, 搬运左矩阵的数据; coffId=1, 搬运右矩阵的数据
uint64_t gmOffset = coffId * constInfo_.headDim * constInfo_.hSize + constInfo_.dIdx * constInfo_.dBaseSize * constInfo_.hSize + hIdx;
uint32_t wkvL1Offset = 0;
uint32_t wgateL1Offset = constInfo_.dBaseSize * (32 / sizeof(X_T)); // wgate与wkv的起始点相隔dBaseSize个32B
uint32_t nValue = constInfo_.dBaseSize;
uint32_t dValue = kBase;
uint32_t srcDValue = constInfo_.hSize;
uint32_t dstNzC0Stride = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
CopySingleMatrixNDToNZ(wL1Tensor[wkvL1Offset], wkvGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
CopySingleMatrixNDToNZ(wL1Tensor[wgateL1Offset], wgateGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::LoadAToL0(const RunInfo &info, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> xL1Tensor, uint32_t kStart, uint32_t kBase, uint32_t mStart, uint32_t mDealSize)
{
uint32_t mSize = info.dealSeqCnt;
uint32_t mSizeAlign = Align(mSize, 16U);
uint32_t xTensorOffset = kStart * mSizeAlign + mStart * (32 / sizeof(X_T));
uint32_t mDealSizeAlign = Align(mDealSize, 16U);
LoadData2DParamsV2 loadData2DParamsV2;
loadData2DParamsV2.mStartPosition = 0;
loadData2DParamsV2.kStartPosition = 0;
loadData2DParamsV2.mStep = mDealSizeAlign / 16;
loadData2DParamsV2.kStep = kBase / (32 / sizeof(X_T));
loadData2DParamsV2.srcStride = mSizeAlign / 16;
loadData2DParamsV2.dstStride = loadData2DParamsV2.mStep;
loadData2DParamsV2.ifTranspose = false;
LoadData(aL0Tensor, xL1Tensor[xTensorOffset], loadData2DParamsV2);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::LoadBToL0(const RunInfo &info, LocalTensor<X_T> bL0Tensor, LocalTensor<X_T> wL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t nStart, uint32_t nDealSize)
{
uint32_t nSize = 2 * constInfo_.dBaseSize;
uint32_t nSizeAlign = Align(nSize, 16U);
uint64_t wTensorOffset = nSizeAlign * kStart + nStart * (32 / sizeof(X_T));
uint32_t nDealSizeAlign = Align(nDealSize, 16U);
LoadData2DParamsV2 loadData2DParamsV2;
loadData2DParamsV2.mStartPosition = 0;
loadData2DParamsV2.kStartPosition = 0;
loadData2DParamsV2.mStep = nDealSizeAlign / 16;
loadData2DParamsV2.kStep = kBase / (32 / sizeof(X_T));
loadData2DParamsV2.srcStride = nSizeAlign / 16;
loadData2DParamsV2.dstStride = loadData2DParamsV2.mStep;
loadData2DParamsV2.ifTranspose = false;
LoadData(bL0Tensor, wL1Tensor[wTensorOffset], loadData2DParamsV2);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::MatrixMmad(LocalTensor<T> cL0Tensor, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> bL0Tensor, uint32_t mActSize, uint32_t nDealSize, uint32_t kActSize, bool isInitL0C)
{
MmadParams mmadParams;
mmadParams.m = mActSize < 16 ? 16 : mActSize;
mmadParams.n = nDealSize;
mmadParams.k = kActSize;
mmadParams.cmatrixInitVal = isInitL0C;
mmadParams.cmatrixSource = false;
Mmad(cL0Tensor, aL0Tensor, bL0Tensor, mmadParams);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::CopyOutMm1Res(const RunInfo &info, LocalTensor<T> cL0Tensor,
uint32_t coffId, uint32_t mStart, uint32_t mDealSize, uint32_t nStart, uint32_t nDealSize)
{
// coffId=0, 存左矩阵的数据; coffId=1, 存右矩阵的数据
FixpipeParamsV220 fixParams;
fixParams.mSize = mDealSize;
fixParams.srcStride = (mDealSize + 15) / 16 * 16; // 需要16对齐
fixParams.dstStride = (uint32_t)COMP::coff * constInfo_.headDim;
fixParams.ndNum = 1;
uint64_t dbOffset = info.cubeDbIdx * constInfo_.dbSize;
uint64_t gmOffset = constInfo_.dIdx * constInfo_.dBaseSize + coffId * constInfo_.headDim + mStart * fixParams.dstStride + dbOffset;
uint32_t kvOffset = (mDealSize + 15) / 16 * 16 * nStart;
uint32_t scoreOffset = (mDealSize + 15) / 16 * 16 * ((nStart + constInfo_.dBaseSize) % (2 * constInfo_.dBaseSize));
if (nStart < constInfo_.dBaseSize) {
fixParams.nSize = min(constInfo_.dBaseSize - nStart, nDealSize);
Fixpipe(kvMm1ResGm[gmOffset], cL0Tensor[kvOffset], fixParams);
}
if (nStart + nDealSize > constInfo_.dBaseSize) {
fixParams.nSize = min(nStart + nDealSize - constInfo_.dBaseSize, nDealSize);
Fixpipe(scoreMm1ResGm[gmOffset], cL0Tensor[scoreOffset], fixParams);
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorBlockCube<COMP>::GetMSize(const RunInfo &info, uint32_t coffId)
{
return info.dealSeqCnt;
}
template <typename COMP>
__aicore__ inline void CompressorBlockCube<COMP>::ComputeMm1(const RunInfo &info)
{
static constexpr uint32_t K_L1_BASE = 256;
static constexpr uint32_t M_L0_BASE = 128;
static constexpr uint32_t K_L0_BASE = 128;
static constexpr uint32_t N_L0_BASE = 128;
uint32_t nCoff = (uint32_t)COMP::coff;
// hSize为K_SIZE=512的倍数
uint32_t hStart = info.hStart;
uint32_t hSize = info.dealKSize;
uint32_t hIdxStart = (constInfo_.aiCoreIdx % constInfo_.dBasicBlockNum) * K_L1_BASE; // 每组核内的h循环起始不同
uint32_t kSize = K_L1_BASE;
for (uint32_t h = 0; h < hSize; h += K_L1_BASE) {
// h方向错位搬运
uint32_t hIdx = (h + hIdxStart) % (CeilDivT(hSize, K_L1_BASE) * K_L1_BASE);
if (hIdx + K_L1_BASE > hSize) {
kSize = hSize - hIdx;
} else {
kSize = K_L1_BASE;
}
bool isFirst = (h == 0);
bool isLast = (h + K_L1_BASE >= hSize);
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT0 + xBufId);
LocalTensor<X_T> xL1Tensor = xBufL1.GetWithOffset<X_T>(L1_X_SIZE / sizeof(X_T), xBufId * L1_X_SIZE);
CopyXGmToL1(info, xL1Tensor, hStart + hIdx, kSize);
SetFlag<HardEvent::MTE2_MTE1>(X_EVENT0 + xBufId);
WaitFlag<HardEvent::MTE2_MTE1>(X_EVENT0 + xBufId);
for (uint32_t i = nCoff; i > 0; i--) {
// coffId=0, 计算pre数据; coffId=1, 计算cur数据
uint32_t coffId = i - 1;
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT0 + wBufId);
LocalTensor<X_T> wL1Tensor = wBufL1.GetWithOffset<X_T>(L1_W_SIZE / sizeof(X_T), wBufId * L1_W_SIZE);
CopyWeightGmToL1(wL1Tensor, hStart + hIdx, kSize, coffId);
SetFlag<HardEvent::MTE2_MTE1>(W_EVENT0 + wBufId);
WaitFlag<HardEvent::MTE2_MTE1>(W_EVENT0 + wBufId);
uint32_t mSize = GetMSize(info, coffId);
uint32_t actMDealSize = M_L0_BASE;
for (uint32_t mL0 = 0; mL0 < mSize; mL0 += M_L0_BASE) {
if (mL0 + M_L0_BASE > mSize) {
actMDealSize = mSize - mL0;
}
uint32_t nDealSize = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
uint32_t actNDealSize = N_L0_BASE;
for (uint32_t nL0 = 0; nL0 < nDealSize; nL0 += N_L0_BASE) {
if (nL0 + N_L0_BASE > nDealSize) {
actNDealSize = nDealSize - nL0;
}
l0cBufId = (mL0 / M_L0_BASE) * 2 + (nL0 / N_L0_BASE) + coffId;
LocalTensor<T> cL0Tensor =
tmpBufL0C.GetWithOffset<T>((L0C_PP_SIZE / sizeof(T)), l0cBufId * L0C_PP_SIZE);
if (isFirst) {
WaitFlag<HardEvent::FIX_M>(L0C_EVENT0 + l0cBufId);
}
uint32_t actKDealSize = K_L0_BASE;
for (uint32_t kL0 = 0; kL0 < kSize; kL0 += K_L0_BASE) {
if (kL0 + K_L0_BASE > kSize) {
actKDealSize = kSize - kL0;
}
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT0 + l0abBufId);
LocalTensor<X_T> aL0Tensor =
tmpBufL0A.GetWithOffset<X_T>(L0A_PP_SIZE / sizeof(X_T), l0abBufId * L0A_PP_SIZE);
LocalTensor<X_T> bL0Tensor =
tmpBufL0B.GetWithOffset<X_T>(L0B_PP_SIZE / sizeof(X_T), l0abBufId * L0B_PP_SIZE);
LoadAToL0(info, aL0Tensor, xL1Tensor, kL0, actKDealSize, mL0, actMDealSize);
LoadBToL0(info, bL0Tensor, wL1Tensor, kL0, actKDealSize, nL0, actNDealSize);
SetFlag<HardEvent::MTE1_M>(L0AB_EVENT0 + l0abBufId);
WaitFlag<HardEvent::MTE1_M>(L0AB_EVENT0 + l0abBufId);
bool isInitL0C = isFirst && (kL0 == 0);
MatrixMmad(cL0Tensor, aL0Tensor, bL0Tensor, actMDealSize, actNDealSize, actKDealSize,
isInitL0C);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT0 + l0abBufId);
l0abBufId = (l0abBufId + 1) % 2;
}
if (isLast) {
SetFlag<HardEvent::M_FIX>(L0C_EVENT0 + l0cBufId);
WaitFlag<HardEvent::M_FIX>(L0C_EVENT0 + l0cBufId);
CopyOutMm1Res(info, cL0Tensor, coffId, mL0, actMDealSize, nL0, actNDealSize);
SetFlag<HardEvent::FIX_M>(L0C_EVENT0 + l0cBufId);
}
}
}
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT0 + wBufId);
wBufId = (wBufId + 1) % 2;
}
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT0 + xBufId);
xBufId = (xBufId + 1) % 2;
}
}
} // namespace Compressor
#endif // COMPRESSOR_BLOCK_CUBE_H

View File

@@ -0,0 +1,437 @@
/**
 * 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 compressor_block_cube_full_load.h
* \brief
*/
#ifndef COMPRESSOR_BLOCK_CUBE_FULL_LOAD_H
#define COMPRESSOR_BLOCK_CUBE_FULL_LOAD_H
#include "compressor_comm.h"
#include "compressor_tools.h"
using namespace AscendC;
namespace Compressor {
template<typename COMP> class CompressorBlockCubeFullLoad {
using MM1_OUT_T = float;
public:
__aicore__ inline CompressorBlockCubeFullLoad(){};
__aicore__ inline void InitParams(const ConstInfo &constInfo, const CompressorTools<COMP> &tools);
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut);
__aicore__ inline void InitBuffers(TPipe *pipe);
__aicore__ inline void InitGlobalBuffers(const GlobalTensor<MM1_OUT_T>& kvMm1ResGm, const GlobalTensor<MM1_OUT_T>& scoreMm1ResGm);
__aicore__ inline void AllocEventID(TPipe *pipe);
__aicore__ inline void FreeEventID(TPipe *pipe);
__aicore__ inline void ComputeMm1(const RunInfo &info);
private:
using T = float;
using X_T = typename AscendC::Conditional<COMP::xDtype == X_DTYPE::BF16, bfloat16_t, half>::type;
__aicore__ inline uint32_t GetMSize(const RunInfo &info, uint32_t coffId);
__aicore__ inline void CopyXGmToL1(LocalTensor<X_T> xL1Tensor, uint32_t hIdx, uint32_t kBase);
__aicore__ inline void CopyWeightGmToL1(LocalTensor<X_T> wL1Tensor,
uint32_t hIdx, uint32_t kBase, uint32_t coffId);
__aicore__ inline void LoadAToL0(LocalTensor<X_T> aL0Tensor, LocalTensor<X_T> xL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t mStart, uint32_t mDealSize);
__aicore__ inline void LoadBToL0(LocalTensor<X_T> bL0Tensor, LocalTensor<X_T> wL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t nStart, uint32_t nDealSize);
__aicore__ inline void MatrixMmad(LocalTensor<T> cL0Tensor, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> bL0Tensor, uint32_t mActSize, uint32_t nDealSize, uint32_t kActSize, bool isInitL0C);
__aicore__ inline void CopyOutMm1Res(const RunInfo &info, LocalTensor<T> cL0Tensor,
uint32_t coffId, uint32_t mStart, uint32_t mDealSize, uint32_t nStart, uint32_t nDealSize);
ConstInfo constInfo_ = {};
CompressorTools<COMP> tools_;
// GM
GlobalTensor<X_T> xGm_;
GlobalTensor<X_T> wkvGm_;
GlobalTensor<X_T> wgateGm_;
GlobalTensor<MM1_OUT_T>kvMm1ResGm;
GlobalTensor<MM1_OUT_T>scoreMm1ResGm;
GlobalTensor<int32_t> cuSeqlensGm_;
GlobalTensor<int32_t> sequsedGm_;
GlobalTensor<int32_t> startPosGm_;
bool isExistSeqUsed = false;
// =================================L1 Buffer=================================
static constexpr uint32_t L1_X_SIZE = 128 * 1024;
static constexpr uint32_t L1_W_SIZE = 128 * 1024;
// L1 Buffer
TBuf<TPosition::A1> xBufL1;
TBuf<TPosition::A1> wBufL1;
// =================================L0 Buffer=================================
// L0 buffer size
static constexpr uint32_t L0A_PP_SIZE = 32 * 1024; // 128 * 128 * 2 = 32k
static constexpr uint32_t L0B_PP_SIZE = 32 * 1024; // 128 * 128 * 2 = 32k
static constexpr uint32_t L0C_PP_SIZE = 64 * 1024; // (128 * 2) * 64 * 4 = 64k
// L0_A
TBuf<TPosition::A2> tmpBufL0A;
// L0_B
TBuf<TPosition::B2> tmpBufL0B;
// L0_C
TBuf<TPosition::CO1> tmpBufL0C;
// =================================Event&Buffer ID===========================
// mte2 <> mte1 EventID
static constexpr uint32_t X_EVENT0 = EVENT_ID0;
static constexpr uint32_t X_EVENT1 = EVENT_ID1;
uint32_t xBufId = 0; // 用于DB计数
static constexpr uint32_t W_EVENT0 = EVENT_ID4;
static constexpr uint32_t W_EVENT1 = EVENT_ID5;
static constexpr uint32_t W_EVENT2 = EVENT_ID6;
static constexpr uint32_t W_EVENT3 = EVENT_ID7;
uint32_t wBufId = 0; // 用于DB计数
// mte1 <> mmad EventID
static constexpr uint32_t L0AB_EVENT0 = EVENT_ID3;
static constexpr uint32_t L0AB_EVENT1 = EVENT_ID4;
uint32_t l0abBufId = 0;
// mmad <> fixpipe EventID
static constexpr uint32_t L0C_EVENT0 = EVENT_ID0; // 每块L0C单独分配EVENT_ID
static constexpr uint32_t L0C_EVENT1 = EVENT_ID1;
static constexpr uint32_t L0C_EVENT2 = EVENT_ID2;
static constexpr uint32_t L0C_EVENT3 = EVENT_ID3;
uint32_t l0cBufId = 0;
// =================================Loop======================================
uint32_t curBIdx_ = 0;
uint32_t curSIdx_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::InitParams(const ConstInfo &constInfo, const CompressorTools<COMP> &tools)
{
this->constInfo_ = constInfo;
this->tools_ = tools;
}
template <typename COMP> __aicore__ inline void CompressorBlockCubeFullLoad<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut)
{
xGm_.SetGlobalBuffer((__gm__ X_T *)x);
wkvGm_.SetGlobalBuffer((__gm__ X_T *)wKv);
wgateGm_.SetGlobalBuffer((__gm__ X_T *)wGate);
startPosGm_.SetGlobalBuffer((__gm__ int32_t *)startPos);
isExistSeqUsed = (seqUsed != nullptr);
if (isExistSeqUsed) {
sequsedGm_.SetGlobalBuffer((__gm__ int32_t *)seqUsed);
}
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
cuSeqlensGm_.SetGlobalBuffer((__gm__ int32_t *)cuSeqlens);
}
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::InitBuffers(TPipe *pipe)
{
// L1
// 1. coff=1时, mBase=256, kL1=256, X单次拷贝到L1的数据量最大为mBase*kL1*sizeof(BF16/FP16)=256*256*2=128K
// 2. coff=2时, mBase=128, kL1=256, r最大为128, X单次拷贝到L1的最大数据量为(128+r)*kL1*sizeof(BF16/FP16)<=128K
pipe->InitBuffer(xBufL1, L1_X_SIZE * 2);
// dBaseSize<=64, wkv和wgate各一份, kL1=256, 右矩阵为dBaseSize*2*sizeof(BF16/FP16)<=64K
// cur和pre循环使用, 2份buffer就足够
pipe->InitBuffer(wBufL1, L1_W_SIZE * 2);
// L0
pipe->InitBuffer(tmpBufL0A, L0A_PP_SIZE * 2);
pipe->InitBuffer(tmpBufL0B, L0B_PP_SIZE * 2);
pipe->InitBuffer(tmpBufL0C, L0C_PP_SIZE * 4);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::InitGlobalBuffers(const GlobalTensor<MM1_OUT_T>& kvMm1ResGm, const GlobalTensor<MM1_OUT_T>& scoreMm1ResGm)
{
this->kvMm1ResGm = kvMm1ResGm;
this->scoreMm1ResGm = scoreMm1ResGm;
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::AllocEventID(TPipe *pipe)
{
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT0);
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT1);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT0);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT1);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT2);
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT3);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT0);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT1);
SetFlag<HardEvent::FIX_M>(L0C_EVENT0);
SetFlag<HardEvent::FIX_M>(L0C_EVENT1);
SetFlag<HardEvent::FIX_M>(L0C_EVENT2);
SetFlag<HardEvent::FIX_M>(L0C_EVENT3);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::FreeEventID(TPipe *pipe)
{
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT0);
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT1);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT0);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT1);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT2);
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT3);
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT0);
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT1);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT0);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT1);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT2);
WaitFlag<HardEvent::FIX_M>(L0C_EVENT3);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::CopyXGmToL1(LocalTensor<X_T> xL1Tensor,
uint32_t hIdx, uint32_t kBase)
{
uint32_t copySeqCnt = constInfo_.mEnd - constInfo_.mStart; //info.dealSeqCnt; // 此基本块处理的长度
uint32_t xL1Offset = 0 * (32 / sizeof(X_T));
uint64_t sIdx = constInfo_.mStart; // 起始s在整个T的起始点
uint64_t gmOffset = sIdx * constInfo_.hSize + hIdx;
uint32_t nValue = copySeqCnt;
uint32_t dValue = kBase; // 拷贝的列数kBase
uint32_t srcDValue = constInfo_.hSize;
uint32_t dstNzC0Stride = (copySeqCnt + 15) / 16 * 16; // 1行变2行的行方向的偏移需要16对齐
CopySingleMatrixNDToNZ(xL1Tensor[xL1Offset], xGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::CopyWeightGmToL1(LocalTensor<X_T> wL1Tensor,
uint32_t hIdx, uint32_t kBase, uint32_t coffId)
{
uint64_t gmOffset = coffId * constInfo_.headDim * constInfo_.hSize + constInfo_.nStart * constInfo_.hSize + hIdx;
uint32_t wkvL1Offset = 0;
uint32_t wgateL1Offset = constInfo_.dBaseSize * (32 / sizeof(X_T)); // wgate与wkv的起始点相隔dBaseSize个32B
uint32_t nValue = constInfo_.dBaseSize;
uint32_t dValue = kBase;
uint32_t srcDValue = constInfo_.hSize;
uint32_t dstNzC0Stride = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
CopySingleMatrixNDToNZ(wL1Tensor[wkvL1Offset], wkvGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
CopySingleMatrixNDToNZ(wL1Tensor[wgateL1Offset], wgateGm_[gmOffset], nValue, dValue, srcDValue, dstNzC0Stride);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::LoadAToL0(LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> xL1Tensor, uint32_t kStart, uint32_t kBase, uint32_t mStart, uint32_t mDealSize)
{
uint32_t mSize = constInfo_.mEnd - constInfo_.mStart;
uint32_t mSizeAlign = Align(mSize, 16U);
uint32_t xTensorOffset = kStart * mSizeAlign + mStart * (32 / sizeof(X_T));
uint32_t mDealSizeAlign = Align(mDealSize, 16U);
LoadData2DParamsV2 loadData2DParamsV2;
loadData2DParamsV2.mStartPosition = 0;
loadData2DParamsV2.kStartPosition = 0;
loadData2DParamsV2.mStep = mDealSizeAlign / 16;
loadData2DParamsV2.kStep = kBase / (32 / sizeof(X_T));
loadData2DParamsV2.srcStride = mSizeAlign / 16;
loadData2DParamsV2.dstStride = loadData2DParamsV2.mStep;
loadData2DParamsV2.ifTranspose = false;
LoadData(aL0Tensor, xL1Tensor[xTensorOffset], loadData2DParamsV2);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::LoadBToL0(LocalTensor<X_T> bL0Tensor, LocalTensor<X_T> wL1Tensor,
uint32_t kStart, uint32_t kBase, uint32_t nStart, uint32_t nDealSize)
{
uint32_t nSize = 2 * constInfo_.dBaseSize;
uint32_t nSizeAlign = Align(nSize, 16U);
uint64_t wTensorOffset = nSizeAlign * kStart + nStart * (32 / sizeof(X_T));
uint32_t nDealSizeAlign = Align(nDealSize, 16U);
LoadData2DParamsV2 loadData2DParamsV2;
loadData2DParamsV2.mStartPosition = 0;
loadData2DParamsV2.kStartPosition = 0;
loadData2DParamsV2.mStep = nDealSizeAlign / 16;
loadData2DParamsV2.kStep = kBase / (32 / sizeof(X_T));
loadData2DParamsV2.srcStride = nSizeAlign / 16;
loadData2DParamsV2.dstStride = loadData2DParamsV2.mStep;
loadData2DParamsV2.ifTranspose = false;
LoadData(bL0Tensor, wL1Tensor[wTensorOffset], loadData2DParamsV2);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::MatrixMmad(LocalTensor<T> cL0Tensor, LocalTensor<X_T> aL0Tensor,
LocalTensor<X_T> bL0Tensor, uint32_t mActSize, uint32_t nDealSize, uint32_t kActSize, bool isInitL0C)
{
MmadParams mmadParams;
mmadParams.m = mActSize < 16 ? 16 : mActSize;
mmadParams.n = nDealSize;
mmadParams.k = kActSize;
mmadParams.cmatrixInitVal = isInitL0C;
mmadParams.cmatrixSource = false;
Mmad(cL0Tensor, aL0Tensor, bL0Tensor, mmadParams);
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::CopyOutMm1Res(const RunInfo &info, LocalTensor<T> cL0Tensor,
uint32_t coffId, uint32_t mStart, uint32_t mDealSize, uint32_t nStart, uint32_t nDealSize)
{
// coffId=0, 存左矩阵的数据; coffId=1, 存右矩阵的数据
FixpipeParamsV220 fixParams;
fixParams.mSize = mDealSize;
fixParams.srcStride = (mDealSize + 15) / 16 * 16; // 需要16对齐
fixParams.dstStride = (uint32_t)COMP::coff * constInfo_.headDim;
fixParams.ndNum = 1;
uint64_t dbOffset = info.cubeDbIdx * constInfo_.dbSize;
uint64_t gmOffset = constInfo_.nStart + coffId * constInfo_.headDim + mStart * fixParams.dstStride + dbOffset;
uint32_t kvOffset = (mDealSize + 15) / 16 * 16 * nStart;
uint32_t scoreOffset = (mDealSize + 15) / 16 * 16 * ((nStart + constInfo_.dBaseSize) % (2 * constInfo_.dBaseSize));
if (nStart < constInfo_.dBaseSize) {
fixParams.nSize = min(constInfo_.dBaseSize - nStart, nDealSize);
Fixpipe(kvMm1ResGm[gmOffset], cL0Tensor[kvOffset], fixParams);
}
if (nStart + nDealSize > constInfo_.dBaseSize) {
fixParams.nSize = min(nStart + nDealSize - constInfo_.dBaseSize, nDealSize);
Fixpipe(scoreMm1ResGm[gmOffset], cL0Tensor[scoreOffset], fixParams);
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorBlockCubeFullLoad<COMP>::GetMSize(const RunInfo &info, uint32_t coffId)
{
return info.dealSeqCnt;
}
template <typename COMP>
__aicore__ inline void CompressorBlockCubeFullLoad<COMP>::ComputeMm1(const RunInfo &info)
{
uint32_t mSize = info.dealSeqCnt;
if (mSize == 0) {
return;
}
static constexpr uint32_t K_L1_BASE = 128;
static constexpr uint32_t M_L0_BASE = 128;
static constexpr uint32_t K_L0_BASE = 128;
static constexpr uint32_t N_L0_BASE = 128;
uint32_t nCoff = (uint32_t)COMP::coff;
// hSize为K_SIZE=512的倍数
uint32_t hStart = constInfo_.kStart;
uint32_t hSize = constInfo_.kEnd - constInfo_.kStart;
uint32_t hIdxStart = (constInfo_.aiCoreIdx % constInfo_.dBasicBlockNum) * K_L1_BASE; // 每组核内的h循环起始不同
uint32_t kSize = K_L1_BASE;
for (uint32_t h = 0; h < hSize; h += K_L1_BASE) {
// h方向错位搬运
uint32_t hIdx = (h + hIdxStart) % (CeilDivT(hSize, K_L1_BASE) * K_L1_BASE);
if (hIdx + K_L1_BASE > hSize) {
kSize = hSize - hIdx;
} else {
kSize = K_L1_BASE;
}
bool isFirst = (h == 0);
bool isLast = (h + K_L1_BASE >= hSize);
WaitFlag<HardEvent::MTE1_MTE2>(X_EVENT0 + xBufId);
LocalTensor<X_T> xL1Tensor = xBufL1.GetWithOffset<X_T>(L1_X_SIZE / sizeof(X_T), xBufId * L1_X_SIZE);
CopyXGmToL1(xL1Tensor, hStart + hIdx, kSize);
SetFlag<HardEvent::MTE2_MTE1>(X_EVENT0 + xBufId);
WaitFlag<HardEvent::MTE2_MTE1>(X_EVENT0 + xBufId);
for (uint32_t i = nCoff; i > 0; i--) {
// coffId=0, 计算pre数据; coffId=1, 计算cur数据
uint32_t coffId = i - 1;
WaitFlag<HardEvent::MTE1_MTE2>(W_EVENT0 + wBufId);
LocalTensor<X_T> wL1Tensor = wBufL1.GetWithOffset<X_T>(L1_W_SIZE / sizeof(X_T), wBufId * L1_W_SIZE);
CopyWeightGmToL1(wL1Tensor, hStart + hIdx, kSize, coffId);
SetFlag<HardEvent::MTE2_MTE1>(W_EVENT0 + wBufId);
WaitFlag<HardEvent::MTE2_MTE1>(W_EVENT0 + wBufId);
uint32_t actMDealSize = M_L0_BASE;
for (uint32_t mL0 = 0; mL0 < mSize; mL0 += M_L0_BASE) {
if (mL0 + M_L0_BASE > mSize) {
actMDealSize = mSize - mL0;
}
uint32_t nDealSize = 2 * constInfo_.dBaseSize; // 2: wkv和wgate各搬运dBaseSize行, dBaseSize需保证8的倍数
uint32_t actNDealSize = N_L0_BASE;
for (uint32_t nL0 = 0; nL0 < nDealSize; nL0 += N_L0_BASE) {
if (nL0 + N_L0_BASE > nDealSize) {
actNDealSize = nDealSize - nL0;
}
l0cBufId = (mL0 / M_L0_BASE) * 2 + (nL0 / N_L0_BASE) + coffId;
LocalTensor<T> cL0Tensor = tmpBufL0C.GetWithOffset<T>((L0C_PP_SIZE / sizeof(T)), l0cBufId * L0C_PP_SIZE);
if (isFirst) {
WaitFlag<HardEvent::FIX_M>(L0C_EVENT0 + l0cBufId);
}
uint32_t actKDealSize = K_L0_BASE;
for (uint32_t kL0 = 0; kL0 < kSize; kL0 += K_L0_BASE) {
if (kL0 + K_L0_BASE > kSize) {
actKDealSize = kSize - kL0;
}
WaitFlag<HardEvent::M_MTE1>(L0AB_EVENT0 + l0abBufId);
LocalTensor<X_T> aL0Tensor = tmpBufL0A.GetWithOffset<X_T>(L0A_PP_SIZE / sizeof(X_T), l0abBufId * L0A_PP_SIZE);
LocalTensor<X_T> bL0Tensor = tmpBufL0B.GetWithOffset<X_T>(L0B_PP_SIZE / sizeof(X_T), l0abBufId * L0B_PP_SIZE);
LoadAToL0(aL0Tensor, xL1Tensor, kL0, actKDealSize, mL0, actMDealSize);
LoadBToL0(bL0Tensor, wL1Tensor, kL0, actKDealSize, nL0, actNDealSize);
SetFlag<HardEvent::MTE1_M>(L0AB_EVENT0 + l0abBufId);
WaitFlag<HardEvent::MTE1_M>(L0AB_EVENT0 + l0abBufId);
bool isInitL0C = isFirst && (kL0 == 0);
MatrixMmad(cL0Tensor, aL0Tensor, bL0Tensor, actMDealSize, actNDealSize, actKDealSize, isInitL0C);
SetFlag<HardEvent::M_MTE1>(L0AB_EVENT0 + l0abBufId);
l0abBufId = (l0abBufId + 1) % 2;
}
if (isLast) {
SetFlag<HardEvent::M_FIX>(L0C_EVENT0 + l0cBufId);
WaitFlag<HardEvent::M_FIX>(L0C_EVENT0 + l0cBufId);
CopyOutMm1Res(info, cL0Tensor, coffId, mL0, actMDealSize, nL0, actNDealSize);
SetFlag<HardEvent::FIX_M>(L0C_EVENT0 + l0cBufId);
}
}
}
SetFlag<HardEvent::MTE1_MTE2>(W_EVENT0 + wBufId);
wBufId = (wBufId + 1) % 2;
}
SetFlag<HardEvent::MTE1_MTE2>(X_EVENT0 + xBufId);
xBufId = (xBufId + 1) % 2;
}
}
} // namespace Compressor
#endif // COMPRESSOR_BLOCK_CUBE_FULL_LOAD_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,445 @@
/**
 * 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 compressor_comm.h
* \brief
*/
#ifndef COMPRESSOR_COMM_H
#define COMPRESSOR_COMM_H
#include "kernel_operator.h"
#include "kernel_operator_list_tensor_intf.h"
#include "kernel_tiling/kernel_tiling.h"
#include "lib/matmul_intf.h"
#include "lib/matrix/matmul/tiling.h"
using namespace AscendC;
namespace Compressor {
template <typename T>
__aicore__ inline T CeilDivT(T num1, T num2)
{
if (num2 == 0) {
return static_cast<T>(0);
}
return (num1 + num2 - 1) / num2;
}
template <typename T>
__aicore__ inline T Align(T num, T rnd)
{
return (((rnd) == 0) ? 0 : (((num) + (rnd)-1) / (rnd) * (rnd)));
}
template <typename T>
__aicore__ inline T Trunc(T num, T rnd)
{
return ((rnd) == 0) ? 0 : (((num) / (rnd) * (rnd)));
}
template <typename T>
__aicore__ inline T FloorPow2(T num)
{
if (num == 0)
return 1;
for (uint32_t i = 1; i < sizeof(T) * 8; i <<= 1) {
num |= (num >> i);
}
return num - (num >> 1);
}
template <typename T>
__aicore__ inline T CeilPow2(T num)
{
if (num <= 1)
return 1;
num--;
for (uint32_t i = 1; i < sizeof(T) * 8; i <<= 1) {
num |= (num >> i);
}
num++;
return num;
}
enum class X_LAYOUT : std::uint8_t {
BSH = static_cast<std::uint8_t>(0),
TH = static_cast<std::uint8_t>(1)
};
enum class X_DTYPE : std::uint8_t {
BF16 = static_cast<std::uint8_t>(0),
FP16 = static_cast<std::uint8_t>(1)
};
enum class COFF : std::uint8_t {
DISABLE = static_cast<std::uint8_t>(1),
OVERLAP = static_cast<std::uint8_t>(2)
};
enum class ROTARY_MODE : std::uint8_t {
HALF = static_cast<std::uint8_t>(1),
INTERLEAVE = static_cast<std::uint8_t>(2)
};
enum class CACHE_MODE : std::uint8_t {
CONTINUOUS = static_cast<std::uint8_t>(1),
CYCLE = static_cast<std::uint8_t>(2)
};
enum class TEMPLATE_ID : uint8_t {
NORMAL = 0,
EMPTY_X = 1,
FULL_LOAD = 2
};
template <X_LAYOUT X_L, X_DTYPE X_T, COFF C, ROTARY_MODE Rotary_Mode, CACHE_MODE Cache_Mode, typename... Args>
struct COMPType {
static constexpr X_LAYOUT xLayout = X_L;
static constexpr X_DTYPE xDtype = X_T;
static constexpr COFF coff = C;
static constexpr ROTARY_MODE rotaryMode = Rotary_Mode;
static constexpr CACHE_MODE cacheMode = Cache_Mode;
};
struct CmpBlockInfo {
__aicore__ inline CmpBlockInfo(){};
__aicore__ inline CmpBlockInfo(uint32_t bIdx, uint32_t sIdx, bool needReset = false)
: bIdx(bIdx), sIdx(sIdx), needReset(needReset){};
uint32_t bIdx = 0U;
uint32_t sIdx = 0U;
uint32_t bSeqUsed = 0U;
uint32_t bStartPos = 0U;
bool needReset = false;
bool isFirst = true;
uint32_t headSeqCnt = 0U;
uint32_t validSeqCnt = 0U;
uint32_t tailSeqCnt = 0U;
bool isCompress = 0U;
};
struct BasicBlockInfo {
uint32_t bIdx = 0;
uint32_t sIdx = 0;
uint32_t compressedTcNum = 0;
uint32_t dealSeqCnt = 0;
uint32_t dealTcNum = 0;
};
struct BatchInfo {
uint32_t tcNum = 0;
uint32_t compressedTcNum = 0;
uint32_t remSeqCnt = 0;
uint32_t seqCnt = 0;
uint32_t seqUsedCnt = 0;
uint32_t headHolderSeq = 0;
uint32_t bStartPos = 0;
uint32_t bIdx = 0;
uint32_t sIdx = 0;
};
struct ConstInfo {
// 整个AICORE的任务信息, 左闭右开区间[ (bStart, s2Start), (bEnd, s2End) )
uint32_t bStart = 0U;
uint32_t sStart = 0U;
uint32_t bEnd = 0U;
uint32_t sEnd = 0U;
// 分核相关
uint32_t usedCoreNum = 0;
uint32_t dBaseSize = 0;
uint32_t mBaseSize = 0;
uint32_t kBaseSize = 0;
uint32_t kBaseNum = 0;
uint32_t tcSize = 0;
uint32_t tcBaseSize = 0;
uint32_t tcBasicBlockNum = 0;
uint32_t dBasicBlockNum = 0;
uint32_t coreGroupNum = 0;
uint32_t singleCoreDealTcBasicNum = 0;
uint32_t dIdx = 0;
uint32_t mStart = 0;
uint32_t mEnd = 0;
uint32_t nStart = 0;
uint32_t nEnd = 0;
uint32_t kStart = 0;
uint32_t kEnd = 0;
uint32_t mLoopNum = 0;
uint32_t bIdxOfLastTc = 0;
uint32_t sIdxOfLastTc = 0;
uint32_t mGroupNum = 0;
uint32_t mCurGroupIdx = 0;
// shape及参数
uint32_t batchSize = 0;
uint32_t hSize = 0;
uint32_t sSize = 0;
uint32_t headDim = 0;
uint32_t ropeHeadDim = 0;
uint32_t cmpRatio = 0;
float normEps = 1e-6;
float reciprocalD = 0;
uint64_t stateCacheStrideDim0 = 0;
uint32_t curGroupIdx = 0;
uint32_t tailGroupIdx = 0;
uint32_t tailBasicBlockNum = 0;
uint32_t realDealBasicBlockNum = 0;
// pageAttention
uint32_t blockNum = 0;
uint32_t blockSize = 0;
uint32_t maxBlockNumPerBatch = 0;
// workSpace
uint32_t dbWorkspaceRatio = 1;
uint32_t mm1KvResSize = 0;
uint32_t mm1ScoreResSize = 0;
uint32_t vec1TailCacheSize = 0;
uint32_t vec1ResSize = 0;
uint32_t mm1ResSize = 0; // 所有cube输出kv/score结果的总大小
uint32_t aiCoreIdx = 0;
uint32_t nSize = 0;
uint32_t dbSize = 0;
};
struct RunInfo {
bool isValid = false;
uint32_t cubeDbIdx = 0; // kernel主循环索引
// 增加字段
uint32_t dealTcNum = 0;
// 右边相关信息
uint32_t bStart = 0;
uint32_t sStart = 0;
uint32_t dealSeqCnt = 0;
// 左边相关信息
uint32_t preBStart = 0;
uint32_t preSStart = 0;
uint32_t preDealSeqCnt = 0; // 左边需要处理的s大小
uint32_t preFirstSeqCnt = 0; // 左边首块大小
uint32_t kStartIdx = 0;
uint32_t dealKSize = 0;
uint32_t hStart = 0;
uint32_t bEnd = 0;
uint32_t sEnd = 0;
uint32_t bStartSeqIdx = 0;
uint32_t bEndSeqIdx = 0;
// v2分核信息 sc是左闭右开
uint32_t scStart = 0;
uint32_t scEnd = 0;
uint32_t dealScSize = 0;
// vec1Res offset
uint64_t vec1ResOffset = 0;
};
struct Vec1RunInfo {
// vec相关信息一次syncAll需处理数据的起始索引
bool resetResFlag = false; // v1积攒N轮 是否是N轮的起始轮
uint32_t c1v1DbIdx = 0; // vec1 doubleBuffer索引
uint32_t v1v2DbIdx = 0; // v1v2 doubleBuffer索引
uint32_t bStart = 0;
uint32_t sStart = 0;
uint32_t dealTcNum = 0;
uint32_t dealScSize = 0;
};
struct Vec2RunInfo {
// uint32_t bStart = 0;
uint32_t v2DbIdx = 0; // v2 doubleBuffer索引
uint32_t sStart = 0;
uint32_t bEnd = 0;
uint32_t sEnd = 0;
// v2分核信息 sc是左闭右开
uint32_t scStart = 0;
uint32_t scEnd = 0;
// 增加字段
uint32_t bStart = 0;
uint32_t compressedId = 0;
uint32_t bCompressedId = 0;
uint32_t dealScSize = 0;
};
struct MSplitInfo {
uint32_t vecStartB = 0U;
uint32_t vecStartS = 0U;
uint32_t vecEndB = 0U;
uint32_t vecEndS = 0U;
uint32_t dealTcNum = 0U;
// vec1Res offset
uint64_t vec1StartOffset = 0;
uint64_t vec1ResOffset = 0;
};
struct BlockInfo {
__aicore__ inline BlockInfo(uint32_t bIdx, uint32_t sIdx, uint32_t dealSeqSize)
: bIdx(bIdx), sIdx(sIdx), dealSeqSize(dealSeqSize){};
uint32_t bIdx = 0U;
uint32_t sIdx = 0U;
uint32_t dealSeqSize = 0;
uint32_t isFirst = true;
uint32_t bSeqUsed = 0U;
uint32_t bStartPos = 0U;
uint32_t headHolderSeqCnt = 0U;
uint32_t validSeqCnt = 0U;
uint32_t tailHolderSeqCnt = 0U;
uint32_t dealTcSize = 0U;
uint32_t tailValidSeqCnt = 0U;
uint32_t compressTcSize = 0U;
};
struct LoopInfo {
uint32_t groupSize = 0U;
uint32_t groupNum = 0U;
uint32_t coreRowIdx = 0U;
uint32_t coreColIdx = 0U;
uint32_t dLoopIdx = 0U;
bool isCoreRowFirst = false;
bool isCoreRowLast = false;
bool isCoreLoopFirst = false;
bool isCoreLoopLast = false;
};
struct Vec1SplitInfo {
uint32_t dealSeqStartIdx = 0;
uint32_t dealSeqCnt = 0;
uint32_t dBaseSize = 0;
uint32_t vec1GroupSize = 0;
uint32_t vec1GroupNum = 0;
uint32_t dealTcSize = 0;
uint32_t dealTcNum = 0;
uint32_t dealBatchNum = 0;
uint32_t preDealTcSize = 0;
uint32_t preDealBatchNum = 0;
uint32_t curBStart = 0;
uint32_t curSStart = 0;
uint32_t curCompressedCnt = 0;
uint32_t preCompressedCnt = 0;
uint32_t totalCompressedCnt = 0;
uint32_t tcSplitSize = 0;
uint32_t dSplitSize = 0;
uint32_t dLoopCount = 0;
};
struct Vec2SplitInfo {
uint32_t dealedScCnt = 0;
uint32_t preScCnt = 0;
uint32_t dealScNum = 0;
uint32_t curBStart = 0;
uint32_t curScStart = 0;
};
// BUFFER的字节数
inline constexpr uint32_t BUFFER_SIZE_BYTE_32B = 32;
inline constexpr uint32_t BUFFER_SIZE_BYTE_64B = 64;
inline constexpr uint32_t BUFFER_SIZE_BYTE_256B = 256;
inline constexpr uint32_t BUFFER_SIZE_BYTE_512B = 512;
inline constexpr uint32_t BUFFER_SIZE_BYTE_1K = 1024;
inline constexpr uint32_t BUFFER_SIZE_BYTE_2K = 2048;
inline constexpr uint32_t BUFFER_SIZE_BYTE_4K = 4096;
inline constexpr uint32_t BUFFER_SIZE_BYTE_8K = 8192;
inline constexpr uint32_t BUFFER_SIZE_BYTE_16K = 16384;
inline constexpr uint32_t BUFFER_SIZE_BYTE_32K = 32768;
inline constexpr uint32_t BUFFER_SIZE_BYTE_64K = 65536;
// BLOCK和REPEAT的字节数
inline constexpr uint64_t BYTE_BLOCK = 32UL;
inline constexpr uint32_t REPEAT_BLOCK_BYTE = 256U;
template <typename T>
__aicore__ inline constexpr T BlockElementNum()
{
return BYTE_BLOCK / sizeof(T);
}
template <typename T>
__aicore__ inline constexpr T RepeatElementNum()
{
return REPEAT_BLOCK_BYTE / sizeof(T);
}
// BLOCK和REPEAT的FP32元素数
inline constexpr uint32_t FP32_BLOCK_ELEMENT_NUM = BYTE_BLOCK / sizeof(float); // 8
inline constexpr uint32_t FP16_BLOCK_ELEMENT_NUM = BYTE_BLOCK / sizeof(bfloat16_t); // 16
inline constexpr uint32_t FP32_REPEAT_ELEMENT_NUM = REPEAT_BLOCK_BYTE / sizeof(float); // 64
inline constexpr uint32_t REPEAT_STRIDE_NUM = REPEAT_BLOCK_BYTE / BYTE_BLOCK; // 8
inline constexpr uint32_t REPEAT_MAX_NUM = 255;
inline constexpr uint32_t BRCB_NUM = 8;
inline constexpr uint32_t MAX_R = 256;
template <typename T>
__aicore__ inline void CopySingleMatrixNDToNZ(LocalTensor<T> l1Tensor, const GlobalTensor<T> gmTensor, uint32_t nValue,
uint32_t dValue, uint32_t srcDValue, uint32_t dstNzC0Stride)
{
Nd2NzParams nd2nzPara;
nd2nzPara.ndNum = 1;
nd2nzPara.nValue = nValue; // nd矩阵的行数
if constexpr (IsSameType<T, int4b_t>::value) {
constexpr uint32_t HALF_SIZE_DIVISOR = 2;
nd2nzPara.dValue = dValue / HALF_SIZE_DIVISOR;
nd2nzPara.srcDValue = srcDValue / HALF_SIZE_DIVISOR;
} else {
nd2nzPara.dValue = dValue; // nd矩阵的列数
nd2nzPara.srcDValue = srcDValue; // 同一nd矩阵相邻行起始地址间的偏移
}
nd2nzPara.dstNzC0Stride = dstNzC0Stride;
nd2nzPara.dstNzNStride = 1;
nd2nzPara.srcNdMatrixStride = 0;
nd2nzPara.dstNzMatrixStride = 0;
DataCopy(l1Tensor, gmTensor, nd2nzPara);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(GlobalTensor<T> tensor, uint32_t desc, uint32_t dumpSize, uint32_t row,
uint32_t col)
{
uint32_t array2[] = {static_cast<uint32_t>(row), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(LocalTensor<T> tensor, uint32_t desc, uint32_t dumpSize, uint32_t row,
uint32_t col)
{
uint32_t array2[] = {static_cast<uint32_t>(row), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(LocalTensor<T> tensor, uint32_t desc, uint32_t dumpSize)
{
uint32_t col = 32 / sizeof(T);
uint32_t array2[] = {static_cast<uint32_t>(dumpSize / col), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
template <typename T>
__aicore__ inline void DumpTensorForDim2(GlobalTensor<T> tensor, uint32_t desc, uint32_t dumpSize)
{
uint32_t col = 32 / sizeof(T);
uint32_t array2[] = {static_cast<uint32_t>(dumpSize / col), static_cast<uint32_t>(col)};
AscendC::ShapeInfo shapeInfo(2, array2);
// AscendC::DumpTensor(tensor, desc, dumpSize, shapeInfo);
}
} // namespace Compressor
#endif

View File

@@ -0,0 +1,628 @@
/**
 * 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 compressor_kernel.h
* \brief
*/
#ifndef COMPRESSOR_KERNEL_H
#define COMPRESSOR_KERNEL_H
#include "compressor_comm.h"
#include "compressor_template_tiling_key.h"
#include "compressor_tiling_data.h"
#include "compressor_tools.h"
#include "compressor_block_cube.h"
#include "compressor_block_vec.h"
using namespace AscendC;
namespace Compressor {
template <typename COMP>
class CompressorKernel {
public:
__aicore__ inline CompressorKernel(TPipe* pipe, const optiling::CompressorTilingData* __restrict tilingData)
: pipe_(pipe), tilingData_(tilingData) {}
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace);
__aicore__ inline void Process();
private:
// ================================Init functions==================================
__aicore__ inline void InitWorkspace(__gm__ uint8_t *workspace);
// ================================Process functions================================
__aicore__ inline void InitTilingData();
__aicore__ inline void SplitK();
// 获取基本块数量
__aicore__ inline uint32_t GetLoopTimes();
__aicore__ inline void SkipInvalidBatch(BatchInfo &batchInfo);
__aicore__ inline void UpdateCurGroup(BasicBlockInfo &basicBlockInfo, BatchInfo batchInfo, uint32_t &curGroupQuota, uint32_t curDealSeq);
__aicore__ inline BasicBlockInfo SkipOneLoop(BatchInfo &batchInfo);
// 计算分核基本信息
__aicore__ inline void CalcSplitCoreInfo();
__aicore__ inline void AllocEventID();
__aicore__ inline void FreeEventID();
__aicore__ inline void ComputeMm1(const RunInfo &info, bool isNeedExcute);
__aicore__ inline void ComputeVec1(const Vec1RunInfo &info);
__aicore__ inline void ComputeVec2(const Vec2RunInfo &info);
__aicore__ inline bool IsNeedExcuteC1(RunInfo info);
__aicore__ inline bool IsNeedSyncAll(uint32_t curBasicBlockIdx);
__aicore__ inline void CalcC1V1Params(RunInfo &info, Vec1RunInfo &vec1Info, BatchInfo &batchInfo, uint32_t loopIdx);
__aicore__ inline void UpdateVec2Info(Vec2RunInfo &vec2Info, uint32_t curBasicBlockIdx, const Vec1RunInfo &info);
__aicore__ inline bool IsNeedExcuteV2(Vec2RunInfo &vec2Info);
using X_T = typename AscendC::Conditional<COMP::xDtype == X_DTYPE::BF16, bfloat16_t, half>::type;
using T = float;
using MM1_OUT_T = T;
using VEC1_OUT_T = T;
// 常量
static constexpr uint64_t SYNC_MODE0 = 0;
static constexpr uint64_t SYNC_MODE2 = 2;
static constexpr uint32_t SYNC_C1_FLAG = 3;
static constexpr uint32_t SYNC_V1_FLAG = 4;
static constexpr uint32_t SYNC_V1_FLAG2 = 5;
static constexpr uint32_t SYNC_C1_V1_FLAG = 7;
static constexpr uint32_t SYNC_V1_C1_FLAG = 9;
// ==============================TilingData&TPipe==============================
TPipe* pipe_;
const optiling::CompressorTilingData* __restrict tilingData_;
// ===========================Workspace Global Tensor===========================
GlobalTensor<MM1_OUT_T> mm1KvResGm;
GlobalTensor<MM1_OUT_T> mm1ScoreResGm;
GlobalTensor<MM1_OUT_T> vec1KvCacheGm;
GlobalTensor<MM1_OUT_T> vec1ScoreCacheGm;
GlobalTensor<MM1_OUT_T> Vec1InputKvGm;
GlobalTensor<MM1_OUT_T> Vec1InputScoreGm;
GlobalTensor<VEC1_OUT_T> vec1ResGm;
GlobalTensor<VEC1_OUT_T> vec2InputGm;
// ================================Task Info====================================
CompressorTools<COMP> tools_;
ConstInfo constInfo{};
uint32_t aiCoreIdx = 0;
// ==============================Service Define==============================
CompressorBlockCube<COMP> blockCube_;
CompressorBlockVector<COMP> blockVec_;
uint32_t allCompressedTcNum_ = 0;
uint32_t curCompressedTcNum_ = 0;
uint32_t accDealSize = 0;
uint32_t loopTimes = 0;
uint32_t cubeLoop = 0;
uint32_t vec1Loop = 0;
uint32_t vec2Loop = 0;
uint32_t kStartIdx_ = 0;
uint32_t dealKSize_ = 0;
uint32_t hStart_ = 0;
bool isFirstUpdateCurGroup = true;
};
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace)
{
if ASCEND_IS_AIV {
constInfo.aiCoreIdx = GetBlockIdx() / 2;
} else {
constInfo.aiCoreIdx = GetBlockIdx();
}
InitTilingData();
// init tools
tools_.toolParams_.seqSize = tilingData_->baseParams.seqSize;
tools_.toolParams_.cmpRatio = tilingData_->baseParams.cmpRatio;
tools_.Init(startPos, seqUsed, cuSeqlens);
// 剔除尾部的无效batch
for (; constInfo.batchSize > 0; --constInfo.batchSize) {
uint32_t bSeqUsed = tools_.GetSeqLength(constInfo.batchSize - 1);
if (bSeqUsed > 0) {
break;
}
}
// 所有batch的有效序列都为0时, 直接退出
if (constInfo.batchSize == 0) {
return;
}
// 0. 计算最后一个Tc块的起始位置
constInfo.bIdxOfLastTc = constInfo.batchSize - 1;
// 1. 计算head_dim的切分大小, 构建ConstInfo的其他信息
CalcSplitCoreInfo();
SplitK(); // K轴切分
// 2. 计算循环次数
loopTimes = GetLoopTimes();
// 3. 初始化workspace
InitWorkspace(workspace);
// 4. 初始化block层
if ASCEND_IS_AIC {
blockCube_.InitParams(constInfo, tools_);
blockCube_.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos,
stateBlockTable, cuSeqlens, seqUsed, startPos, cmpKvOut);
blockCube_.InitBuffers(pipe_);
blockCube_.InitGlobalBuffers(mm1KvResGm, mm1ScoreResGm);
} else {
blockVec_.InitParams(constInfo, tools_);
blockVec_.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos, stateBlockTable,
cuSeqlens, seqUsed, startPos, cmpKvOut);
blockVec_.InitBuffers(pipe_);
blockVec_.InitVec1GlobalTensor(Vec1InputKvGm, Vec1InputScoreGm, vec1KvCacheGm, vec1ScoreCacheGm, vec1ResGm, vec2InputGm);
}
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::InitTilingData() {
constInfo.cmpRatio = tilingData_->baseParams.cmpRatio;
constInfo.batchSize = tilingData_->baseParams.batchSize;
constInfo.mBaseSize = tilingData_->innerSplitParams.mBaseSize;
constInfo.dBaseSize = tilingData_->innerSplitParams.dBaseSize;
constInfo.kBaseSize = tilingData_->baseParams.hiddenSize;
constInfo.kBaseNum = 1;
constInfo.headDim = tilingData_->baseParams.headDim;
constInfo.hSize = tilingData_->baseParams.hiddenSize;
constInfo.sSize = tilingData_->baseParams.seqSize;
constInfo.ropeHeadDim = tilingData_->baseParams.ropeHeadDim;
constInfo.normEps = tilingData_->baseParams.normEps;
constInfo.stateCacheStrideDim0 = tilingData_->baseParams.stateCacheStrideDim0;
constInfo.reciprocalD = tilingData_->baseParams.reciprocalD;
constInfo.usedCoreNum = tilingData_->baseParams.usedCoreNum;
constInfo.blockNum = tilingData_->pageAttentionParams.blockNum;
constInfo.blockSize = tilingData_->pageAttentionParams.blockSize;
constInfo.maxBlockNumPerBatch = tilingData_->pageAttentionParams.maxBlockNumPerBatch;
constInfo.nSize = tilingData_->baseParams.nSize;
constInfo.vec1TailCacheSize = tilingData_->workspaceParams.vec1TailCacheSize;
constInfo.dbWorkspaceRatio = tilingData_->workspaceParams.dbWorkspaceRatio;
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::SplitK()
{
uint32_t mSize = 0;
for (uint32_t i = 0; i < constInfo.batchSize; i++) {
uint32_t bSeqUsed = tools_.GetSeqLength(i);
// 获取m大小
mSize += bSeqUsed;
}
uint32_t mBaseNum = CeilDivT(mSize, constInfo.mBaseSize);
if (constInfo.dBasicBlockNum * mBaseNum < constInfo.usedCoreNum) {
constInfo.kBaseNum = constInfo.usedCoreNum / constInfo.dBasicBlockNum;
uint32_t kAlignSize =
CeilDivT(Align(constInfo.hSize, static_cast<uint32_t>(BUFFER_SIZE_BYTE_32B / sizeof(X_T))),
constInfo.kBaseNum);
constInfo.kBaseSize = Trunc(kAlignSize, static_cast<uint32_t>(BUFFER_SIZE_BYTE_32B / sizeof(X_T)));
// 当切m轴无法满足开满核时不切m轴(切m处理有点复杂)
constInfo.mGroupNum = 1; // 在m轴处理上所有核当一个组
constInfo.mCurGroupIdx = 0; // 只有一个组
}
// 每轮固定不变,预计算后主循环直接复用
if (constInfo.kBaseNum > 1) {
kStartIdx_ = constInfo.aiCoreIdx / constInfo.dBasicBlockNum;
if (constInfo.curGroupIdx + 1 < constInfo.coreGroupNum) {
dealKSize_ = constInfo.kBaseSize;
hStart_ = kStartIdx_ * dealKSize_;
} else {
dealKSize_ = kStartIdx_ < constInfo.coreGroupNum ?
constInfo.hSize - kStartIdx_ * constInfo.kBaseSize : 0;
hStart_ = kStartIdx_ * constInfo.kBaseSize;
}
} else {
kStartIdx_ = 0;
dealKSize_ = constInfo.hSize;
hStart_ = kStartIdx_ * dealKSize_;
}
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::SkipInvalidBatch(BatchInfo &batchInfo)
{
for (; batchInfo.bIdx < constInfo.batchSize; ++batchInfo.bIdx) {
batchInfo.seqCnt = tools_.GetSeqLength(batchInfo.bIdx);
if (batchInfo.seqCnt > 0) {
break;
}
}
batchInfo.remSeqCnt = batchInfo.seqCnt;
if (tools_.isExistSeqUsed_) {
batchInfo.seqUsedCnt = tools_.GetSeqUsed(batchInfo.bIdx);
} else {
batchInfo.seqUsedCnt = batchInfo.seqCnt;
}
if (batchInfo.bIdx < constInfo.batchSize) {
batchInfo.bStartPos = tools_.GetStartPos(batchInfo.bIdx);
batchInfo.sIdx = 0;
batchInfo.headHolderSeq = batchInfo.bStartPos & (constInfo.cmpRatio - 1);
batchInfo.tcNum = (batchInfo.bStartPos + batchInfo.seqCnt + constInfo.cmpRatio - 1) / constInfo.cmpRatio - batchInfo.bStartPos / constInfo.cmpRatio;
batchInfo.compressedTcNum = (batchInfo.bStartPos + batchInfo.seqUsedCnt) / constInfo.cmpRatio - batchInfo.bStartPos / constInfo.cmpRatio;
}
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::UpdateCurGroup(BasicBlockInfo &basicBlockInfo,
BatchInfo batchInfo, uint32_t &curGroupQuota, uint32_t curDealSeq)
{
// 更新当前组的信息
if (curGroupQuota == 0 && !isFirstUpdateCurGroup) {
return;
}
isFirstUpdateCurGroup = false;
basicBlockInfo.bIdx = batchInfo.bIdx;
uint32_t curGroupDealSeq = curGroupQuota < curDealSeq ? curGroupQuota : curDealSeq;
basicBlockInfo.sIdx = batchInfo.sIdx + curGroupDealSeq;
basicBlockInfo.dealSeqCnt += curGroupDealSeq;
curGroupQuota -= curGroupDealSeq;
// 结尾需要跳batch需要考虑在当前组起始为末尾或者当前组起始大于整个M轴
if ((curGroupQuota == 0 || basicBlockInfo.bIdx == constInfo.batchSize - 1) && basicBlockInfo.sIdx == batchInfo.seqCnt) {
basicBlockInfo.sIdx = 0;
for (basicBlockInfo.bIdx++; basicBlockInfo.bIdx < constInfo.batchSize; ++basicBlockInfo.bIdx) {
uint32_t seqCnt = tools_.GetSeqLength(basicBlockInfo.bIdx);
if (seqCnt > 0) {
break;
}
}
}
}
template <typename COMP>
__aicore__ inline BasicBlockInfo CompressorKernel<COMP>::SkipOneLoop(BatchInfo &batchInfo)
{
BasicBlockInfo basicBlockInfo{};
isFirstUpdateCurGroup = true;
uint32_t curGroupQuota = constInfo.mBaseSize * constInfo.mCurGroupIdx; // m轴当前组起始
bool curGroupStartFlag = false;
uint32_t quota = constInfo.mGroupNum * constInfo.mBaseSize;
for (; batchInfo.bIdx < constInfo.batchSize;) {
uint32_t curDealSeq = 0;
uint32_t curDealTcNum = 0;
uint32_t curDealCompressedTcNum = 0;
// 无法处理完当前整个batch
if (quota < batchInfo.remSeqCnt) {
// 向下对齐r
uint32_t alignSeq = constInfo.cmpRatio;
if (batchInfo.bIdx == 0) {
alignSeq = constInfo.cmpRatio - batchInfo.headHolderSeq;
}
if (quota > alignSeq) {
uint32_t delta = (batchInfo.bStartPos + batchInfo.sIdx + quota) & (constInfo.cmpRatio - 1); // 超出对齐的部分
curDealSeq = quota - delta;
quota -= curDealSeq;
curDealTcNum = (curDealSeq + constInfo.cmpRatio - 1) / constInfo.cmpRatio;
curDealCompressedTcNum = min(curDealTcNum, batchInfo.compressedTcNum);
// 更新当前组所需信息
UpdateCurGroup(basicBlockInfo, batchInfo, curGroupQuota, curDealSeq);
// 更新batch信息
batchInfo.remSeqCnt = batchInfo.remSeqCnt - curDealSeq;
batchInfo.sIdx = batchInfo.sIdx + curDealSeq;
batchInfo.compressedTcNum -= curDealCompressedTcNum;
batchInfo.tcNum -= curDealTcNum;
// 更新loop信息
basicBlockInfo.dealTcNum += curDealTcNum;
basicBlockInfo.compressedTcNum += curDealCompressedTcNum;
}
break;
} else {
// 处理整个batch
quota -= batchInfo.remSeqCnt;
curDealSeq = batchInfo.remSeqCnt;
curDealTcNum = batchInfo.tcNum;
// 更新当前组所需信息
UpdateCurGroup(basicBlockInfo, batchInfo, curGroupQuota, curDealSeq);
// 更新batch和loop信息
batchInfo.remSeqCnt = 0;
basicBlockInfo.dealTcNum += batchInfo.tcNum;
basicBlockInfo.compressedTcNum += batchInfo.compressedTcNum;
batchInfo.bIdx++;
SkipInvalidBatch(batchInfo);
}
}
uint32_t totalDataSize = constInfo.mGroupNum * constInfo.mBaseSize - quota;
// 2. 当前组的起始偏移
uint32_t currentGroupStart = constInfo.mCurGroupIdx * constInfo.mBaseSize;
// 3. 安全判断
if (currentGroupStart >= totalDataSize) {
// 超出尾块
basicBlockInfo.dealSeqCnt = 0;
} else {
// 还在有效范围内,计算剩余量
uint32_t remaining = totalDataSize - currentGroupStart;
basicBlockInfo.dealSeqCnt = (remaining < constInfo.mBaseSize) ? remaining : constInfo.mBaseSize;
}
return basicBlockInfo;
}
template <typename COMP>
__aicore__ inline uint32_t CompressorKernel<COMP>::GetLoopTimes()
{
// 计算主循环次数
uint32_t loopTimes = 0;
BatchInfo batchInfo{};
SkipInvalidBatch(batchInfo);
for (;batchInfo.bIdx < constInfo.batchSize; ++loopTimes) {
SkipOneLoop(batchInfo);
}
return loopTimes;
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::CalcSplitCoreInfo()
{
// D方向的基本块数量
constInfo.dBasicBlockNum = constInfo.headDim / constInfo.dBaseSize;
// 核的组数
constInfo.coreGroupNum = constInfo.usedCoreNum / constInfo.dBasicBlockNum;
// 每个核处理的d方向的索引
constInfo.dIdx = constInfo.aiCoreIdx % constInfo.dBasicBlockNum;
// 当前组id
constInfo.curGroupIdx = constInfo.aiCoreIdx / constInfo.dBasicBlockNum;
constInfo.mGroupNum = constInfo.coreGroupNum;
constInfo.mCurGroupIdx = constInfo.curGroupIdx;
constInfo.mm1ResSize = constInfo.mBaseSize * constInfo.headDim * constInfo.coreGroupNum;
uint32_t coff = (uint32_t)COMP::coff;
constInfo.mm1KvResSize = constInfo.mBaseSize * constInfo.headDim * coff;
constInfo.mm1ScoreResSize = constInfo.mBaseSize * constInfo.headDim * coff;
constInfo.vec1ResSize = constInfo.mBaseSize * constInfo.headDim * constInfo.nSize;
constInfo.dbSize = constInfo.coreGroupNum * constInfo.mm1KvResSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::InitWorkspace(__gm__ uint8_t *workspace) {
uint64_t offset = 0;
uint64_t mm1KvResStartOffset = offset;
// mm1KvResGm
mm1KvResGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + offset +
constInfo.curGroupIdx * constInfo.mm1KvResSize * sizeof(MM1_OUT_T)));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.mm1KvResSize * sizeof(MM1_OUT_T);
uint64_t mm1ScoreResStartOffset = offset;
// mm1ScoreResGm
mm1ScoreResGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + offset +
constInfo.curGroupIdx * constInfo.mm1ScoreResSize * sizeof(MM1_OUT_T)));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.mm1ScoreResSize * sizeof(MM1_OUT_T);
Vec1InputKvGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + mm1KvResStartOffset));
Vec1InputScoreGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + mm1ScoreResStartOffset));
vec1KvCacheGm.SetGlobalBuffer((__gm__ MM1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.vec1TailCacheSize * sizeof(MM1_OUT_T);
vec1ScoreCacheGm.SetGlobalBuffer((__gm__ MM1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.vec1TailCacheSize * sizeof(MM1_OUT_T);
uint64_t beforeVecOffset = offset;
// vec1Res
vec1ResGm.SetGlobalBuffer(
(__gm__ VEC1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.vec1ResSize * sizeof(VEC1_OUT_T);
// vec2Input
vec2InputGm.SetGlobalBuffer(
(__gm__ VEC1_OUT_T *)(workspace + beforeVecOffset));
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::ComputeMm1(const RunInfo &info, bool isNeedExcute) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_FIX>(SYNC_V1_C1_FLAG + info.cubeDbIdx);
if (isNeedExcute) {
blockCube_.ComputeMm1(info);
}
CrossCoreSetFlag<SYNC_MODE0, PIPE_FIX>(SYNC_C1_FLAG);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_FIX>(SYNC_C1_FLAG);
CrossCoreSetFlag<SYNC_MODE2, PIPE_FIX>(SYNC_C1_V1_FLAG + info.cubeDbIdx);
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::ComputeVec1(const Vec1RunInfo &info) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_C1_V1_FLAG + info.c1v1DbIdx);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG2 + info.c1v1DbIdx);
blockVec_.ComputeVec1(info);
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG);
CrossCoreSetFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_V1_C1_FLAG + info.c1v1DbIdx);
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE3>(SYNC_V1_FLAG2 + (info.c1v1DbIdx + 1) % constInfo.dbWorkspaceRatio);
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::ComputeVec2(const Vec2RunInfo &info) {
blockVec_.ComputeVec2(info);
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::AllocEventID()
{
if ASCEND_IS_AIC {
blockCube_.AllocEventID(pipe_);
} else {
blockVec_.AllocEventID();
for (int i = 0; i < constInfo.dbWorkspaceRatio; ++i) {
CrossCoreSetFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_V1_C1_FLAG + i);
}
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE3>(SYNC_V1_FLAG2);
}
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::FreeEventID()
{
if ASCEND_IS_AIC {
for (int i = 0; i < constInfo.dbWorkspaceRatio; ++i) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_FIX>(SYNC_V1_C1_FLAG + i);
}
blockCube_.FreeEventID(pipe_);
} else {
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG2 + loopTimes % constInfo.dbWorkspaceRatio);
blockVec_.FreeEventID();
}
}
template <typename COMP>
__aicore__ inline bool CompressorKernel<COMP>::IsNeedExcuteC1(RunInfo info)
{
// B超出范围则cube不执行
return info.bStart < constInfo.batchSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::CalcC1V1Params(RunInfo &info, Vec1RunInfo &vec1Info, BatchInfo &batchInfo, uint32_t loopIdx)
{
vec1Info.bStart = batchInfo.bIdx;
vec1Info.sStart = batchInfo.sIdx;
vec1Info.resetResFlag = (loopIdx & (constInfo.nSize - 1)) == 0;
vec1Info.c1v1DbIdx = (vec1Loop++ & (constInfo.dbWorkspaceRatio - 1));
vec1Info.v1v2DbIdx = (vec2Loop & (constInfo.dbWorkspaceRatio - 1));
BasicBlockInfo basicBlockInfo = SkipOneLoop(batchInfo);
info.cubeDbIdx = (cubeLoop++ & (constInfo.dbWorkspaceRatio - 1));
info.dealSeqCnt = basicBlockInfo.dealSeqCnt;
info.dealTcNum = basicBlockInfo.dealTcNum;
info.bStart = basicBlockInfo.bIdx;
info.sStart = basicBlockInfo.sIdx;
info.kStartIdx = kStartIdx_;
info.dealKSize = dealKSize_;
info.hStart = hStart_;
vec1Info.dealTcNum = basicBlockInfo.dealTcNum;
vec1Info.dealScSize = basicBlockInfo.compressedTcNum;
allCompressedTcNum_ += basicBlockInfo.compressedTcNum;
}
template <typename COMP>
__aicore__ inline bool CompressorKernel<COMP>::IsNeedExcuteV2(Vec2RunInfo &vec2Info)
{
return (vec2Info.dealScSize > 0);
}
template <typename COMP>
__aicore__ inline bool CompressorKernel<COMP>::IsNeedSyncAll(uint32_t curBasicBlockIdx)
{
if (allCompressedTcNum_ == 0) {
return false;
}
uint32_t cnt = curBasicBlockIdx + 1;
if ((cnt == loopTimes) || (cnt % constInfo.nSize == 0)) {
return true;
}
return false;
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::UpdateVec2Info(
Vec2RunInfo &vec2Info, uint32_t curBasicBlockIdx, const Vec1RunInfo &info)
{
// nSize轮起始先重置v2Info信息
if (curBasicBlockIdx % constInfo.nSize == 0) {
vec2Info.v2DbIdx = (vec2Loop & (constInfo.dbWorkspaceRatio - 1));
vec2Info.bStart = info.bStart;
vec2Info.sStart = info.sStart;
// 将sStart转成bCompressedId
uint32_t startPos = tools_.GetStartPos(info.bStart);
if (tools_.isExistSeqUsed_) {
uint32_t seqUsed = tools_.GetSeqUsed(info.bStart);
if (vec2Info.sStart >= seqUsed) {
vec2Info.bStart++;
vec2Info.sStart = 0;
}
}
vec2Info.bCompressedId = (startPos + vec2Info.sStart) / constInfo.cmpRatio - startPos / constInfo.cmpRatio;
vec2Info.dealScSize = 0;
} else if ((curBasicBlockIdx + 1) % constInfo.nSize == 0) {
vec2Loop++;
}
vec2Info.dealScSize += info.dealScSize;
vec2Info.compressedId += info.dealScSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernel<COMP>::Process()
{
// 所有batch的有效序列都为0时, 直接退出
if (constInfo.batchSize == 0) {
return;
}
AllocEventID();
BatchInfo batchInfo{};
RunInfo extraInfo[1];
Vec1RunInfo vec1Info{};
Vec2RunInfo vec2Info{};
SkipInvalidBatch(batchInfo);
for (uint32_t i = 0; i < loopTimes; ++i) {
RunInfo &extraInfo0 = extraInfo[0];
CalcC1V1Params(extraInfo0, vec1Info, batchInfo, i);
bool isNeedExcuteC1 = IsNeedExcuteC1(extraInfo0);
if ASCEND_IS_AIC {
ComputeMm1(extraInfo0, isNeedExcuteC1);
} else {
ComputeVec1(vec1Info);
UpdateVec2Info(vec2Info, i, vec1Info);
if (IsNeedSyncAll(i)) {
SyncAll();
if (IsNeedExcuteV2(vec2Info)) {
ComputeVec2(vec2Info);
}
}
}
}
FreeEventID();
}
} // namespace Compressor
#endif // COMPRESSOR_KERNEL_H

View File

@@ -0,0 +1,474 @@
/**
 * 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 compressor_kernel_full_load.h
* \brief
*/
#ifndef COMPRESSOR_KERNEL_FULL_LOAD_H
#define COMPRESSOR_KERNEL_FULL_LOAD_H
#include "compressor_comm.h"
#include "compressor_template_tiling_key.h"
#include "compressor_tiling_data.h"
#include "compressor_tools.h"
#include "compressor_block_cube_full_load.h"
#include "compressor_block_vec_full_load.h"
using namespace AscendC;
namespace Compressor {
template <typename COMP>
class CompressorKernelFullLoad {
public:
__aicore__ inline CompressorKernelFullLoad(TPipe* pipe, const optiling::CompressorTilingData* __restrict tilingData)
: pipe_(pipe), tilingData_(tilingData) {}
__aicore__ inline void Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace);
__aicore__ inline void Process();
private:
// ================================Init functions==================================
__aicore__ inline void InitWorkspace(__gm__ uint8_t *workspace);
// ================================Process functions================================
__aicore__ inline void InitTilingData();
// 获取基本块数量
__aicore__ inline void SkipInvalidBatch(BatchInfo &batchInfo);
// 计算分核基本信息
__aicore__ inline void CalcSplitCoreInfo();
__aicore__ inline void AllocEventID();
__aicore__ inline void FreeEventID();
__aicore__ inline void ComputeMm1(const RunInfo &info);
__aicore__ inline void ComputeVec1(const Vec1RunInfo &info);
__aicore__ inline void ComputeVec2(const Vec2RunInfo &info);
__aicore__ inline bool IsNeedExcuteC1(RunInfo info);
__aicore__ inline bool IsNeedSyncAll(uint32_t curBasicBlockIdx);
__aicore__ inline void UpdateVec2Info(Vec2RunInfo &vec2Info, uint32_t curBasicBlockIdx, const Vec1RunInfo &info);
__aicore__ inline bool IsNeedExcuteV2(Vec2RunInfo &vec2Info);
__aicore__ inline void CalcCubeParams(RunInfo &info);
__aicore__ inline void CalcV1Params(Vec1RunInfo &vec1Info);
using X_T = typename AscendC::Conditional<COMP::xDtype == X_DTYPE::BF16, bfloat16_t, half>::type;
using T = float;
using MM1_OUT_T = T;
using VEC1_OUT_T = T;
// 常量
static constexpr uint64_t SYNC_MODE0 = 0;
static constexpr uint64_t SYNC_MODE2 = 2;
static constexpr uint32_t SYNC_C1_FLAG = 3;
static constexpr uint32_t SYNC_V1_FLAG = 4;
static constexpr uint32_t SYNC_V1_FLAG2 = 5;
static constexpr uint32_t SYNC_C1_V1_FLAG = 7;
static constexpr uint32_t SYNC_V1_C1_FLAG = 9;
// ==============================TilingData&TPipe==============================
TPipe* pipe_;
const optiling::CompressorTilingData* __restrict tilingData_;
// ===========================Workspace Global Tensor===========================
GlobalTensor<MM1_OUT_T> mm1KvResGm;
GlobalTensor<MM1_OUT_T> mm1ScoreResGm;
GlobalTensor<MM1_OUT_T> vec1KvCacheGm;
GlobalTensor<MM1_OUT_T> vec1ScoreCacheGm;
GlobalTensor<MM1_OUT_T> Vec1InputKvGm;
GlobalTensor<MM1_OUT_T> Vec1InputScoreGm;
GlobalTensor<VEC1_OUT_T> vec1ResGm;
GlobalTensor<VEC1_OUT_T> vec2InputGm;
// ================================Task Info====================================
CompressorTools<COMP> tools_;
ConstInfo constInfo{};
uint32_t aiCoreIdx = 0;
// ==============================Service Define==============================
CompressorBlockCubeFullLoad<COMP> blockCube_;
CompressorBlockVectorFullLoad<COMP> blockVec_;
uint32_t allCompressedTcNum_ = 0;
uint32_t curCompressedTcNum_ = 0;
uint32_t accDealSize = 0;
uint32_t loopTimes = 0;
uint32_t cubeLoop = 0;
uint32_t vec1Loop = 0;
uint32_t vec2Loop = 0;
uint32_t kStartIdx_ = 0;
uint32_t dealKSize_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::Init(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *workspace)
{
if ASCEND_IS_AIV {
constInfo.aiCoreIdx = GetBlockIdx() / 2;
} else {
constInfo.aiCoreIdx = GetBlockIdx();
}
InitTilingData();
// init tools
tools_.toolParams_.seqSize = tilingData_->baseParams.seqSize;
tools_.toolParams_.cmpRatio = tilingData_->baseParams.cmpRatio;
tools_.Init(startPos, seqUsed, cuSeqlens);
// 剔除尾部的无效batch
for (; constInfo.batchSize > 0; --constInfo.batchSize) {
uint32_t bSeqUsed = tools_.GetSeqLength(constInfo.batchSize - 1);
if (bSeqUsed > 0) {
break;
}
}
// 所有batch的有效序列都为0时, 直接退出
if (constInfo.batchSize == 0) {
return;
}
// 0. 计算最后一个Tc块的起始位置
constInfo.bIdxOfLastTc = constInfo.batchSize - 1;
// 1. 计算head_dim的切分大小, 构建ConstInfo的其他信息
CalcSplitCoreInfo();
// 2. 计算循环次数
loopTimes = 1;
// 3. 初始化workspace
InitWorkspace(workspace);
// 4. 初始化block层
if ASCEND_IS_AIC {
blockCube_.InitParams(constInfo, tools_);
blockCube_.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos,
stateBlockTable, cuSeqlens, seqUsed, startPos, cmpKvOut);
blockCube_.InitBuffers(pipe_);
blockCube_.InitGlobalBuffers(mm1KvResGm, mm1ScoreResGm);
} else {
blockVec_.InitParams(constInfo, tools_);
blockVec_.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos, stateBlockTable,
cuSeqlens, seqUsed, startPos, cmpKvOut);
blockVec_.InitBuffers(pipe_);
blockVec_.InitVec1GlobalTensor(Vec1InputKvGm, Vec1InputScoreGm, vec1KvCacheGm, vec1ScoreCacheGm, vec1ResGm, vec2InputGm);
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::InitTilingData() {
constInfo.cmpRatio = tilingData_->baseParams.cmpRatio;
constInfo.batchSize = tilingData_->baseParams.batchSize;
constInfo.mBaseSize = tilingData_->innerSplitParams.mBaseSize;
constInfo.dBaseSize = tilingData_->innerSplitParams.dBaseSize;
constInfo.headDim = tilingData_->baseParams.headDim;
constInfo.hSize = tilingData_->baseParams.hiddenSize;
constInfo.sSize = tilingData_->baseParams.seqSize;
constInfo.ropeHeadDim = tilingData_->baseParams.ropeHeadDim;
constInfo.normEps = tilingData_->baseParams.normEps;
constInfo.stateCacheStrideDim0 = tilingData_->baseParams.stateCacheStrideDim0;
constInfo.reciprocalD = tilingData_->baseParams.reciprocalD;
constInfo.usedCoreNum = tilingData_->baseParams.usedCoreNum;
constInfo.blockNum = tilingData_->pageAttentionParams.blockNum;
constInfo.blockSize = tilingData_->pageAttentionParams.blockSize;
constInfo.maxBlockNumPerBatch = tilingData_->pageAttentionParams.maxBlockNumPerBatch;
constInfo.nSize = tilingData_->baseParams.nSize;
constInfo.vec1TailCacheSize = tilingData_->workspaceParams.vec1TailCacheSize;
constInfo.dbWorkspaceRatio = tilingData_->workspaceParams.dbWorkspaceRatio;
constInfo.mStart = tilingData_->baseParams.splitCoreParam[constInfo.aiCoreIdx].mStart;
constInfo.mEnd = tilingData_->baseParams.splitCoreParam[constInfo.aiCoreIdx].mEnd;
constInfo.nStart = tilingData_->baseParams.splitCoreParam[constInfo.aiCoreIdx].nStart;
constInfo.nEnd = tilingData_->baseParams.splitCoreParam[constInfo.aiCoreIdx].nEnd;
constInfo.kStart = tilingData_->baseParams.splitCoreParam[constInfo.aiCoreIdx].kStart;
constInfo.kEnd = tilingData_->baseParams.splitCoreParam[constInfo.aiCoreIdx].kEnd;
constInfo.mLoopNum = tilingData_->baseParams.mLoopNum;
constInfo.kBaseNum = tilingData_->baseParams.kBaseNum;
constInfo.kBaseSize = tilingData_->baseParams.kBaseSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::SkipInvalidBatch(BatchInfo &batchInfo)
{
for (; batchInfo.bIdx < constInfo.batchSize; ++batchInfo.bIdx) {
batchInfo.seqCnt = tools_.GetSeqLength(batchInfo.bIdx);
if (batchInfo.seqCnt > 0) {
break;
}
}
batchInfo.remSeqCnt = batchInfo.seqCnt;
if (tools_.isExistSeqUsed_) {
batchInfo.seqUsedCnt = tools_.GetSeqUsed(batchInfo.bIdx);
} else {
batchInfo.seqUsedCnt = batchInfo.seqCnt;
}
if (batchInfo.bIdx < constInfo.batchSize) {
batchInfo.bStartPos = tools_.GetStartPos(batchInfo.bIdx);
batchInfo.sIdx = 0;
batchInfo.headHolderSeq = batchInfo.bStartPos & (constInfo.cmpRatio - 1);
batchInfo.tcNum = (batchInfo.bStartPos + batchInfo.seqCnt + constInfo.cmpRatio - 1) / constInfo.cmpRatio - batchInfo.bStartPos / constInfo.cmpRatio;
batchInfo.compressedTcNum = (batchInfo.bStartPos + batchInfo.seqUsedCnt) / constInfo.cmpRatio - batchInfo.bStartPos / constInfo.cmpRatio;
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::CalcSplitCoreInfo()
{
// D方向的基本块数量
constInfo.dBasicBlockNum = constInfo.headDim / constInfo.dBaseSize;
// 核的组数
constInfo.coreGroupNum = constInfo.usedCoreNum / constInfo.dBasicBlockNum;
// 当前组id
constInfo.curGroupIdx = constInfo.aiCoreIdx / constInfo.dBasicBlockNum;
constInfo.mGroupNum = constInfo.coreGroupNum;
constInfo.mCurGroupIdx = constInfo.curGroupIdx;
uint32_t coff = (uint32_t)COMP::coff;
constInfo.mm1KvResSize = constInfo.mBaseSize * constInfo.headDim * coff;
constInfo.mm1ScoreResSize = constInfo.mBaseSize * constInfo.headDim * coff;
constInfo.vec1ResSize = constInfo.mBaseSize * constInfo.headDim * constInfo.nSize;
constInfo.dbSize = constInfo.coreGroupNum * constInfo.mm1KvResSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::InitWorkspace(__gm__ uint8_t *workspace) {
uint64_t offset = 0;
uint64_t mm1KvResStartOffset = offset;
// mm1KvResGm
mm1KvResGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + offset +
constInfo.curGroupIdx * constInfo.mm1KvResSize * sizeof(MM1_OUT_T)));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.mm1KvResSize * sizeof(MM1_OUT_T);
uint64_t mm1ScoreResStartOffset = offset;
// mm1ScoreResGm
mm1ScoreResGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + offset +
constInfo.curGroupIdx * constInfo.mm1ScoreResSize * sizeof(MM1_OUT_T)));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.mm1ScoreResSize * sizeof(MM1_OUT_T);
Vec1InputKvGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + mm1KvResStartOffset));
Vec1InputScoreGm.SetGlobalBuffer(
(__gm__ MM1_OUT_T *)(workspace + mm1ScoreResStartOffset));
vec1KvCacheGm.SetGlobalBuffer((__gm__ MM1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.vec1TailCacheSize * sizeof(MM1_OUT_T);
vec1ScoreCacheGm.SetGlobalBuffer((__gm__ MM1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.vec1TailCacheSize * sizeof(MM1_OUT_T);
uint64_t beforeVecOffset = offset;
// vec1Res
vec1ResGm.SetGlobalBuffer(
(__gm__ VEC1_OUT_T *)(workspace + offset));
offset += constInfo.dbWorkspaceRatio * constInfo.coreGroupNum * constInfo.vec1ResSize * sizeof(VEC1_OUT_T);
// vec2Input
vec2InputGm.SetGlobalBuffer(
(__gm__ VEC1_OUT_T *)(workspace + beforeVecOffset));
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::ComputeMm1(const RunInfo &info) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_FIX>(SYNC_V1_C1_FLAG + info.cubeDbIdx);
blockCube_.ComputeMm1(info);
CrossCoreSetFlag<SYNC_MODE0, PIPE_FIX>(SYNC_C1_FLAG);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_FIX>(SYNC_C1_FLAG);
CrossCoreSetFlag<SYNC_MODE2, PIPE_FIX>(SYNC_C1_V1_FLAG + info.cubeDbIdx);
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::ComputeVec1(const Vec1RunInfo &info) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_C1_V1_FLAG + info.c1v1DbIdx);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG2 + info.c1v1DbIdx);
blockVec_.ComputeVec1();
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG);
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG);
CrossCoreSetFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_V1_C1_FLAG + info.c1v1DbIdx);
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE3>(SYNC_V1_FLAG2 + (info.c1v1DbIdx + 1) % constInfo.dbWorkspaceRatio);
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::ComputeVec2(const Vec2RunInfo &info) {
blockVec_.ComputeVec2();
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::AllocEventID()
{
if ASCEND_IS_AIC {
blockCube_.AllocEventID(pipe_);
} else {
blockVec_.AllocEventID();
for (int i = 0; i < constInfo.dbWorkspaceRatio; ++i) {
CrossCoreSetFlag<SYNC_MODE2, PIPE_MTE2>(SYNC_V1_C1_FLAG + i);
}
CrossCoreSetFlag<SYNC_MODE0, PIPE_MTE3>(SYNC_V1_FLAG2);
}
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::FreeEventID()
{
if ASCEND_IS_AIC {
for (int i = 0; i < constInfo.dbWorkspaceRatio; ++i) {
CrossCoreWaitFlag<SYNC_MODE2, PIPE_FIX>(SYNC_V1_C1_FLAG + i);
}
blockCube_.FreeEventID(pipe_);
} else {
CrossCoreWaitFlag<SYNC_MODE0, PIPE_MTE2>(SYNC_V1_FLAG2 + loopTimes % constInfo.dbWorkspaceRatio);
blockVec_.FreeEventID();
}
}
template <typename COMP>
__aicore__ inline bool CompressorKernelFullLoad<COMP>::IsNeedExcuteC1(RunInfo info)
{
// B超出范围则cube不执行
return info.bStart < constInfo.batchSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::CalcCubeParams(RunInfo &info)
{
info.cubeDbIdx = (cubeLoop++ & (constInfo.dbWorkspaceRatio - 1));
info.dealSeqCnt = constInfo.mEnd - constInfo.mStart;
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::CalcV1Params(Vec1RunInfo &vec1Info)
{
vec1Info.bStart = 0;
vec1Info.sStart = 0;
vec1Info.resetResFlag = false;
vec1Info.c1v1DbIdx = (vec1Loop++ & (constInfo.dbWorkspaceRatio - 1));
vec1Info.v1v2DbIdx = (vec2Loop & (constInfo.dbWorkspaceRatio - 1));
for (uint32_t bIdx = 0; bIdx < constInfo.batchSize; ++bIdx) {
uint64_t bSeqCnt = tools_.GetSeqLength(bIdx);
if (bSeqCnt == 0) {
continue;
}
uint64_t bStartPos = tools_.GetStartPos(bIdx);
vec1Info.dealTcNum += (bStartPos + bSeqCnt + constInfo.cmpRatio - 1) / constInfo.cmpRatio - bStartPos / constInfo.cmpRatio;
vec1Info.dealScSize += (bStartPos + bSeqCnt) / constInfo.cmpRatio - bStartPos / constInfo.cmpRatio;
}
}
template <typename COMP>
__aicore__ inline bool CompressorKernelFullLoad<COMP>::IsNeedExcuteV2(Vec2RunInfo &vec2Info)
{
return (vec2Info.dealScSize > 0);
}
template <typename COMP>
__aicore__ inline bool CompressorKernelFullLoad<COMP>::IsNeedSyncAll(uint32_t curBasicBlockIdx)
{
if (allCompressedTcNum_ == 0) {
return false;
}
uint32_t cnt = curBasicBlockIdx + 1;
if ((cnt == loopTimes) || (cnt % constInfo.nSize == 0)) {
return true;
}
return false;
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::UpdateVec2Info(
Vec2RunInfo &vec2Info, uint32_t curBasicBlockIdx, const Vec1RunInfo &info)
{
// nSize轮起始先重置v2Info信息
if (curBasicBlockIdx % constInfo.nSize == 0) {
vec2Info.v2DbIdx = (vec2Loop & (constInfo.dbWorkspaceRatio - 1));
vec2Info.bStart = info.bStart;
vec2Info.sStart = info.sStart;
// 将sStart转成bCompressedId
uint32_t startPos = tools_.GetStartPos(info.bStart);
if (tools_.isExistSeqUsed_) {
uint32_t seqUsed = tools_.GetSeqUsed(info.bStart);
if (vec2Info.sStart >= seqUsed) {
vec2Info.bStart++;
vec2Info.sStart = 0;
}
}
vec2Info.bCompressedId = (startPos + vec2Info.sStart) / constInfo.cmpRatio - startPos / constInfo.cmpRatio;
vec2Info.dealScSize = 0;
} else if ((curBasicBlockIdx + 1) % constInfo.nSize == 0) {
vec2Loop++;
}
vec2Info.dealScSize += info.dealScSize;
vec2Info.compressedId += info.dealScSize;
}
template <typename COMP>
__aicore__ inline void CompressorKernelFullLoad<COMP>::Process()
{
// 所有batch的有效序列都为0时, 直接退出
if (constInfo.batchSize == 0) {
return;
}
AllocEventID();
RunInfo extraInfo[1];
Vec1RunInfo vec1Info{};
Vec2RunInfo vec2Info{};
for (uint32_t i = 0; i < loopTimes; ++i) {
RunInfo &extraInfo0 = extraInfo[0];
if ASCEND_IS_AIV {
CalcV1Params(vec1Info);
} else {
CalcCubeParams(extraInfo0);
}
if ASCEND_IS_AIC {
ComputeMm1(extraInfo0);
} else {
ComputeVec1(vec1Info);
UpdateVec2Info(vec2Info, i, vec1Info);
SyncAll();
if (IsNeedExcuteV2(vec2Info)) {
ComputeVec2(vec2Info);
}
}
}
FreeEventID();
}
} // namespace Compressor
#endif // COMPRESSOR_KERNEL_FULL_LOAD_H

View File

@@ -0,0 +1,54 @@
/**
 * 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 COMPRESSOR_template_tiling_key.h
* \brief
*/
#ifndef COMPRESSOR_TEMPLATE_TILING_KEY_H
#define COMPRESSOR_TEMPLATE_TILING_KEY_H
#include "ascendc/host_api/tiling/template_argument.h"
#define ASCENDC_TPL_1_BW 1 // 每个参数占用1个bit位
#define ASCENDC_TPL_2_BW 2 // 每个参数占用2个bit位
#define ASCENDC_TPL_4_BW 4 // 每个参数占用4个bit位
// 可表示的tilingkey范围为64bit注意不可超过限制
ASCENDC_TPL_ARGS_DECL(compressor, // 算子唯一标识与opType保持一致
// 可能需要切分之后的headdim
// bit:0 LAYOUT 0:BSH 1:TH
ASCENDC_TPL_UINT_DECL(X_LAYOUT, ASCENDC_TPL_1_BW, ASCENDC_TPL_UI_LIST, 0, 1),
// bit:1-4 x的dtype 0:BF16 1:FP16
ASCENDC_TPL_UINT_DECL(X_DTYPE, ASCENDC_TPL_4_BW, ASCENDC_TPL_UI_LIST, 0, 1),
// bit:5-6 coff 1:无需overlap 2:需要overlap
ASCENDC_TPL_UINT_DECL(COFF, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 1, 2),
// bit:7-8 rotary_mode 1:half 2:interleave
ASCENDC_TPL_UINT_DECL(ROTARY_MODE, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 1, 2),
// bit:9-10 cache_mode 1:CONTINUOUS 2:cycle
ASCENDC_TPL_UINT_DECL(CACHE_MODE, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 1, 2),
// bit:11-12 template_id 0:empty_tensor 1:normal 2:full load
ASCENDC_TPL_UINT_DECL(TEMPLATE_ID, ASCENDC_TPL_2_BW, ASCENDC_TPL_UI_LIST, 0, 1, 2),
);
ASCENDC_TPL_SEL(
ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_UINT_SEL(X_LAYOUT, ASCENDC_TPL_UI_LIST, 0, 1),
ASCENDC_TPL_UINT_SEL(X_DTYPE, ASCENDC_TPL_UI_LIST, 0, 1),
ASCENDC_TPL_UINT_SEL(COFF, ASCENDC_TPL_UI_LIST, 1, 2),
ASCENDC_TPL_UINT_SEL(ROTARY_MODE, ASCENDC_TPL_UI_LIST, 1, 2),
ASCENDC_TPL_UINT_SEL(CACHE_MODE, ASCENDC_TPL_UI_LIST, 1, 2),
ASCENDC_TPL_UINT_SEL(TEMPLATE_ID, ASCENDC_TPL_UI_LIST, 0, 1, 2),
ASCENDC_TPL_TILING_STRUCT_SEL(optiling::CompressorTilingData)),
);
#endif // COMPRESSOR_TEMPLATE_TILING_KEY_H

View File

@@ -0,0 +1,83 @@
/**
 * 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 COMPRESSOR_tiling_datay.h
* \brief
*/
#ifndef COMPRESSOR_TILING_DATA_H
#define COMPRESSOR_TILING_DATA_H
#include <cstdint>
#include "kernel_tiling/kernel_tiling.h"
const uint32_t CMP_MAX_AIC_CORE_NUM = 36;
namespace optiling {
struct CompressorSplitCoreParams {
uint32_t mStart;
uint32_t mEnd;
uint32_t nStart;
uint32_t nEnd;
uint32_t kStart;
uint32_t kEnd;
};
// 1. 基础参数结构体
struct CompressorBaseParams {
uint32_t batchSize = 0; // bastch size批大小
uint32_t seqSize = 0; // sequence sizekvs大小
uint32_t hiddenSize = 0; // hidden size隐藏层大小
uint32_t tokenSize = 0; // token size = batchSize * seqSize(token总数批大小x序列1长度)
uint32_t headDim = 0; // head size of kv
uint32_t ropeHeadDim = 64; // dim size per rope head 64单个带RoPE头的维度
uint32_t csSize = 0; // Compress sequence len
uint32_t cmpRatio = 4; // Compress ratio
uint32_t cgSize = 0; // Compress group size
float normEps = 1e-6; // RMSNorm eps
float reciprocalD = 0; // 1分之D
uint32_t usedCoreNum = 0; // 使用核数
uint32_t nSize = 0; // 控制v2积攒的轮数
uint64_t stateCacheStrideDim0 = 0; // stateCache第0维的stride
uint32_t kBaseNum = 0;
uint32_t kBaseSize = 0;
uint32_t coreGroupNum = 0;
uint32_t mLoopNum = 0;
CompressorSplitCoreParams splitCoreParam[CMP_MAX_AIC_CORE_NUM];
};
struct CompressorPageAttentionParams {
uint32_t blockNum = 0;
uint32_t blockSize = 1;
uint32_t maxBlockNumPerBatch = 1;
};
struct CompressorInnerSplitParams {
uint32_t mBaseSize;
uint32_t dBaseSize;
};
struct CompressorWorkspaceParams {
uint32_t mm1KvResSize;
uint32_t mm1ScoreResSize;
uint32_t vec1ResSize;
uint32_t vec1TailCacheSize;
uint32_t dbWorkspaceRatio = 1;
};
struct CompressorTilingData {
CompressorBaseParams baseParams;
CompressorPageAttentionParams pageAttentionParams;
CompressorInnerSplitParams innerSplitParams;
CompressorWorkspaceParams workspaceParams;
};
} // optiling
#endif // COMPRESSOR_TILING_DATA_H

View File

@@ -0,0 +1,890 @@
/**
 * 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 compressor_tools.h
* \brief 放算子都需要、与算子联系紧密、但是又不方便单独独立出来的公共工具
*/
#ifndef COMPRESSOR_TOOLS_H
#define COMPRESSOR_TOOLS_H
#include "compressor_comm.h"
using namespace AscendC;
namespace Compressor {
struct ToolsParams {
uint32_t seqSize = 0U;
uint32_t cmpRatio = 0U;
};
template <typename COMP>
class CompressorTools {
public:
__aicore__ inline CompressorTools() {}
__aicore__ inline void Init(__gm__ uint8_t *cuSeqlens, __gm__ uint8_t *seqUsed, __gm__ uint8_t *startPos);
__aicore__ inline uint32_t GetSeqUsed(uint32_t bIdx);
__aicore__ inline uint32_t GetStartPos(uint32_t bIdx);
__aicore__ inline uint32_t GetSeqLength(uint32_t bIdx);
__aicore__ inline uint32_t GetTIdxByBatch(uint32_t bIdx);
public:
ToolsParams toolParams_ {};
bool isExistSeqUsed_ = false;
private:
bool isExistStartPos_ = false;
GlobalTensor<int32_t> cuSeqlensGm_;
GlobalTensor<int32_t> sequsedGm_;
GlobalTensor<int32_t> startPosGm_;
};
template <typename COMP>
__aicore__ inline void CompressorTools<COMP>::Init(__gm__ uint8_t *startPos, __gm__ uint8_t *seqUsed,
__gm__ uint8_t *cuSeqlens)
{
isExistStartPos_ = (startPos != nullptr);
if (isExistStartPos_) {
startPosGm_.SetGlobalBuffer((__gm__ int32_t *)startPos);
}
isExistSeqUsed_ = (seqUsed != nullptr);
if (isExistSeqUsed_) {
sequsedGm_.SetGlobalBuffer((__gm__ int32_t *)seqUsed);
}
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
cuSeqlensGm_.SetGlobalBuffer((__gm__ int32_t *)cuSeqlens);
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetSeqUsed(uint32_t bIdx)
{
if (isExistSeqUsed_) {
return (uint32_t)sequsedGm_.GetValue(bIdx);
} else {
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
return (uint32_t)(cuSeqlensGm_.GetValue(bIdx + 1) - cuSeqlensGm_.GetValue(bIdx));
} else {
return toolParams_.seqSize;
}
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetStartPos(uint32_t bIdx)
{
if (isExistStartPos_) {
return (uint32_t)startPosGm_.GetValue(bIdx);
} else {
return 0;
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetSeqLength(uint32_t bIdx)
{
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
return cuSeqlensGm_.GetValue(bIdx + 1) - cuSeqlensGm_.GetValue(bIdx);
} else {
return toolParams_.seqSize;
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorTools<COMP>::GetTIdxByBatch(uint32_t bIdx)
{
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
return (uint32_t)(cuSeqlensGm_.GetValue(bIdx));
} else {
return toolParams_.seqSize * bIdx;
}
}
// iterator
struct SliceInfo {
__aicore__ inline SliceInfo(){};
__aicore__ inline SliceInfo(uint32_t bIdx, uint32_t sIdx) : bIdx(bIdx), sIdx(sIdx) {};
uint32_t bIdx = 0U;
uint32_t sIdx = 0U;
uint32_t bSeqUsed = 0U;
uint32_t bStartPos = 0U;
uint32_t headHolderSeqCnt = 0U;
uint32_t validSeqCnt = 0U;
uint32_t tailHolderSeqCnt = 0U;
uint32_t dealSeqCnt = 0;
uint32_t dealTcSize = 0U;
uint32_t compressTcSize = 0U;
};
template <typename COMP>
class CompressorSliceIterator {
public:
__aicore__ inline CompressorSliceIterator(CompressorTools<COMP> &tools) : tools_(tools) {}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetMaxDealSeqCnt(uint32_t maxDealSeqCnt);
__aicore__ inline bool IsEnd();
__aicore__ inline void IteratorSlice();
__aicore__ inline SliceInfo& GetSlice();
__aicore__ inline SliceInfo& GetSliceByCmp();
bool isFirst_ = true;
SliceInfo sliceInfo_{};
private:
CompressorTools<COMP> &tools_;
// iterator
uint32_t maxDealSeqCnt_ = 0;
uint32_t batch_size_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx)
{
sliceInfo_.bIdx = bIdx;
sliceInfo_.sIdx = sIdx;
isFirst_ = true;
}
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
}
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::SetMaxDealSeqCnt(uint32_t maxDealSeqCnt)
{
this->maxDealSeqCnt_ = maxDealSeqCnt;
}
template <typename COMP>
__aicore__ inline bool CompressorSliceIterator<COMP>::IsEnd()
{
return (sliceInfo_.bIdx >= batch_size_) || (maxDealSeqCnt_ == 0);
}
template <typename COMP>
__aicore__ inline void CompressorSliceIterator<COMP>::IteratorSlice()
{
bool isUpdateBatchInfo = false;
if (!isFirst_) {
// 更新剩余未处理的行数
maxDealSeqCnt_ -= sliceInfo_.dealSeqCnt;
// 更新sIdx和bIdx、以及与bIdx相关的bStartPos和bSeqUsed
sliceInfo_.sIdx += sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == sliceInfo_.bSeqUsed) {
sliceInfo_.sIdx = 0;
sliceInfo_.bIdx++;
isUpdateBatchInfo = true;
}
} else {
isUpdateBatchInfo = true;
isFirst_ = false;
}
// 更新与bIdx相关的bStartPos和bSeqUsed
if (isUpdateBatchInfo) {
// SkipInvalidBatch
while (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
if (sliceInfo_.bSeqUsed > 0) {
break;
}
sliceInfo_.bIdx++;
}
if (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
}
}
}
template <typename COMP>
__aicore__ inline SliceInfo& CompressorSliceIterator<COMP>::GetSliceByCmp()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
// 头和尾处理否则需要处理的seq等于cmpRatio
if (sliceInfo_.validSeqCnt < cmpRatio) {
sliceInfo_.dealSeqCnt = sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == 0) {
sliceInfo_.dealSeqCnt = cmpRatio - sliceInfo_.headHolderSeqCnt;
}
} else {
sliceInfo_.dealSeqCnt = cmpRatio;
}
sliceInfo_.validSeqCnt = sliceInfo_.dealSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = (sliceInfo_.dealSeqCnt + cmpRatio - 1) / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
return sliceInfo_;
}
template <typename COMP>
__aicore__ inline SliceInfo& CompressorSliceIterator<COMP>::GetSlice()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
sliceInfo_.dealSeqCnt = sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt + sliceInfo_.tailHolderSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = sliceInfo_.dealSeqCnt / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
return sliceInfo_;
}
struct SplitCoreSliceInfo : public SliceInfo {
__aicore__ inline SplitCoreSliceInfo() {};
__aicore__ inline SplitCoreSliceInfo(uint32_t bIdx, uint32_t sIdx) : SliceInfo(bIdx, sIdx) {};
uint32_t preFirstSeqCnt = 0U; // 左边每次迭代基本块的第一个seqCnt大小
};
template <typename COMP>
class CompressorSplitCoreSliceIterator {
public:
__aicore__ inline CompressorSplitCoreSliceIterator(CompressorTools<COMP> &tools) : tools_(tools) {}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetMaxDealSeqCnt(uint32_t maxDealSeqCnt);
__aicore__ inline bool IsEnd();
__aicore__ inline void IteratorSlice();
__aicore__ inline SplitCoreSliceInfo& GetSlice();
__aicore__ inline SplitCoreSliceInfo& GetSliceByCmp();
__aicore__ inline uint32_t GetBIdx();
__aicore__ inline SplitCoreSliceInfo& GetLeftNextCmpSeqCnt();
__aicore__ inline SplitCoreSliceInfo& GetRightNextCmpSeqCnt();
bool isFirst_ = true;
bool isLeftFirstBath = false;
bool isMaxDealSeqCntFirst = false;
SplitCoreSliceInfo sliceInfo_{};
private:
CompressorTools<COMP> &tools_;
// iterator
uint32_t maxDealSeqCnt_ = 0;
uint32_t batch_size_ = 0;
};
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx)
{
sliceInfo_.bIdx = bIdx;
sliceInfo_.sIdx = sIdx;
isFirst_ = true;
}
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
isMaxDealSeqCntFirst = true;
}
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::SetMaxDealSeqCnt(uint32_t maxDealSeqCnt)
{
this->maxDealSeqCnt_ = maxDealSeqCnt;
}
template <typename COMP>
__aicore__ inline bool CompressorSplitCoreSliceIterator<COMP>::IsEnd()
{
return (sliceInfo_.bIdx >= batch_size_) || (maxDealSeqCnt_ == 0);
}
template <typename COMP>
__aicore__ inline uint32_t CompressorSplitCoreSliceIterator<COMP>::GetBIdx()
{
return sliceInfo_.bIdx;
}
template <typename COMP>
__aicore__ inline void CompressorSplitCoreSliceIterator<COMP>::IteratorSlice()
{
bool isUpdateBatchInfo = false;
if (isMaxDealSeqCntFirst) {
isMaxDealSeqCntFirst = false;
}
if (!isFirst_) {
// 更新剩余未处理的行数
maxDealSeqCnt_ -= sliceInfo_.dealSeqCnt;
// 更新sIdx和bIdx、以及与bIdx相关的bStartPos和bSeqUsed
sliceInfo_.sIdx += sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == sliceInfo_.bSeqUsed) {
sliceInfo_.sIdx = 0;
// 左边最后一块跳到b=0 s=0处理
if (isLeftFirstBath) {
isLeftFirstBath = false;
} else {
sliceInfo_.bIdx++;
}
isUpdateBatchInfo = true;
}
} else {
isUpdateBatchInfo = true;
isFirst_ = false;
}
// 更新与bIdx相关的bStartPos和bSeqUsed
if (isUpdateBatchInfo) {
// SkipInvalidBatch
while (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
if (sliceInfo_.bSeqUsed > 0) {
break;
}
sliceInfo_.bIdx++;
}
if (sliceInfo_.bIdx < batch_size_) {
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
}
}
}
template <typename COMP>
__aicore__ inline SplitCoreSliceInfo& CompressorSplitCoreSliceIterator<COMP>::GetLeftNextCmpSeqCnt()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
// 左边 T轴首次减去T轴最后一块
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(batch_size_ - 1);
sliceInfo_.bStartPos = tools_.GetStartPos(batch_size_ - 1);
// 处理最后一块是中间整块或者尾块的情况
uint32_t lastSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.bSeqUsed) % cmpRatio == 0 ?
cmpRatio :
(sliceInfo_.bStartPos + sliceInfo_.bSeqUsed) % cmpRatio;
// 处理最后一块是头块的情况
if (sliceInfo_.bSeqUsed < cmpRatio) {
lastSeqCnt = sliceInfo_.bSeqUsed;
}
sliceInfo_.sIdx = sliceInfo_.bSeqUsed - lastSeqCnt;
isLeftFirstBath = true;
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
// 头和尾处理否则需要处理的seq等于cmpRatio
if (sliceInfo_.validSeqCnt < cmpRatio) {
sliceInfo_.dealSeqCnt = sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == 0) {
sliceInfo_.dealSeqCnt = cmpRatio - sliceInfo_.headHolderSeqCnt;
}
} else {
sliceInfo_.dealSeqCnt = cmpRatio;
}
sliceInfo_.validSeqCnt = sliceInfo_.dealSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = (sliceInfo_.dealSeqCnt + cmpRatio - 1) / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
// 记录左边第一个块
if (isMaxDealSeqCntFirst) {
sliceInfo_.preFirstSeqCnt = sliceInfo_.dealSeqCnt;
}
return sliceInfo_;
}
template <typename COMP>
__aicore__ inline SplitCoreSliceInfo& CompressorSplitCoreSliceIterator<COMP>::GetRightNextCmpSeqCnt()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (isFirst_) {
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
isFirst_ = false;
}
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt > maxDealSeqCnt_) {
sliceInfo_.validSeqCnt = maxDealSeqCnt_ - sliceInfo_.headHolderSeqCnt;
}
sliceInfo_.tailHolderSeqCnt =
cmpRatio - (sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt) % cmpRatio;
if (sliceInfo_.tailHolderSeqCnt == cmpRatio) {
sliceInfo_.tailHolderSeqCnt = 0;
}
// 头和尾处理否则需要处理的seq等于cmpRatio
if (sliceInfo_.validSeqCnt < cmpRatio) {
sliceInfo_.dealSeqCnt = sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx == 0) {
sliceInfo_.dealSeqCnt = cmpRatio - sliceInfo_.headHolderSeqCnt;
}
} else {
sliceInfo_.dealSeqCnt = cmpRatio;
}
sliceInfo_.validSeqCnt = sliceInfo_.dealSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize = (sliceInfo_.dealSeqCnt + cmpRatio - 1) / cmpRatio;
// 因为是一个batch的数据, 只有最后一个压缩块才可能不需要压缩, 此时sliceInfo_.tailHolderSeqCnt > 0
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize;
if (sliceInfo_.tailHolderSeqCnt > 0) {
sliceInfo_.compressTcSize = sliceInfo_.dealTcSize - 1; // 最后一个压缩块不满时,其不需要压缩
}
return sliceInfo_;
}
struct Vec1SliceInfo : public SliceInfo {
__aicore__ inline Vec1SliceInfo() {};
__aicore__ inline Vec1SliceInfo(uint32_t bIdx, uint32_t sIdx) : SliceInfo(bIdx, sIdx) {};
__aicore__ inline Vec1SliceInfo(uint32_t bIdx, uint32_t sIdx, uint32_t dealedSeqCnt)
: SliceInfo(bIdx, sIdx), dealedSeqCnt(dealedSeqCnt) {};
uint32_t dealedSeqCnt = 0U;
uint32_t dealedTcCnt = 0U;
uint32_t bSeqLength = 0U;
uint32_t compressoredScCnt = 0U;
bool isFirst = false;
bool isLast = false;
};
struct StatisticInfo {
__aicore__ inline StatisticInfo() {};
__aicore__ inline StatisticInfo(uint32_t actualTcCnt, uint32_t dealSeqCnt, uint32_t compressorScCnt)
: actualTcCnt(actualTcCnt), dealSeqCnt(dealSeqCnt), compressorScCnt(compressorScCnt) {};
uint32_t actualTcCnt = 0U;
uint32_t dealSeqCnt = 0U;
uint32_t compressorScCnt = 0U;
};
template <typename COMP>
class CompressorVec1SliceIterator {
public:
__aicore__ inline CompressorVec1SliceIterator(CompressorTools<COMP> &tools) : tools_(tools) {}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx);
__aicore__ inline void Reset(uint32_t bIdx, uint32_t sIdx, uint32_t dealedSeqCnt, uint32_t compressoredScCnt);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetDealedSeqCnt(uint32_t dealedSeqCnt);
__aicore__ inline void SetDealedTcCnt(uint32_t dealedTcCnt);
__aicore__ inline void SetCompressoredScCnt(uint32_t compressoredScCnt);
__aicore__ inline void SetNeedDealTcSize(uint32_t needDealTcSize);
__aicore__ inline void SetNeedDealTcSize(uint32_t needDealTcSize, uint32_t canDealTcSize);
__aicore__ inline uint32_t GetNeedDealTcSize();
__aicore__ inline bool IsEnd();
template <bool IS_STATISTIC = false>
__aicore__ inline void IteratorSlice();
__aicore__ inline Vec1SliceInfo &GetSlice();
template <bool IS_STATISTIC = false>
__aicore__ inline StatisticInfo &FullIteratorSlice();
private:
CompressorTools<COMP> &tools_;
bool isFirst_ = true;
Vec1SliceInfo sliceInfo_{};
StatisticInfo statisticInfo_{};
uint32_t needDealTcSize_ = 0U;
uint32_t batch_size_ = 0U;
};
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx)
{
sliceInfo_.bIdx = bIdx;
sliceInfo_.sIdx = sIdx;
while (tools_.GetSeqLength(sliceInfo_.bIdx) == 0) {
sliceInfo_.bIdx++;
if (sliceInfo_.bIdx == batch_size_) {
sliceInfo_.bIdx = 0;
}
}
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
sliceInfo_.bSeqLength = tools_.GetSeqLength(sliceInfo_.bIdx);
isFirst_ = true;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t sIdx, uint32_t dealedSeqCnt,
uint32_t compressoredScCnt)
{
Reset(bIdx, sIdx);
SetDealedSeqCnt(dealedSeqCnt);
SetCompressoredScCnt(compressoredScCnt);
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetDealedSeqCnt(uint32_t dealedSeqCnt)
{
this->sliceInfo_.dealedSeqCnt = dealedSeqCnt;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetCompressoredScCnt(uint32_t compressoredScCnt)
{
this->sliceInfo_.compressoredScCnt = compressoredScCnt;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetDealedTcCnt(uint32_t dealedTcCnt)
{
this->sliceInfo_.dealedTcCnt = dealedTcCnt;
}
template <typename COMP>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::SetNeedDealTcSize(uint32_t needDealTcSize)
{
this->needDealTcSize_ = needDealTcSize;
}
template <typename COMP>
template <bool IS_STATISTIC>
__aicore__ inline void CompressorVec1SliceIterator<COMP>::IteratorSlice()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if constexpr (IS_STATISTIC) {
statisticInfo_.actualTcCnt += sliceInfo_.dealTcSize;
statisticInfo_.compressorScCnt += sliceInfo_.compressTcSize;
}
needDealTcSize_ -= sliceInfo_.dealTcSize;
sliceInfo_.dealedSeqCnt += sliceInfo_.validSeqCnt;
sliceInfo_.compressoredScCnt += sliceInfo_.compressTcSize;
sliceInfo_.sIdx += sliceInfo_.validSeqCnt;
if (sliceInfo_.sIdx >= sliceInfo_.bSeqUsed) {
do {
uint32_t seqLength = tools_.GetSeqLength(sliceInfo_.bIdx);
if (sliceInfo_.bSeqUsed < seqLength) {
uint32_t nextAlignSIdx = Align(sliceInfo_.bStartPos + sliceInfo_.sIdx, cmpRatio) - sliceInfo_.bStartPos;
sliceInfo_.dealedSeqCnt += nextAlignSIdx - sliceInfo_.sIdx;
uint32_t tcGap = CeilDivT(static_cast<int32_t>(seqLength - nextAlignSIdx),
static_cast<int32_t>(cmpRatio));
if (sliceInfo_.bSeqUsed == 0 && nextAlignSIdx > sliceInfo_.sIdx) {
// 此时bseqused所在压缩块未被纳入计算
tcGap++;
}
sliceInfo_.sIdx = nextAlignSIdx;
if (needDealTcSize_ < tcGap) {
sliceInfo_.dealedSeqCnt += needDealTcSize_ * cmpRatio;
sliceInfo_.sIdx += needDealTcSize_ * cmpRatio;
needDealTcSize_ = 0;
break;
}
sliceInfo_.dealedSeqCnt += seqLength - sliceInfo_.sIdx;
needDealTcSize_ -= tcGap;
}
sliceInfo_.bIdx++;
if (sliceInfo_.bIdx == batch_size_) {
sliceInfo_.bIdx = 0;
}
sliceInfo_.sIdx = 0;
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
} while (sliceInfo_.bSeqUsed == 0);
sliceInfo_.bSeqLength = tools_.GetSeqLength(sliceInfo_.bIdx);
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
}
if (isFirst_) {
isFirst_ = false;
}
}
template <typename COMP>
__aicore__ inline uint32_t CompressorVec1SliceIterator<COMP>::GetNeedDealTcSize()
{
return needDealTcSize_;
}
template <typename COMP>
__aicore__ inline bool CompressorVec1SliceIterator<COMP>::IsEnd()
{
return (needDealTcSize_ == 0);
}
template <typename COMP>
__aicore__ inline Vec1SliceInfo& CompressorVec1SliceIterator<COMP>::GetSlice()
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
if (sliceInfo_.bSeqUsed < sliceInfo_.sIdx) {
sliceInfo_.headHolderSeqCnt = 0;
sliceInfo_.validSeqCnt = 0;
sliceInfo_.tailHolderSeqCnt = 0;
sliceInfo_.dealTcSize = 0;
sliceInfo_.compressTcSize = 0;
} else {
// 计算头部占位行数、有效数据行数、尾部占位行数
sliceInfo_.headHolderSeqCnt = (sliceInfo_.bStartPos + sliceInfo_.sIdx) % cmpRatio;
sliceInfo_.validSeqCnt = sliceInfo_.bSeqUsed - sliceInfo_.sIdx;
if (CeilDivT(sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt, cmpRatio) > needDealTcSize_) {
sliceInfo_.validSeqCnt = needDealTcSize_ * cmpRatio - sliceInfo_.headHolderSeqCnt;
}
uint32_t globalTotalSeqCnt = sliceInfo_.bStartPos + sliceInfo_.sIdx + sliceInfo_.validSeqCnt;
sliceInfo_.tailHolderSeqCnt = Align(globalTotalSeqCnt, cmpRatio) - globalTotalSeqCnt;
// 计算本次可以处理的Tc个数
sliceInfo_.dealTcSize =
(sliceInfo_.headHolderSeqCnt + sliceInfo_.validSeqCnt + sliceInfo_.tailHolderSeqCnt) / cmpRatio;
sliceInfo_.compressTcSize =
(sliceInfo_.headHolderSeqCnt + min(sliceInfo_.validSeqCnt, sliceInfo_.bSeqUsed - sliceInfo_.sIdx)) /
cmpRatio;
}
sliceInfo_.isFirst = isFirst_;
sliceInfo_.isLast =
sliceInfo_.bSeqUsed > sliceInfo_.sIdx &&
CeilDivT(sliceInfo_.headHolderSeqCnt + sliceInfo_.bSeqUsed - sliceInfo_.sIdx, cmpRatio) >= needDealTcSize_;
return sliceInfo_;
}
template <typename COMP>
template <bool IS_STATISTIC>
__aicore__ inline StatisticInfo& CompressorVec1SliceIterator<COMP>::FullIteratorSlice()
{
if constexpr (IS_STATISTIC) {
statisticInfo_ = {0U, 0U, 0U};
Vec1SliceInfo tempSliceInfo = GetSlice();
while (!IsEnd()) {
GetSlice();
IteratorSlice<IS_STATISTIC>();
}
Vec1SliceInfo sliceInfo = GetSlice();
statisticInfo_.dealSeqCnt = sliceInfo.dealedSeqCnt - tempSliceInfo.dealedSeqCnt;
} else {
while (!IsEnd()) {
GetSlice();
IteratorSlice<IS_STATISTIC>();
}
}
return statisticInfo_;
}
struct Vec2SliceInfo{
__aicore__ inline Vec2SliceInfo(){};
__aicore__ inline Vec2SliceInfo(uint32_t bIdx, uint32_t scIdx) : bIdx(bIdx), scIdx(scIdx)
{
}
uint32_t bIdx = 0U;
uint32_t scIdx = 0U;
uint32_t scNum = 0U;
uint32_t remainScCnt = 0U; // 当前batch剩余sc数量
uint32_t bStartPos = 0U;
uint32_t bSeqUsed = 0U;
uint32_t bSeqLength = 0U;
uint32_t dealedScCnt = 0U; // 全局的dealedScCntReset刷新
uint32_t curDealScNum = 0U; // 当前循环处理的sc数量IteratorSlice刷新
uint32_t bOutputScLen = 0U; // BSH场景每个batch填充后的输出长度
uint32_t padScIdx = 0U; // 当前sc输出位置TH场景为全局的dealedScCntBSH场景则为填充后全局的索引Reset刷新
uint32_t loopDealedScCnt = 0U; // 当前迭代已处理的sc数量Reset刷新
};
template <typename COMP>
class CompressorVec2SliceIterator {
public:
__aicore__ inline CompressorVec2SliceIterator(CompressorTools<COMP> &tools) : tools_(tools)
{
}
__aicore__ inline void Reset(uint32_t bIdx, uint32_t scIdx, uint32_t dealedScCnt);
__aicore__ inline void SetMaxBatchSize(uint32_t batch_size);
__aicore__ inline void SetNeedDealScSize(uint32_t needDealScSize);
__aicore__ inline void ResetLoopDealedScCnt();
__aicore__ inline uint32_t GetNeedDealScSize();
__aicore__ inline bool IsEnd();
__aicore__ inline void IteratorSlice();
__aicore__ inline Vec2SliceInfo &GetSlice();
private:
CompressorTools<COMP> &tools_;
Vec2SliceInfo sliceInfo_{};
uint32_t needDealScSize_ = 0U;
uint32_t batch_size_ = 0U;
};
template <typename COMP>
__aicore__ inline void CompressorVec2SliceIterator<COMP>::Reset(uint32_t bIdx, uint32_t scIdx, uint32_t dealedScCnt)
{
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
sliceInfo_.bIdx = bIdx;
sliceInfo_.scIdx = scIdx;
sliceInfo_.dealedScCnt = dealedScCnt;
if constexpr (COMP::xLayout == X_LAYOUT::BSH) {
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.scNum = (sliceInfo_.bStartPos + sliceInfo_.bSeqUsed) / cmpRatio - sliceInfo_.bStartPos / cmpRatio;
sliceInfo_.remainScCnt = sliceInfo_.scNum - sliceInfo_.scIdx;
sliceInfo_.bOutputScLen = CeilDivT(tools_.GetSeqLength(sliceInfo_.bIdx), cmpRatio);
sliceInfo_.padScIdx = sliceInfo_.bIdx * sliceInfo_.bOutputScLen + sliceInfo_.scIdx;
} else {
sliceInfo_.padScIdx = sliceInfo_.dealedScCnt;
}
sliceInfo_.loopDealedScCnt = 0U;
}
template <typename COMP>
__aicore__ inline void CompressorVec2SliceIterator<COMP>::ResetLoopDealedScCnt()
{
sliceInfo_.loopDealedScCnt = 0U;
}
template <typename COMP>
__aicore__ inline void CompressorVec2SliceIterator<COMP>::SetMaxBatchSize(uint32_t batch_size)
{
this->batch_size_ = batch_size;
}
template <typename COMP>
__aicore__ inline void CompressorVec2SliceIterator<COMP>::SetNeedDealScSize(uint32_t needDealScSize)
{
this->needDealScSize_ = needDealScSize;
}
template <typename COMP>
__aicore__ inline void CompressorVec2SliceIterator<COMP>::IteratorSlice()
{
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
sliceInfo_.padScIdx += sliceInfo_.curDealScNum;
} else {
if (needDealScSize_ <= sliceInfo_.remainScCnt) {
sliceInfo_.scIdx += sliceInfo_.curDealScNum;
sliceInfo_.padScIdx += sliceInfo_.curDealScNum;
} else {
uint32_t cmpRatio = tools_.toolParams_.cmpRatio;
sliceInfo_.padScIdx += sliceInfo_.bOutputScLen - sliceInfo_.scIdx;
sliceInfo_.bIdx++;
sliceInfo_.scIdx = 0;
sliceInfo_.bStartPos = tools_.GetStartPos(sliceInfo_.bIdx);
sliceInfo_.bSeqUsed = tools_.GetSeqUsed(sliceInfo_.bIdx);
sliceInfo_.scNum = (sliceInfo_.bStartPos + sliceInfo_.bSeqUsed) / cmpRatio - sliceInfo_.bStartPos / cmpRatio;
}
sliceInfo_.remainScCnt = sliceInfo_.scNum - sliceInfo_.scIdx;
}
sliceInfo_.dealedScCnt += sliceInfo_.curDealScNum;
needDealScSize_ -= sliceInfo_.curDealScNum;
sliceInfo_.loopDealedScCnt += sliceInfo_.curDealScNum;
}
template <typename COMP>
__aicore__ inline uint32_t CompressorVec2SliceIterator<COMP>::GetNeedDealScSize()
{
return needDealScSize_;
}
template <typename COMP>
__aicore__ inline bool CompressorVec2SliceIterator<COMP>::IsEnd()
{
return (needDealScSize_ == 0);
}
template <typename COMP>
__aicore__ inline Vec2SliceInfo &CompressorVec2SliceIterator<COMP>::GetSlice()
{
if constexpr (COMP::xLayout == X_LAYOUT::TH) {
sliceInfo_.curDealScNum = needDealScSize_;
} else {
sliceInfo_.curDealScNum = min(sliceInfo_.remainScCnt, needDealScSize_);
}
return sliceInfo_;
}
} // namespace Compressor
#endif

View File

@@ -0,0 +1,378 @@
/**
 * 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 vf_add.h
* \brief
*/
#ifndef VF_ADD_H
#define VF_ADD_H
#include "kernel_operator.h"
#include <cstdint>
using namespace AscendC;
constexpr uint32_t FLOAT_REP_SIZE = 64;
constexpr uint32_t BTYEALIGNSIZE = 32;
constexpr uint32_t REGSIZE = 256;
constexpr uint32_t HALFCORED = 128;
template <typename T>
struct AddRegList {
MicroAPI::RegTensor<T> vreg;
MicroAPI::RegTensor<T> vregape;
};
template <typename T>
__simd_callee__ void AddVFImpl(__ubuf__ T *inputAddr, __ubuf__ T *apeAddr, AddRegList<T> &regList, uint32_t row,
uint32_t col, uint64_t offset0, uint64_t offset1)
{
uint32_t maskValue = col;
MicroAPI::MaskReg mask = MicroAPI::UpdateMask<T>(maskValue);
MicroAPI::LoadAlign(regList.vreg, inputAddr + offset0);
MicroAPI::LoadAlign(regList.vregape, apeAddr + offset1);
MicroAPI::Add(regList.vreg, regList.vreg, regList.vregape, mask);
MicroAPI::StoreAlign(inputAddr + offset0, regList.vreg, mask);
}
template <bool IS_FIRST, typename T>
__simd_callee__ void MultiAddVFImpl(__ubuf__ T *outputAddr, __ubuf__ T *inputAddr, AddRegList<T> &regList, uint32_t row,
uint32_t col, uint64_t offset, uint32_t repeatNum, uint64_t repeatOffset)
{
uint32_t maskValue = col;
uint32_t initialRepeatIdx = IS_FIRST ? 1 : 0;
__ubuf__ T *initialAddr = IS_FIRST ? inputAddr : outputAddr;
MicroAPI::MaskReg mask = MicroAPI::UpdateMask<T>(maskValue);
MicroAPI::LoadAlign(regList.vreg, initialAddr + offset);
for (uint32_t repeatIdx = initialRepeatIdx; repeatIdx < repeatNum; repeatIdx++) {
uint64_t addOffset = offset + repeatIdx * repeatOffset;
MicroAPI::LoadAlign(regList.vregape, inputAddr + addOffset);
MicroAPI::Add(regList.vreg, regList.vreg, regList.vregape, mask);
}
MicroAPI::StoreAlign(outputAddr + offset, regList.vreg, mask);
}
template <typename T>
__simd_vf__ void Add64VFImpl(__ubuf__ T *inputAddr, __ubuf__ T *apeAddr, uint32_t row, uint32_t col, uint32_t actualCol0, uint32_t actualCol1)
{
AddRegList<T> regList[4];
uint32_t loopTimes = row / 4;
for (uint32_t idx = 0; idx < loopTimes; idx++) {
uint64_t offset0 = idx * 4 * actualCol0;
uint64_t offset1 = idx * 4 * actualCol1;
AddVFImpl(inputAddr, apeAddr, regList[0], row, col, offset0, offset1);
AddVFImpl(inputAddr, apeAddr, regList[1], row, col, offset0 + actualCol0, offset1 + actualCol1);
AddVFImpl(inputAddr, apeAddr, regList[2], row, col, offset0 + 2 * actualCol0, offset1 + 2 * actualCol1);
AddVFImpl(inputAddr, apeAddr, regList[3], row, col, offset0 + 3 * actualCol0, offset1 + 3 * actualCol1);
}
if (row % 4 > 0) {
AddVFImpl(inputAddr, apeAddr, regList[0], row, col, loopTimes * 4 * actualCol0, loopTimes * 4 * actualCol1);
}
if (row % 4 > 1) {
AddVFImpl(inputAddr, apeAddr, regList[1], row, col, (loopTimes * 4 + 1) * actualCol0, (loopTimes * 4 + 1) * actualCol1);
}
if (row % 4 > 2) {
AddVFImpl(inputAddr, apeAddr, regList[2], row, col, (loopTimes * 4 + 2) * actualCol0, (loopTimes * 4 + 2) * actualCol1);
}
}
template <typename T>
__simd_vf__ void Add128VFImpl(__ubuf__ T *inputAddr, __ubuf__ T *apeAddr, uint32_t row, uint32_t actualCol0, uint32_t actualCol1)
{
AddRegList<T> regList[4];
uint32_t loopTimes = row / 2;
for (uint32_t idx = 0; idx < loopTimes; idx++) {
uint64_t offset0 = idx * 2 * actualCol0;
uint64_t offset1 = idx * 2 * actualCol1;
AddVFImpl(inputAddr, apeAddr, regList[0], row, FLOAT_REP_SIZE, offset0, offset1);
AddVFImpl(inputAddr, apeAddr, regList[1], row, FLOAT_REP_SIZE, offset0 + FLOAT_REP_SIZE, offset1 + FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[2], row, FLOAT_REP_SIZE, offset0 + actualCol0, offset1 + actualCol1);
AddVFImpl(inputAddr, apeAddr, regList[3], row, FLOAT_REP_SIZE, offset0 + actualCol0 + FLOAT_REP_SIZE, offset1 + actualCol1 + FLOAT_REP_SIZE);
}
if (row % 2 > 0) {
AddVFImpl(inputAddr, apeAddr, regList[0], row, FLOAT_REP_SIZE, loopTimes * 2 * actualCol0, loopTimes * 2 * actualCol1);
AddVFImpl(inputAddr, apeAddr, regList[1], row, FLOAT_REP_SIZE, loopTimes * 2 * actualCol0 + FLOAT_REP_SIZE, loopTimes * 2 * actualCol1 + FLOAT_REP_SIZE);
}
}
template <typename T>
__simd_vf__ void Add256VFImpl(__ubuf__ T *inputAddr, __ubuf__ T *apeAddr, uint32_t row, uint32_t actualCol0, uint32_t actualCol1)
{
AddRegList<T> regList[4];
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
for (uint32_t idx = 0; idx < row; idx++) {
uint64_t offset0 = idx * actualCol0;
uint64_t offset1 = idx * actualCol1;
AddVFImpl(inputAddr, apeAddr, regList[0], row, FLOAT_REP_SIZE, offset0, offset1);
AddVFImpl(inputAddr, apeAddr, regList[1], row, FLOAT_REP_SIZE, offset0 + FLOAT_REP_SIZE, offset1 + FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[2], row, FLOAT_REP_SIZE, offset0 + 2 * FLOAT_REP_SIZE, offset1 + 2 * FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[3], row, FLOAT_REP_SIZE, offset0 + 3 * FLOAT_REP_SIZE, offset1 + 3 * FLOAT_REP_SIZE);
}
}
template <typename T>
__simd_vf__ void Add512VFImpl(__ubuf__ T *inputAddr, __ubuf__ T *apeAddr, uint32_t row, uint32_t actualCol0, uint32_t actualCol1)
{
AddRegList<T> regList[8];
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
for (uint32_t idx = 0; idx < row; idx++) {
uint64_t offset0 = idx * actualCol0;
uint64_t offset1 = idx * actualCol1;
AddVFImpl(inputAddr, apeAddr, regList[0], row, FLOAT_REP_SIZE, offset0, offset1);
AddVFImpl(inputAddr, apeAddr, regList[1], row, FLOAT_REP_SIZE, offset0 + FLOAT_REP_SIZE, offset1 + FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[2], row, FLOAT_REP_SIZE, offset0 + 2 * FLOAT_REP_SIZE, offset1 + 2 * FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[3], row, FLOAT_REP_SIZE, offset0 + 3 * FLOAT_REP_SIZE, offset1 + 3 * FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[4], row, FLOAT_REP_SIZE, offset0 + 4 * FLOAT_REP_SIZE, offset1 + 4 * FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[5], row, FLOAT_REP_SIZE, offset0 + 5 * FLOAT_REP_SIZE, offset1 + 5 * FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[6], row, FLOAT_REP_SIZE, offset0 + 6 * FLOAT_REP_SIZE, offset1 + 6 * FLOAT_REP_SIZE);
AddVFImpl(inputAddr, apeAddr, regList[7], row, FLOAT_REP_SIZE, offset0 + 7 * FLOAT_REP_SIZE, offset1 + 7 * FLOAT_REP_SIZE);
}
}
template <bool IS_FIRST, typename T>
__simd_vf__ void MultiAdd64VFImpl(__ubuf__ T *outputAddr, __ubuf__ T *inputAddr, uint32_t row, uint32_t col,
uint32_t actualCol, uint32_t repeatNum, uint64_t repeatOffset)
{
AddRegList<T> regList[4];
uint32_t loopTimes = row / 4;
uint32_t maskValue = col;
uint32_t initialRepeatIdx = IS_FIRST ? 1 : 0;
__ubuf__ T *initialAddr = IS_FIRST ? inputAddr : outputAddr;
MicroAPI::MaskReg mask = MicroAPI::UpdateMask<T>(maskValue);
for (uint32_t idx = 0; idx < loopTimes; idx++) {
uint64_t offset = idx * 4 * actualCol;
MicroAPI::LoadAlign(regList[0].vreg, initialAddr + offset);
MicroAPI::LoadAlign(regList[1].vreg, initialAddr + offset + actualCol);
MicroAPI::LoadAlign(regList[2].vreg, initialAddr + offset + 2 * actualCol);
MicroAPI::LoadAlign(regList[3].vreg, initialAddr + offset + 3 * actualCol);
for (uint32_t repeatIdx = initialRepeatIdx; repeatIdx < repeatNum; repeatIdx++) {
uint64_t addOffset = offset + repeatIdx * repeatOffset;
MicroAPI::LoadAlign(regList[0].vregape, inputAddr + addOffset);
MicroAPI::LoadAlign(regList[1].vregape, inputAddr + addOffset + actualCol);
MicroAPI::LoadAlign(regList[2].vregape, inputAddr + addOffset + 2 * actualCol);
MicroAPI::LoadAlign(regList[3].vregape, inputAddr + addOffset + 3 * actualCol);
MicroAPI::Add(regList[0].vreg, regList[0].vreg, regList[0].vregape, mask);
MicroAPI::Add(regList[1].vreg, regList[1].vreg, regList[1].vregape, mask);
MicroAPI::Add(regList[2].vreg, regList[2].vreg, regList[2].vregape, mask);
MicroAPI::Add(regList[3].vreg, regList[3].vreg, regList[3].vregape, mask);
}
MicroAPI::StoreAlign(outputAddr + offset, regList[0].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + actualCol, regList[1].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 2 * actualCol, regList[2].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 3 * actualCol, regList[3].vreg, mask);
}
if (row % 4 > 0) {
MultiAddVFImpl<IS_FIRST, T>(outputAddr, inputAddr, regList[0], row, col, loopTimes * 4 * actualCol, repeatNum,
repeatOffset);
}
if (row % 4 > 1) {
MultiAddVFImpl<IS_FIRST, T>(outputAddr, inputAddr, regList[1], row, col, (loopTimes * 4 + 1) * actualCol,
repeatNum, repeatOffset);
}
if (row % 4 > 2) {
MultiAddVFImpl<IS_FIRST, T>(outputAddr, inputAddr, regList[2], row, col, (loopTimes * 4 + 2) * actualCol,
repeatNum, repeatOffset);
}
}
template <bool IS_FIRST, typename T>
__simd_vf__ void MultiAdd128VFImpl(__ubuf__ T *outputAddr, __ubuf__ T *inputAddr, uint32_t row, uint32_t col,
uint32_t actualCol, uint32_t repeatNum, uint64_t repeatOffset)
{
AddRegList<T> regList[4];
uint32_t loopTimes = row / 2;
uint32_t initialRepeatIdx = IS_FIRST ? 1 : 0;
__ubuf__ T *initialAddr = IS_FIRST ? inputAddr : outputAddr;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
for (uint32_t idx = 0; idx < loopTimes; idx++) {
uint64_t offset = idx * actualCol * 2;
MicroAPI::LoadAlign(regList[0].vreg, initialAddr + offset);
MicroAPI::LoadAlign(regList[1].vreg, initialAddr + offset + FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[2].vreg, initialAddr + offset + actualCol);
MicroAPI::LoadAlign(regList[3].vreg, initialAddr + offset + actualCol + FLOAT_REP_SIZE);
for (uint32_t repeatIdx = initialRepeatIdx; repeatIdx < repeatNum; repeatIdx++) {
uint64_t addOffset = offset + repeatIdx * repeatOffset;
MicroAPI::LoadAlign(regList[0].vregape, inputAddr + addOffset);
MicroAPI::LoadAlign(regList[1].vregape, inputAddr + addOffset + FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[2].vregape, inputAddr + addOffset + actualCol);
MicroAPI::LoadAlign(regList[3].vregape, inputAddr + addOffset + actualCol + FLOAT_REP_SIZE);
MicroAPI::Add(regList[0].vreg, regList[0].vreg, regList[0].vregape, mask);
MicroAPI::Add(regList[1].vreg, regList[1].vreg, regList[1].vregape, mask);
MicroAPI::Add(regList[2].vreg, regList[2].vreg, regList[2].vregape, mask);
MicroAPI::Add(regList[3].vreg, regList[3].vreg, regList[3].vregape, mask);
}
MicroAPI::StoreAlign(outputAddr + offset, regList[0].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + FLOAT_REP_SIZE, regList[1].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + actualCol, regList[2].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + actualCol + FLOAT_REP_SIZE, regList[3].vreg, mask);
}
if (row % 2 > 0) {
MultiAddVFImpl<IS_FIRST, T>(outputAddr, inputAddr, regList[0], row, col, loopTimes * 2 * actualCol, repeatNum,
repeatOffset);
MultiAddVFImpl<IS_FIRST, T>(outputAddr, inputAddr, regList[1], row, col,
loopTimes * 2 * actualCol + FLOAT_REP_SIZE, repeatNum, repeatOffset);
}
}
template <bool IS_FIRST, typename T>
__simd_vf__ void MultiAdd256VFImpl(__ubuf__ T *outputAddr, __ubuf__ T *inputAddr, uint32_t row,
uint32_t actualCol, uint32_t repeatNum, uint64_t repeatOffset)
{
AddRegList<T> regList[4];
uint32_t loopTimes = row;
uint32_t initialRepeatIdx = IS_FIRST ? 1 : 0;
__ubuf__ T *initialAddr = IS_FIRST ? inputAddr : outputAddr;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
for (uint32_t idx = 0; idx < loopTimes; idx++) {
uint64_t offset = idx * actualCol;
MicroAPI::LoadAlign(regList[0].vreg, initialAddr + offset);
MicroAPI::LoadAlign(regList[1].vreg, initialAddr + offset + FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[2].vreg, initialAddr + offset + 2 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[3].vreg, initialAddr + offset + 3 * FLOAT_REP_SIZE);
for (uint32_t repeatIdx = initialRepeatIdx; repeatIdx < repeatNum; repeatIdx++) {
uint64_t addOffset = offset + repeatIdx * repeatOffset;
MicroAPI::LoadAlign(regList[0].vregape, inputAddr + addOffset);
MicroAPI::LoadAlign(regList[1].vregape, inputAddr + addOffset + FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[2].vregape, inputAddr + addOffset + 2 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[3].vregape, inputAddr + addOffset + 3 * FLOAT_REP_SIZE);
MicroAPI::Add(regList[0].vreg, regList[0].vreg, regList[0].vregape, mask);
MicroAPI::Add(regList[1].vreg, regList[1].vreg, regList[1].vregape, mask);
MicroAPI::Add(regList[2].vreg, regList[2].vreg, regList[2].vregape, mask);
MicroAPI::Add(regList[3].vreg, regList[3].vreg, regList[3].vregape, mask);
}
MicroAPI::StoreAlign(outputAddr + offset, regList[0].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + FLOAT_REP_SIZE, regList[1].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 2 * FLOAT_REP_SIZE, regList[2].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 3 * FLOAT_REP_SIZE, regList[3].vreg, mask);
}
}
template <bool IS_FIRST, typename T>
__simd_vf__ void MultiAdd512VFImpl(__ubuf__ T *outputAddr, __ubuf__ T *inputAddr, uint32_t row,
uint32_t actualCol, uint32_t repeatNum, uint64_t repeatOffset)
{
AddRegList<T> regList[8];
uint32_t loopTimes = row;
uint32_t initialRepeatIdx = IS_FIRST ? 1 : 0;
__ubuf__ T *initialAddr = IS_FIRST ? inputAddr : outputAddr;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
for (uint32_t idx = 0; idx < loopTimes; idx++) {
uint64_t offset = idx * actualCol;
MicroAPI::LoadAlign(regList[0].vreg, initialAddr + offset);
MicroAPI::LoadAlign(regList[1].vreg, initialAddr + offset + FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[2].vreg, initialAddr + offset + 2 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[3].vreg, initialAddr + offset + 3 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[4].vreg, initialAddr + offset + 4 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[5].vreg, initialAddr + offset + 5 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[6].vreg, initialAddr + offset + 6 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[7].vreg, initialAddr + offset + 7 * FLOAT_REP_SIZE);
for (uint32_t repeatIdx = initialRepeatIdx; repeatIdx < repeatNum; repeatIdx++) {
uint64_t addOffset = offset + repeatIdx * row * actualCol;
MicroAPI::LoadAlign(regList[0].vregape, inputAddr + addOffset);
MicroAPI::LoadAlign(regList[1].vregape, inputAddr + addOffset + FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[2].vregape, inputAddr + addOffset + 2 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[3].vregape, inputAddr + addOffset + 3 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[4].vregape, inputAddr + addOffset + 4 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[5].vregape, inputAddr + addOffset + 5 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[6].vregape, inputAddr + addOffset + 6 * FLOAT_REP_SIZE);
MicroAPI::LoadAlign(regList[7].vregape, inputAddr + addOffset + 7 * FLOAT_REP_SIZE);
MicroAPI::Add(regList[0].vreg, regList[0].vreg, regList[0].vregape, mask);
MicroAPI::Add(regList[1].vreg, regList[1].vreg, regList[1].vregape, mask);
MicroAPI::Add(regList[2].vreg, regList[2].vreg, regList[2].vregape, mask);
MicroAPI::Add(regList[3].vreg, regList[3].vreg, regList[3].vregape, mask);
MicroAPI::Add(regList[4].vreg, regList[4].vreg, regList[4].vregape, mask);
MicroAPI::Add(regList[5].vreg, regList[5].vreg, regList[5].vregape, mask);
MicroAPI::Add(regList[6].vreg, regList[6].vreg, regList[6].vregape, mask);
MicroAPI::Add(regList[7].vreg, regList[7].vreg, regList[7].vregape, mask);
}
MicroAPI::StoreAlign(outputAddr + offset, regList[0].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + FLOAT_REP_SIZE, regList[1].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 2 * FLOAT_REP_SIZE, regList[2].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 3 * FLOAT_REP_SIZE, regList[3].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 4 * FLOAT_REP_SIZE, regList[4].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 5 * FLOAT_REP_SIZE, regList[5].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 6 * FLOAT_REP_SIZE, regList[6].vreg, mask);
MicroAPI::StoreAlign(outputAddr + offset + 7 * FLOAT_REP_SIZE, regList[7].vreg, mask);
}
}
/**
* @brief AddVF 输入与apt相加
* @param rightLocal 输出tensor []
* @param leftLocal 输入tensor [row, col]
* @param aptLocal apt输入tensor [r]
* @param apeIdx ape起始位置
* @param d coff*d为ape的D轴大小
* @param coreSplitD scoreleft大小coff*coreSplitD为总大小
* @param coreSplitS 核间d轴切分大小
*/
template <typename T>
__aicore__ inline void AddVF(const LocalTensor<T> &scoreLocal, const LocalTensor<T> &apeLocal, uint32_t row,
uint32_t col, uint32_t actualCol0, uint32_t actualCol1)
{
__ubuf__ T *scoreAddr = (__ubuf__ T *)scoreLocal.GetPhyAddr();
__ubuf__ T *apeAddr = (__ubuf__ T *)apeLocal.GetPhyAddr();
if (col <= 64) {
Add64VFImpl<T>(scoreAddr, apeAddr, row, col, actualCol0, actualCol1);
} else if (col == 128) {
Add128VFImpl<T>(scoreAddr, apeAddr, row, actualCol0, actualCol1);
} else if (col == 256) {
Add256VFImpl<T>(scoreAddr, apeAddr, row, actualCol0, actualCol1);
} else if (col == 512) {
Add512VFImpl<T>(scoreAddr, apeAddr, row, actualCol0, actualCol1);
}
}
template <typename T>
__aicore__ inline void AddVF(const LocalTensor<T> &scoreLocal, const LocalTensor<T> &apeLocal, uint32_t row,
uint32_t col, uint32_t actualCol)
{
__ubuf__ T *scoreAddr = (__ubuf__ T *)scoreLocal.GetPhyAddr();
__ubuf__ T *apeAddr = (__ubuf__ T *)apeLocal.GetPhyAddr();
if (col <= 64) {
Add64VFImpl<T>(scoreAddr, apeAddr, row, col, actualCol, actualCol);
} else if (col == 128) {
Add128VFImpl<T>(scoreAddr, apeAddr, row, actualCol, actualCol);
} else if (col == 256) {
Add256VFImpl<T>(scoreAddr, apeAddr, row, actualCol, actualCol);
} else if (col == 512) {
Add512VFImpl<T>(scoreAddr, apeAddr, row, actualCol, actualCol);
}
}
template <bool IS_FIRST, typename T>
__aicore__ inline void MultiAddVF(const LocalTensor<T> &outputLocal, const LocalTensor<T> &inputLocal, uint32_t row,
uint32_t col, uint32_t actualCol, uint32_t repeatNum, uint64_t repeatOffset)
{
__ubuf__ T *outputAddr = (__ubuf__ T *)outputLocal.GetPhyAddr();
__ubuf__ T *inputAddr = (__ubuf__ T *)inputLocal.GetPhyAddr();
if (col <= 64) {
MultiAdd64VFImpl<IS_FIRST, T>(outputAddr, inputAddr, row, col, actualCol, repeatNum, repeatOffset);
} else if (col == 128) {
MultiAdd128VFImpl<IS_FIRST, T>(outputAddr, inputAddr, row, col, actualCol, repeatNum, repeatOffset);
} else if (col == 256) {
MultiAdd256VFImpl<IS_FIRST, T>(outputAddr, inputAddr, row, actualCol, repeatNum, repeatOffset);
} else if (col == 512) {
MultiAdd512VFImpl<IS_FIRST, T>(outputAddr, inputAddr, row, actualCol, repeatNum, repeatOffset);
}
}
#endif

View File

@@ -0,0 +1,318 @@
/**
 * 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 vf_mul.h
* \brief
*/
#ifndef VF_MUL_H
#define VF_MUL_H
#include "kernel_operator.h"
#include <cstdint>
using namespace AscendC;
constexpr uint32_t FLOATBYTE = 4;
constexpr uint32_t baseD8 = 8;
constexpr uint32_t baseD16 = 16;
constexpr uint32_t baseD32 = 32;
constexpr uint32_t baseD64 = 64;
constexpr uint32_t baseD128 = 128;
constexpr uint32_t baseD256 = 256;
constexpr uint32_t baseD512 = 512;
template <typename T>
__simd_callee__ inline T SimdCeilDivT(T num1, T num2)
{
if (num2 == 0) {
return static_cast<T>(0);
}
return (num1 + num2 - 1) / num2;
}
template <typename T>
struct ReduceMulRegList {
MicroAPI::RegTensor<T> vreg0;
MicroAPI::RegTensor<T> vreg1;
MicroAPI::RegTensor<T> vregMul;
MicroAPI::RegTensor<T> vregSum;
};
template <typename T>
__simd_callee__ void LoadMulAddVFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, ReduceMulRegList<T> &regList, uint64_t offset, uint32_t maskValue)
{
MicroAPI::MaskReg mask = MicroAPI::UpdateMask<T>(maskValue);
MicroAPI::LoadAlign(regList.vreg0, kvAddr + offset);
MicroAPI::LoadAlign(regList.vreg1, scoreAddr + offset);
MicroAPI::Mul(regList.vregMul, regList.vreg0, regList.vreg1, mask);
MicroAPI::Add(regList.vregSum, regList.vregSum, regList.vregMul, mask);
}
template <typename T>
__simd_vf__ void MulReduceSumbase8VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList;
MicroAPI::RegTensor<T> vregSum0;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg maskL32 = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL32>();
MicroAPI::MaskReg maskL16 = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL16>();
MicroAPI::MaskReg maskL8 = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL8>();
MicroAPI::MaskReg maskH32;
MicroAPI::MaskReg maskH48;
MicroAPI::MaskReg maskH56;
MicroAPI::Not(maskH48, maskL16, mask);
MicroAPI::Not(maskH32, maskL32, mask);
MicroAPI::Not(maskH56, maskL8, mask);
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList.vregSum, 0, mask);
// 当前仅支持coff * cmpRatio为2的幂的情况
for (uint32_t rLoop = 0; rLoop < SimdCeilDivT(rCnt, 8U); rLoop++) {
uint32_t dealLen = min((rCnt - rLoop * 8) * baseD, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList, offset, dealLen);
offset += dealLen;
}
// 64 -> 32
MicroAPI::Squeeze<T, AscendC::MicroAPI::GatherMaskMode::NO_STORE_REG>(vregSum0, regList.vregSum, maskH32);
MicroAPI::Add(regList.vregSum, regList.vregSum, vregSum0, maskL32);
// 32 -> 16
MicroAPI::Squeeze<T, AscendC::MicroAPI::GatherMaskMode::NO_STORE_REG>(vregSum0, regList.vregSum, maskH48);
MicroAPI::Add(regList.vregSum, regList.vregSum, vregSum0, maskL16);
// 16 -> 8
MicroAPI::Squeeze<T, AscendC::MicroAPI::GatherMaskMode::NO_STORE_REG>(vregSum0, regList.vregSum, maskH56);
MicroAPI::Add(regList.vregSum, regList.vregSum, vregSum0, maskL8);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList.vregSum, maskL8);
}
}
template <typename T>
__simd_vf__ void MulReduceSumbase16VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList;
MicroAPI::RegTensor<T> vregSum0;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg maskL32 = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL32>();
MicroAPI::MaskReg maskL16 = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL16>();
MicroAPI::MaskReg maskH32;
MicroAPI::MaskReg maskH48;
MicroAPI::Not(maskH48, maskL16, mask);
MicroAPI::Not(maskH32, maskL32, mask);
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList.vregSum, 0, mask);
// 当前仅支持coff * cmpRatio为2的幂的情况
for (uint32_t rLoop = 0; rLoop < SimdCeilDivT(rCnt, 4U); rLoop++) {
uint32_t dealLen = min((rCnt - rLoop * 4) * baseD, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList, offset, dealLen);
offset += dealLen;
}
// 64 -> 32
MicroAPI::Squeeze<T, AscendC::MicroAPI::GatherMaskMode::NO_STORE_REG>(vregSum0, regList.vregSum, maskH32);
MicroAPI::Add(regList.vregSum, regList.vregSum, vregSum0, maskL32);
// 32 -> 16
MicroAPI::Squeeze<T, AscendC::MicroAPI::GatherMaskMode::NO_STORE_REG>(vregSum0, regList.vregSum, maskH48);
MicroAPI::Add(regList.vregSum, regList.vregSum, vregSum0, maskL16);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList.vregSum, maskL16);
}
}
template <typename T>
__simd_vf__ void MulReduceSumbase32VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList;
MicroAPI::RegTensor<T> vregSum0;
MicroAPI::RegTensor<T> vregSum1;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg maskL32 = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL32>();
MicroAPI::MaskReg maskH32;
MicroAPI::Not(maskH32, maskL32, mask);
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList.vregSum, 0, mask);
// 当前仅支持coff * cmpRatio为2的幂的情况
for (uint32_t rLoop = 0; rLoop < SimdCeilDivT(rCnt, 2U); rLoop++) {
uint32_t dealLen = min((rCnt - rLoop * 2) * baseD, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList, offset, dealLen);
offset += dealLen;
}
// 64 -> 32
MicroAPI::Squeeze<T, AscendC::MicroAPI::GatherMaskMode::NO_STORE_REG>(vregSum0, regList.vregSum, maskH32);
MicroAPI::Add(regList.vregSum, regList.vregSum, vregSum0, maskL32);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList.vregSum, maskL32);
}
}
template <typename T>
__simd_vf__ void MulReduceSumbase64VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList;
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList.vregSum, 0, mask);
for (uint32_t rLoop = 0; rLoop < rCnt; rLoop++) {
LoadMulAddVFImpl(kvAddr, scoreAddr, regList, offset, baseD64);
offset += baseD;
}
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList.vregSum, mask);
}
}
template <typename T>
__simd_vf__ void MulReduceSumbase128VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList[2];
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList[0].vregSum, 0, mask);
MicroAPI::Duplicate(regList[1].vregSum, 0, mask);
for (uint32_t rLoop = 0; rLoop < rCnt; rLoop++) {
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[0], offset, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[1], offset + baseD64, baseD64);
offset += baseD;
}
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList[0].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + baseD64, regList[1].vregSum, mask);
}
}
template <typename T>
__simd_vf__ void MulReduceSumbase256VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList[4];
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList[0].vregSum, 0, mask);
MicroAPI::Duplicate(regList[1].vregSum, 0, mask);
MicroAPI::Duplicate(regList[2].vregSum, 0, mask);
MicroAPI::Duplicate(regList[3].vregSum, 0, mask);
for (uint32_t rLoop = 0; rLoop < rCnt; rLoop++) {
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[0], offset, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[1], offset + baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[2], offset + 2 * baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[3], offset + 3 * baseD64, baseD64);
offset += baseD;
}
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList[0].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + baseD64, regList[1].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 2 * baseD64, regList[2].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 3 * baseD64, regList[3].vregSum, mask);
}
}
template <typename T>
__simd_vf__ void MulReduceSumbase512VFImpl(__ubuf__ T *kvAddr, __ubuf__ T *scoreAddr, __ubuf__ T *outputAddr,
const uint32_t coff, const uint32_t cmpRatio, const uint32_t scLoopCnt,
const uint32_t baseD)
{
ReduceMulRegList<T> regList[8];
MicroAPI::MaskReg mask = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
uint32_t offset = 0;
uint32_t rCnt = coff * cmpRatio;
for (uint32_t scLoop = 0; scLoop < scLoopCnt; scLoop++) {
MicroAPI::Duplicate(regList[0].vregSum, 0, mask);
MicroAPI::Duplicate(regList[1].vregSum, 0, mask);
MicroAPI::Duplicate(regList[2].vregSum, 0, mask);
MicroAPI::Duplicate(regList[3].vregSum, 0, mask);
MicroAPI::Duplicate(regList[4].vregSum, 0, mask);
MicroAPI::Duplicate(regList[5].vregSum, 0, mask);
MicroAPI::Duplicate(regList[6].vregSum, 0, mask);
MicroAPI::Duplicate(regList[7].vregSum, 0, mask);
for (uint32_t rLoop = 0; rLoop < rCnt; rLoop++) {
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[0], offset, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[1], offset + baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[2], offset + 2 * baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[3], offset + 3 * baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[4], offset + 4 * baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[5], offset + 5 * baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[6], offset + 6 * baseD64, baseD64);
LoadMulAddVFImpl(kvAddr, scoreAddr, regList[7], offset + 7 * baseD64, baseD64);
offset += baseD;
}
MicroAPI::StoreAlign(outputAddr + scLoop * baseD, regList[0].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + baseD64, regList[1].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 2 * baseD64, regList[2].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 3 * baseD64, regList[3].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 4 * baseD64, regList[4].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 5 * baseD64, regList[5].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 6 * baseD64, regList[6].vregSum, mask);
MicroAPI::StoreAlign(outputAddr + scLoop * baseD + 7 * baseD64, regList[7].vregSum, mask);
}
}
/**
* @brief MulReduceSumbaseVF 包含mul和reducesum
* @param outputLocal 输出tensor []
* @param coff
* @param cmpRatio 压缩块大小
* @param baseD 核内d轴切分大小
* @param scLoopCnt sc数,
*/
// 当前仅支持coff * cmpRatio为2的幂的情况
template <typename T>
__aicore__ inline void MulReduceSumbaseVF(const LocalTensor<T> &kvLocal, const LocalTensor<T> &scoreLocal,
const LocalTensor<T> &outputLocal, const uint32_t coff, const uint32_t cmpRatio,
const uint32_t baseD, const uint32_t scLoopCnt)
{
__ubuf__ T *kvAddr = (__ubuf__ T *)kvLocal.GetPhyAddr();
__ubuf__ T *scoreAddr = (__ubuf__ T *)scoreLocal.GetPhyAddr();
__ubuf__ T *outputAddr = (__ubuf__ T *)outputLocal.GetPhyAddr();
if (baseD == baseD8) {
MulReduceSumbase8VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
} else if (baseD == baseD16) {
MulReduceSumbase16VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
} else if (baseD == baseD32) {
MulReduceSumbase32VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
} else if (baseD == baseD64) {
MulReduceSumbase64VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
} else if (baseD == baseD128) {
MulReduceSumbase128VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
} else if (baseD == baseD256) {
MulReduceSumbase256VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
} else if (baseD == baseD512) {
MulReduceSumbase512VFImpl(kvAddr, scoreAddr, outputAddr, coff, cmpRatio, scLoopCnt, baseD);
}
}
#endif

View File

@@ -0,0 +1,95 @@
/**
 * 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 vf_rms_norm.h
* \brief
*/
#ifndef VF_RMS_NORM_H
#define VF_RMS_NORM_H
#include "kernel_tensor.h"
//repeatTimes——D轴的分块数
template <typename T, typename GammaType>
__simd_vf__ void RmsNormVFImpl(__ubuf__ T * inputBuf, __ubuf__ GammaType * gammaBuf, __ubuf__ T * outputBuf,
uint32_t repeatTimes, float reciprocal, float epsilon)
{
MicroAPI::RegTensor<T> vregSum;
MicroAPI::RegTensor<T> vregSumReduce;
MicroAPI::RegTensor<T> vregDiv;
MicroAPI::RegTensor<T> vregSquareRoot;
MicroAPI::MaskReg maskAll = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::ALL>();
MicroAPI::MaskReg maskFirst = MicroAPI::CreateMask<T, MicroAPI::MaskPattern::VL1>();
static constexpr MicroAPI::CastTrait castTraitB162B32 = {MicroAPI::RegLayout::ZERO,
MicroAPI::SatMode::UNKNOWN, MicroAPI::MaskMergeMode::ZEROING, RoundMode::UNKNOWN};
MicroAPI::Duplicate<T,T>(vregSum, 0.0f);
for(uint32_t i = 0; i < repeatTimes; ++i){
MicroAPI::RegTensor<T> vregX;
MicroAPI::RegTensor<T> vregXSquare;
uint64_t loopOffset = i * FLOAT_REP_SIZE;
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregX, inputBuf + loopOffset);
MicroAPI::Mul(vregXSquare, vregX, vregX, maskAll);
MicroAPI::Add(vregSum, vregXSquare, vregSum, maskAll);
}
MicroAPI::Reduce<MicroAPI::ReduceType::SUM, T, T, MicroAPI::MaskMergeMode::ZEROING>(vregSumReduce, vregSum, maskAll);
MicroAPI::Muls<T, T, MicroAPI::MaskMergeMode::ZEROING>(vregSumReduce, vregSumReduce, reciprocal, maskFirst);
MicroAPI::Adds<T, T, MicroAPI::MaskMergeMode::ZEROING>(vregSumReduce, vregSumReduce, epsilon, maskFirst);
MicroAPI::Sqrt(vregSquareRoot, vregSumReduce, maskFirst);
MicroAPI::Duplicate<T, MicroAPI::HighLowPart::LOWEST, MicroAPI::MaskMergeMode::ZEROING>(vregDiv, vregSquareRoot, maskAll);
for(uint32_t i = 0; i < repeatTimes; ++i){
MicroAPI::RegTensor<T> vregX;
MicroAPI::RegTensor<T> vregGammaCast;
uint16_t loopOffset = i * FLOAT_REP_SIZE;
MicroAPI::LoadAlign<T, MicroAPI::LoadDist::DIST_NORM>(vregX, inputBuf + loopOffset);
MicroAPI::LoadAlign<GammaType, MicroAPI::LoadDist::DIST_NORM>(vregGammaCast, gammaBuf + loopOffset);
MicroAPI::Div(vregX, vregX, vregDiv, maskAll);
MicroAPI::Mul(vregX, vregX, vregGammaCast, maskAll);
MicroAPI::StoreAlign<T, MicroAPI::StoreDist::DIST_NORM>(outputBuf + loopOffset, vregX, maskAll);
}
}
/**
* @brief RmsNormVF 对一行进行rmsnorm
* @param outputLocal 输出tensor [row, col]row目前均为1
* @param inputLocal 输入tensor [row, col]
* @param gammaLocal gamma参数tensor [row, col]
* @param rmsNormParams rmsNrom计算所需系数包括
row 行数 1
col 列数对应headSizeCq或headSizeCkv
reciprocal 1/N
epsilon防止除零极小数
*/
template <typename T, typename GammaType>
__aicore__ inline void RmsNormVF(const LocalTensor<T> outputLocal, const LocalTensor<T> inputLocal, const LocalTensor<GammaType> gammaLocal,
float reciprocal, float epsilon, uint32_t row, uint32_t col)
{
uint32_t cnt = row * col;
uint32_t repeatTimes = (cnt + FLOAT_REP_SIZE - 1) / FLOAT_REP_SIZE;
__ubuf__ T * inputBuf = (__ubuf__ T *)inputLocal.GetPhyAddr();
__ubuf__ GammaType * gammaBuf = (__ubuf__ GammaType *)gammaLocal.GetPhyAddr();
__ubuf__ T * outputBuf = (__ubuf__ T *)outputLocal.GetPhyAddr();
RmsNormVFImpl<T, GammaType>(inputBuf, gammaBuf, outputBuf, repeatTimes, reciprocal, epsilon);
}
#endif

View File

@@ -0,0 +1,158 @@
/**
 * 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 vf_rope.h
* \brief
*/
#ifndef VF_ROPE_H
#define VF_ROPE_H
#include "kernel_operator.h"
#include "../compressor_comm.h"
using namespace AscendC;
constexpr MicroAPI::CastTrait castTraitB162B32 = {
MicroAPI::RegLayout::ZERO,
MicroAPI::SatMode::UNKNOWN,
MicroAPI::MaskMergeMode::ZEROING,
RoundMode::UNKNOWN,
};
constexpr MicroAPI::CastTrait castTraitB322B16 = {
MicroAPI::RegLayout::ZERO,
MicroAPI::SatMode::NO_SAT,
MicroAPI::MaskMergeMode::ZEROING,
RoundMode::CAST_RINT,
};
template <typename T, typename ROPET>
__simd_vf__ void HalfModeRopeVF(__ubuf__ T *sinUb, __ubuf__ T *cosUb, __ubuf__ T *inUb, __ubuf__ ROPET *outUb,
uint32_t row, uint32_t col, uint32_t actualCol, uint64_t baseAddr)
{
MicroAPI::RegTensor<T> vregCos;
MicroAPI::RegTensor<T> vregHalfCos;
MicroAPI::RegTensor<T> vregSin;
MicroAPI::RegTensor<T> vregHalfSin;
MicroAPI::RegTensor<T> vregIn;
MicroAPI::RegTensor<T> vregHalfIn;
MicroAPI::RegTensor<T> vregOut;
MicroAPI::RegTensor<T> vregHalfOut;
MicroAPI::RegTensor<T> vregCastIn;
MicroAPI::RegTensor<ROPET> vregOutBf16;
MicroAPI::RegTensor<ROPET> vregOutHalfBf16;
MicroAPI::RegTensor<ROPET> vregCastOut;
uint32_t maskValue = col / 2;
MicroAPI::MaskReg mask = MicroAPI::UpdateMask<T>(maskValue);
uint32_t halfCol = col / 2;
for (uint32_t rIdx = 0; rIdx < row; rIdx++) {
__ubuf__ T *curSinUb = sinUb + rIdx * col;
__ubuf__ T *curCosUb = cosUb + rIdx * col;
__ubuf__ T *curInUb = inUb + rIdx * actualCol;
__ubuf__ ROPET *curOutUb = outUb + rIdx * actualCol;
MicroAPI::DataCopy(vregIn, curInUb + baseAddr);
MicroAPI::DataCopy(vregHalfIn, curInUb + baseAddr + halfCol);
MicroAPI::DataCopy(vregCos, curCosUb);
MicroAPI::DataCopy(vregHalfCos, curCosUb + halfCol);
MicroAPI::DataCopy(vregSin, curSinUb);
MicroAPI::DataCopy(vregHalfSin, curSinUb + halfCol);
MicroAPI::Mul(vregSin, vregSin, vregHalfIn, mask);
MicroAPI::Mul(vregHalfSin, vregHalfSin, vregIn, mask);
MicroAPI::Mul(vregCos, vregCos, vregIn, mask);
MicroAPI::Sub(vregOut, vregCos, vregSin, mask);
MicroAPI::Mul(vregHalfCos, vregHalfCos, vregHalfIn, mask);
MicroAPI::Add(vregHalfOut, vregHalfSin, vregHalfCos, mask);
MicroAPI::Cast<ROPET, T, castTraitB322B16>(vregOutBf16, vregOut, mask);
MicroAPI::DataCopy<ROPET, MicroAPI::StoreDist::DIST_PACK_B32>(curOutUb + baseAddr, vregOutBf16, mask);
MicroAPI::Cast<ROPET, T, castTraitB322B16>(vregOutHalfBf16, vregHalfOut, mask);
MicroAPI::DataCopy<ROPET, MicroAPI::StoreDist::DIST_PACK_B32>(curOutUb + baseAddr + halfCol, vregOutHalfBf16,
mask);
for (uint64_t dOffset = 0; dOffset < baseAddr; dOffset += 64) {
uint32_t castMaskValue = min(baseAddr - dOffset, static_cast<uint64_t>(64));
MicroAPI::MaskReg castMask = MicroAPI::UpdateMask<T>(castMaskValue);
MicroAPI::DataCopy(vregCastIn, curInUb + dOffset);
MicroAPI::Cast<ROPET, T, castTraitB322B16>(vregCastOut, vregCastIn, castMask);
MicroAPI::DataCopy<ROPET, MicroAPI::StoreDist::DIST_PACK_B32>(curOutUb + dOffset, vregCastOut, castMask);
}
}
}
template <typename T, typename ROPET>
__simd_vf__ void InterleaveModeRopeVF(__ubuf__ T *sinUb, __ubuf__ T *cosUb, __ubuf__ T *inUb, __ubuf__ ROPET *outUb,
uint32_t row, uint32_t col, uint32_t actualCol, uint64_t baseAddr)
{
MicroAPI::RegTensor<T> vregCos;
MicroAPI::RegTensor<T> vregSin;
MicroAPI::RegTensor<T> vregIn;
MicroAPI::RegTensor<T> vregOdd;
MicroAPI::RegTensor<T> vregEven;
MicroAPI::RegTensor<T> vregOut;
MicroAPI::RegTensor<T> vregTemp;
MicroAPI::RegTensor<T> vregCastIn;
MicroAPI::RegTensor<ROPET> vregOutBf16;
MicroAPI::RegTensor<ROPET> vregCastOut;
uint32_t maskValue = col;
MicroAPI::MaskReg mask = MicroAPI::UpdateMask<T>(maskValue);
for (uint32_t rIdx = 0; rIdx < row; rIdx++) {
__ubuf__ T *curSinUb = sinUb + rIdx * col;
__ubuf__ T *curCosUb = cosUb + rIdx * col;
__ubuf__ T *curInUb = inUb + rIdx * actualCol;
__ubuf__ ROPET *curOutUb = outUb + rIdx * actualCol;
MicroAPI::DataCopy(vregIn, curInUb + baseAddr);
MicroAPI::DataCopy(vregCos, curCosUb);
MicroAPI::DataCopy(vregSin, curSinUb);
MicroAPI::Mul(vregCos, vregCos, vregIn, mask);
MicroAPI::DeInterleave<T>(vregEven, vregOdd, vregIn, vregTemp);
MicroAPI::Muls(vregOdd, vregOdd, static_cast<T>(-1.0), mask);
MicroAPI::Interleave<T>(vregIn, vregTemp, vregOdd, vregEven);
MicroAPI::Mul(vregSin, vregSin, vregIn, mask);
MicroAPI::Add(vregOut, vregCos, vregSin, mask);
MicroAPI::Cast<ROPET, T, castTraitB322B16>(vregOutBf16, vregOut, mask);
MicroAPI::DataCopy<ROPET, MicroAPI::StoreDist::DIST_PACK_B32>(curOutUb + baseAddr, vregOutBf16, mask);
for (uint64_t dOffset = 0; dOffset < baseAddr; dOffset += 64) {
uint32_t castMaskValue = min(baseAddr - dOffset, static_cast<uint64_t>(64));
MicroAPI::MaskReg castMask = MicroAPI::UpdateMask<T>(castMaskValue);
MicroAPI::DataCopy(vregCastIn, curInUb + dOffset);
MicroAPI::Cast<ROPET, T, castTraitB322B16>(vregCastOut, vregCastIn, castMask);
MicroAPI::DataCopy<ROPET, MicroAPI::StoreDist::DIST_PACK_B32>(curOutUb + dOffset, vregCastOut, castMask);
}
}
}
template <Compressor::ROTARY_MODE MODE, typename T, typename ROPET>
__aicore__ inline void RopeVF(const LocalTensor<T> &sinTensor, const LocalTensor<T> &cosTensor,
const LocalTensor<T> &inTensor, const LocalTensor<ROPET> &outTensor, uint32_t row,
uint32_t col, uint32_t actualCol, uint64_t baseAddr)
{
__ubuf__ T *sinUb = (__ubuf__ T *)sinTensor.GetPhyAddr();
__ubuf__ T *cosUb = (__ubuf__ T *)cosTensor.GetPhyAddr();
__ubuf__ T *inUb = (__ubuf__ T *)inTensor.GetPhyAddr();
__ubuf__ ROPET *outUb = (__ubuf__ ROPET *)outTensor.GetPhyAddr();
if constexpr (MODE == Compressor::ROTARY_MODE::HALF) {
HalfModeRopeVF(sinUb, cosUb, inUb, outUb, row, col, actualCol, baseAddr);
} else {
InterleaveModeRopeVF(sinUb, cosUb, inUb, outUb, row, col, actualCol, baseAddr);
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/**
 * 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 compressor.cpp
* \brief
*/
#if (__CCE_AICORE__ == 220)
#include "arch32/compressor_kernel.h"
#include "arch32/compressor_kernel_perf.h"
#else
#include "arch35/compressor_kernel.h"
#include "arch35/compressor_kernel_full_load.h"
#endif
using namespace Compressor;
#define INVOKE_COMPRESSOR_GENERAL_OP_IMPL(templateClass, ...) \
do { \
templateClass<COMPType<__VA_ARGS__>> op(&pipe, tilingData); \
op.Init(x, wKv, wGate, stateCache, ape, normWeight, ropeSin, ropeCos, stateBlockTable, \
cuSeqlens, seqUsed, startPos, cmpKvOut, workspace); \
op.Process(); \
} while (0)
#if (__CCE_AICORE__ == 220)
template<uint8_t XLayout, uint8_t XDType, uint8_t Coff, uint8_t RotaryMode, uint8_t CacheMode, uint8_t TemplateId, uint8_t RopeDType>
#else
template<uint8_t XLayout, uint8_t XDType, uint8_t Coff, uint8_t RotaryMode, uint8_t CacheMode, uint8_t TemplateId>
#endif
__global__ __aicore__ void compressor(
__gm__ uint8_t *x,
__gm__ uint8_t *wKv,
__gm__ uint8_t *wGate,
__gm__ uint8_t *stateCache,
__gm__ uint8_t *ape,
__gm__ uint8_t *normWeight,
__gm__ uint8_t *ropeSin,
__gm__ uint8_t *ropeCos,
__gm__ uint8_t *stateBlockTable,
__gm__ uint8_t *cuSeqlens,
__gm__ uint8_t *seqUsed,
__gm__ uint8_t *startPos,
__gm__ uint8_t *cmpKvOut,
__gm__ uint8_t *stateCacheOut,
__gm__ uint8_t *workspace,
__gm__ uint8_t *tiling) {
REGISTER_TILING_DEFAULT(optiling::CompressorTilingData);
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_MIX_AIC_1_2);
GET_TILING_DATA_WITH_STRUCT(optiling::CompressorTilingData, tilingDataIn, tiling);
if constexpr (static_cast<TEMPLATE_ID>(TemplateId) == TEMPLATE_ID::EMPTY_X) {
return;
}
const optiling::CompressorTilingData *__restrict tilingData = &tilingDataIn;
TPipe pipe;
constexpr auto xLayout = static_cast<X_LAYOUT>(XLayout);
constexpr auto xDtype = static_cast<X_DTYPE>(XDType);
#if (__CCE_AICORE__ == 220)
constexpr auto ropeDtype = static_cast<ROPE_DTYPE>(RopeDType);
#endif
constexpr auto coff = static_cast<COFF>(Coff);
constexpr auto rotaryMode = static_cast<ROTARY_MODE>(RotaryMode);
#if (__CCE_AICORE__ != 220)
constexpr auto cacheMode = static_cast<CACHE_MODE>(CacheMode);
#endif
#if (__CCE_AICORE__ == 220)
if constexpr (static_cast<TEMPLATE_ID>(TemplateId) == TEMPLATE_ID::PERF) {
INVOKE_COMPRESSOR_GENERAL_OP_IMPL(CompressorKernelPerf, xLayout, xDtype, ropeDtype, coff, rotaryMode);
} else {
INVOKE_COMPRESSOR_GENERAL_OP_IMPL(CompressorKernel, xLayout, xDtype, ropeDtype, coff, rotaryMode);
}
#else
if constexpr (static_cast<TEMPLATE_ID>(TemplateId) == TEMPLATE_ID::FULL_LOAD) {
INVOKE_COMPRESSOR_GENERAL_OP_IMPL(CompressorKernelFullLoad, xLayout, xDtype, coff, rotaryMode, cacheMode);
} else {
INVOKE_COMPRESSOR_GENERAL_OP_IMPL(CompressorKernel, xLayout, xDtype, coff, rotaryMode, cacheMode);
}
#endif
}

View File

@@ -0,0 +1,19 @@
# -----------------------------------------------------------------------------------------------------------
# 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(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
if(NOT ENABLE_TEST AND NOT BENCHMARK)
list(REMOVE_ITEM CURRENT_DIRS tests)
endif()
foreach(SUB_DIR ${CURRENT_DIRS})
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
add_subdirectory(${SUB_DIR})
endif()
endforeach()

View File

@@ -0,0 +1,33 @@
# -----------------------------------------------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------------------------------------------
add_op_to_compiled_list()
if (BUILD_OPEN_PROJECT)
target_sources(op_host_aclnn PRIVATE
compressor_metadata_def.cpp
)
endif()
add_ops_compile_options(
OP_NAME CompressorMetadata
OPTIONS --cce-auto-sync=off
-Wno-deprecated-declarations
-mllvm -cce-aicore-hoist-movemask=false
--op_relocatable_kernel_binary=true
)
if (NOT BUILD_OPS_RTY_KERNEL)
add_modules_sources(OPTYPE compressor_metadata ACLNNTYPE aclnn)
target_include_directories(${OPHOST_NAME}_tiling_obj PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
endif()

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
*/
#include "register/op_def_registry.h"
namespace ops {
class CompressorMetadata : public OpDef {
public:
explicit CompressorMetadata(const char* name) : OpDef(name)
{
this->Input("ropeCos")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.AutoContiguous();
this->Input("ropeSin")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.AutoContiguous();
this->Input("cuSeqlens")
.ParamType(REQUIRED)
.DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.AutoContiguous();
this->Input("startPos")
.ParamType(REQUIRED)
.DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.AutoContiguous();
this->Input("kvBlockTable")
.ParamType(REQUIRED)
.DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.AutoContiguous();
this->Output("compressCos")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Output("compressSin")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Output("slotMapping")
.ParamType(REQUIRED)
.DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Attr("kvBlockSize").Int();
this->Attr("slotMappingFormat").Int();
this->Attr("cmpRatio").Int();
this->Attr("actualNumReqs").Int();
this->AICore().AddConfig("ascend910b");
this->AICore().AddConfig("ascend910_93");
this->AICore().AddConfig("ascend950");
}
};
OP_ADD(CompressorMetadata);
} // namespace ops

View File

@@ -0,0 +1,289 @@
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
*/
#include "compressor_metadata_tiling.h"
#include <algorithm>
#include "register/op_def_registry.h"
#include "tiling/platform/platform_ascendc.h"
#include "tiling_base/error_log.h"
namespace optiling {
namespace {
constexpr uint32_t ROPE_COS_INDEX = 0;
constexpr uint32_t ROPE_SIN_INDEX = 1;
constexpr uint32_t CU_SEQLENS_INDEX = 2;
constexpr uint32_t START_POS_INDEX = 3;
constexpr uint32_t KV_BLOCK_TABLE_INDEX = 4;
constexpr uint32_t COMPRESS_COS_INDEX = 0;
constexpr uint32_t COMPRESS_SIN_INDEX = 1;
constexpr uint32_t SLOT_MAPPING_INDEX = 2;
constexpr uint32_t SLOT_MAPPING_FLAT = 1;
constexpr uint32_t SLOT_MAPPING_BLOCK_OFFSET = 2;
constexpr int64_t MAX_UINT32_VALUE = 0xFFFFFFFFLL;
constexpr int64_t MAX_INT32_VALUE = 0x7FFFFFFFLL;
constexpr uint32_t TILING_KEY_FLOAT = 1;
constexpr uint32_t TILING_KEY_FLOAT16 = 2;
constexpr uint32_t TILING_KEY_BF16 = 3;
constexpr uint32_t ALIGN_BYTES = 32;
constexpr uint32_t BUFFER_NUM = 2;
constexpr uint32_t MAX_TILE_ROWS = 512;
constexpr uint32_t MAX_DATACOPY_BLOCK_COUNT = 4095;
constexpr uint32_t ROWS_PER_CORE_TARGET = 64;
constexpr uint32_t UB_RESERVED_BYTES = 16 * 1024;
uint32_t AlignUp(uint64_t value, uint32_t align)
{
return static_cast<uint32_t>((value + align - 1) / align * align);
}
uint32_t CeilDiv(uint64_t lhs, uint64_t rhs)
{
return static_cast<uint32_t>((lhs + rhs - 1) / rhs);
}
} // namespace
static ge::graphStatus CompressorMetadataTilingFunc(gert::TilingContext* context)
{
auto platformInfo = context->GetPlatformInfo();
OP_CHECK_NULL_WITH_CONTEXT(context, platformInfo);
auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfo);
uint32_t aivCoreNum = ascendcPlatform.GetCoreNumAiv();
if (aivCoreNum == 0) {
aivCoreNum = ascendcPlatform.GetCoreNum();
}
if (aivCoreNum == 0) {
OP_LOGE(context->GetNodeName(), "Failed to get AIV core num.");
return ge::GRAPH_FAILED;
}
uint64_t ubSize = 0;
ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize);
if (ubSize == 0) {
OP_LOGE(context->GetNodeName(), "Failed to get UB size.");
return ge::GRAPH_FAILED;
}
auto outputShape = context->GetOutputShape(COMPRESS_COS_INDEX);
auto compressSinShape = context->GetOutputShape(COMPRESS_SIN_INDEX);
auto slotMappingShape = context->GetOutputShape(SLOT_MAPPING_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context, outputShape);
OP_CHECK_NULL_WITH_CONTEXT(context, compressSinShape);
OP_CHECK_NULL_WITH_CONTEXT(context, slotMappingShape);
auto outputDimNum = outputShape->GetStorageShape().GetDimNum();
if (outputDimNum < 2) {
OP_LOGE(context->GetNodeName(), "compressCos dim num should be at least 2.");
return ge::GRAPH_FAILED;
}
if (compressSinShape->GetStorageShape().GetDimNum() != outputDimNum) {
OP_LOGE(context->GetNodeName(), "compressCos and compressSin dim num mismatch.");
return ge::GRAPH_FAILED;
}
for (size_t dimIdx = 0; dimIdx < outputDimNum; ++dimIdx) {
if (compressSinShape->GetStorageShape().GetDim(dimIdx) != outputShape->GetStorageShape().GetDim(dimIdx)) {
OP_LOGE(context->GetNodeName(), "compressCos and compressSin shape mismatch.");
return ge::GRAPH_FAILED;
}
}
int64_t numRows = outputShape->GetStorageShape().GetDim(0);
int64_t ropeDim = outputShape->GetStorageShape().GetDim(outputDimNum - 1);
if (numRows <= 0 || ropeDim <= 0 || numRows > MAX_UINT32_VALUE || ropeDim > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "compressCos shape is invalid.");
return ge::GRAPH_FAILED;
}
auto ropeCosShape = context->GetInputShape(ROPE_COS_INDEX);
auto ropeSinShape = context->GetInputShape(ROPE_SIN_INDEX);
auto cuSeqlensShape = context->GetInputShape(CU_SEQLENS_INDEX);
auto startPosShape = context->GetInputShape(START_POS_INDEX);
auto kvBlockTableShape = context->GetInputShape(KV_BLOCK_TABLE_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context, ropeCosShape);
OP_CHECK_NULL_WITH_CONTEXT(context, ropeSinShape);
OP_CHECK_NULL_WITH_CONTEXT(context, cuSeqlensShape);
OP_CHECK_NULL_WITH_CONTEXT(context, startPosShape);
OP_CHECK_NULL_WITH_CONTEXT(context, kvBlockTableShape);
if (ropeCosShape->GetStorageShape().GetDimNum() != 2 || ropeSinShape->GetStorageShape().GetDimNum() != 2) {
OP_LOGE(context->GetNodeName(), "ropeCos and ropeSin should be 2D tensors.");
return ge::GRAPH_FAILED;
}
int64_t ropeRows = ropeCosShape->GetStorageShape().GetDim(0);
int64_t ropeCosDim = ropeCosShape->GetStorageShape().GetDim(1);
if (ropeRows <= 0 || ropeCosDim <= 0 || ropeRows > MAX_UINT32_VALUE || ropeCosDim != ropeDim ||
ropeSinShape->GetStorageShape().GetDim(0) != ropeRows ||
ropeSinShape->GetStorageShape().GetDim(1) != ropeCosDim) {
OP_LOGE(context->GetNodeName(), "ropeCos and ropeSin shape mismatch.");
return ge::GRAPH_FAILED;
}
int64_t cuSeqlensDim0 = cuSeqlensShape->GetStorageShape().GetDim(0);
if (cuSeqlensDim0 < 2 || cuSeqlensDim0 > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "cuSeqlens dim0 should be at least 2.");
return ge::GRAPH_FAILED;
}
if (startPosShape->GetStorageShape().GetDimNum() != 1 ||
startPosShape->GetStorageShape().GetDim(0) <= 0 ||
startPosShape->GetStorageShape().GetDim(0) > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "startPos should be a non-empty 1D tensor.");
return ge::GRAPH_FAILED;
}
if (kvBlockTableShape->GetStorageShape().GetDimNum() != 2) {
OP_LOGE(context->GetNodeName(), "kvBlockTable should be a 2D tensor.");
return ge::GRAPH_FAILED;
}
int64_t kvBlockTableRows = kvBlockTableShape->GetStorageShape().GetDim(0);
int64_t kvBlockTableStride = kvBlockTableShape->GetStorageShape().GetDim(1);
if (kvBlockTableRows <= 0 || kvBlockTableStride <= 0 || kvBlockTableRows > MAX_UINT32_VALUE ||
kvBlockTableStride > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "kvBlockTable shape is invalid.");
return ge::GRAPH_FAILED;
}
auto attrs = context->GetAttrs();
OP_CHECK_NULL_WITH_CONTEXT(context, attrs);
const int64_t* kvBlockSizePtr = attrs->GetInt(0);
const int64_t* slotMappingFormatPtr = attrs->GetInt(1);
const int64_t* cmpRatioPtr = attrs->GetInt(2);
const int64_t* actualNumReqsPtr = attrs->GetInt(3);
OP_CHECK_NULL_WITH_CONTEXT(context, kvBlockSizePtr);
OP_CHECK_NULL_WITH_CONTEXT(context, slotMappingFormatPtr);
OP_CHECK_NULL_WITH_CONTEXT(context, cmpRatioPtr);
OP_CHECK_NULL_WITH_CONTEXT(context, actualNumReqsPtr);
if (*kvBlockSizePtr <= 0 || *kvBlockSizePtr > MAX_INT32_VALUE) {
OP_LOGE(context->GetNodeName(), "kvBlockSize should be in (0, INT32_MAX].");
return ge::GRAPH_FAILED;
}
if (*cmpRatioPtr <= 0 || *cmpRatioPtr > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "cmpRatio should be in (0, UINT32_MAX].");
return ge::GRAPH_FAILED;
}
if (*slotMappingFormatPtr != SLOT_MAPPING_FLAT && *slotMappingFormatPtr != SLOT_MAPPING_BLOCK_OFFSET) {
OP_LOGE(context->GetNodeName(), "slotMappingFormat should be 1(flat) or 2(block_offset).");
return ge::GRAPH_FAILED;
}
auto slotMappingDimNum = slotMappingShape->GetStorageShape().GetDimNum();
if ((*slotMappingFormatPtr == SLOT_MAPPING_FLAT &&
(slotMappingDimNum != 1 || slotMappingShape->GetStorageShape().GetDim(0) != numRows)) ||
(*slotMappingFormatPtr == SLOT_MAPPING_BLOCK_OFFSET &&
(slotMappingDimNum != 2 || slotMappingShape->GetStorageShape().GetDim(0) != numRows ||
slotMappingShape->GetStorageShape().GetDim(1) != 2))) {
OP_LOGE(context->GetNodeName(), "slotMapping shape does not match slotMappingFormat.");
return ge::GRAPH_FAILED;
}
if (*actualNumReqsPtr <= 0 || *actualNumReqsPtr >= cuSeqlensDim0 ||
*actualNumReqsPtr > startPosShape->GetStorageShape().GetDim(0) ||
*actualNumReqsPtr > kvBlockTableRows ||
*actualNumReqsPtr > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "actualNumReqs is invalid.");
return ge::GRAPH_FAILED;
}
CompressorMetadataTilingData tilingData;
tilingData.set_numRows(static_cast<uint32_t>(numRows));
tilingData.set_numReqs(static_cast<uint32_t>(cuSeqlensDim0 - 1));
tilingData.set_actualNumReqs(static_cast<uint32_t>(*actualNumReqsPtr));
tilingData.set_ropeRows(static_cast<uint32_t>(ropeRows));
tilingData.set_ropeDim(static_cast<uint32_t>(ropeDim));
tilingData.set_kvBlockTableStride(static_cast<uint32_t>(kvBlockTableStride));
tilingData.set_kvBlockSize(static_cast<uint32_t>(*kvBlockSizePtr));
tilingData.set_slotMappingFormat(static_cast<uint32_t>(*slotMappingFormatPtr));
tilingData.set_cmpRatio(static_cast<uint32_t>(*cmpRatioPtr));
auto ropeDesc = context->GetInputDesc(ROPE_COS_INDEX);
auto ropeSinDesc = context->GetInputDesc(ROPE_SIN_INDEX);
auto compressCosDesc = context->GetOutputDesc(COMPRESS_COS_INDEX);
auto compressSinDesc = context->GetOutputDesc(COMPRESS_SIN_INDEX);
OP_CHECK_NULL_WITH_CONTEXT(context, ropeDesc);
OP_CHECK_NULL_WITH_CONTEXT(context, ropeSinDesc);
OP_CHECK_NULL_WITH_CONTEXT(context, compressCosDesc);
OP_CHECK_NULL_WITH_CONTEXT(context, compressSinDesc);
auto ropeDtype = ropeDesc->GetDataType();
if (ropeSinDesc->GetDataType() != ropeDtype ||
compressCosDesc->GetDataType() != ropeDtype ||
compressSinDesc->GetDataType() != ropeDtype) {
OP_LOGE(context->GetNodeName(), "rope and compress output dtypes should match.");
return ge::GRAPH_FAILED;
}
uint64_t tilingKey = 0;
uint32_t dtypeSize = 0;
if (ropeDtype == ge::DataType::DT_FLOAT) {
tilingKey = TILING_KEY_FLOAT;
dtypeSize = sizeof(float);
} else if (ropeDtype == ge::DataType::DT_FLOAT16) {
tilingKey = TILING_KEY_FLOAT16;
dtypeSize = sizeof(uint16_t);
} else if (ropeDtype == ge::DataType::DT_BF16) {
tilingKey = TILING_KEY_BF16;
dtypeSize = sizeof(uint16_t);
} else {
OP_LOGE(context->GetNodeName(), "Unsupported rope dtype.");
return ge::GRAPH_FAILED;
}
uint32_t actualNumReqs = static_cast<uint32_t>(*actualNumReqsPtr);
uint32_t cmpRatio = static_cast<uint32_t>(*cmpRatioPtr);
if (static_cast<uint64_t>(ropeDim) * dtypeSize > MAX_UINT32_VALUE ||
(static_cast<uint64_t>(actualNumReqs) + 1) * sizeof(int32_t) > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "tiling byte size exceeds UINT32_MAX.");
return ge::GRAPH_FAILED;
}
uint32_t ropeRowBytes = static_cast<uint32_t>(ropeDim) * dtypeSize;
if (static_cast<uint64_t>(cmpRatio - 1) * ropeRowBytes > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "rope stride exceeds UINT32_MAX.");
return ge::GRAPH_FAILED;
}
uint32_t ropeRowBytesAligned = AlignUp(ropeRowBytes, ALIGN_BYTES);
uint32_t slotCols = (*slotMappingFormatPtr == SLOT_MAPPING_FLAT) ? 1U : 2U;
uint32_t reqTableBytes = AlignUp((static_cast<uint64_t>(actualNumReqs) + 1) * sizeof(int32_t), ALIGN_BYTES);
uint64_t fixedUbBytes = static_cast<uint64_t>(reqTableBytes) * 3 + ALIGN_BYTES + UB_RESERVED_BYTES;
uint64_t rowUbBytes =
static_cast<uint64_t>(BUFFER_NUM) * ropeRowBytesAligned * 2 + slotCols * sizeof(int32_t) + sizeof(int32_t);
if (rowUbBytes > MAX_UINT32_VALUE) {
OP_LOGE(context->GetNodeName(), "row UB footprint exceeds UINT32_MAX.");
return ge::GRAPH_FAILED;
}
uint64_t minUbBytes = static_cast<uint64_t>(reqTableBytes) * 3 + ALIGN_BYTES + rowUbBytes;
if (ubSize <= minUbBytes) {
OP_LOGE(context->GetNodeName(), "UB size is insufficient for compressor metadata.");
return ge::GRAPH_FAILED;
}
uint32_t tileRows = 1;
if (ubSize > fixedUbBytes && rowUbBytes > 0) {
tileRows = static_cast<uint32_t>((ubSize - fixedUbBytes) / rowUbBytes);
tileRows = std::max(tileRows, 1U);
}
tileRows = std::min(tileRows, MAX_TILE_ROWS);
tileRows = std::min(tileRows, MAX_DATACOPY_BLOCK_COUNT);
uint32_t usedCoreNum =
std::min(aivCoreNum, std::max(1U, CeilDiv(static_cast<uint64_t>(numRows), ROWS_PER_CORE_TARGET)));
tilingData.set_usedCoreNum(usedCoreNum);
tilingData.set_tileRows(tileRows);
tilingData.set_ropeRowBytes(ropeRowBytes);
tilingData.set_ropeRowBytesAligned(ropeRowBytesAligned);
tilingData.set_slotCols(slotCols);
size_t* workspaceSize = context->GetWorkspaceSizes(1);
OP_CHECK_NULL_WITH_CONTEXT(context, workspaceSize);
*workspaceSize = 0;
context->SetBlockDim(usedCoreNum);
context->SetTilingKey(tilingKey);
auto rawTilingData = context->GetRawTilingData();
OP_CHECK_NULL_WITH_CONTEXT(context, rawTilingData);
tilingData.SaveToBuffer(rawTilingData->GetData(), rawTilingData->GetCapacity());
rawTilingData->SetDataSize(tilingData.GetDataSize());
return ge::GRAPH_SUCCESS;
}
static ge::graphStatus TilingParseForCompressorMetadata(gert::TilingParseContext* context)
{
return ge::GRAPH_SUCCESS;
}
IMPL_OP_OPTILING(CompressorMetadata)
.Tiling(CompressorMetadataTilingFunc)
.TilingParse<CompressorMetadataCompileInfo>(TilingParseForCompressorMetadata);
} // namespace optiling

View File

@@ -0,0 +1,32 @@
#ifndef COMPRESSOR_METADATA_TILING_H
#define COMPRESSOR_METADATA_TILING_H
#include "register/tilingdata_base.h"
namespace optiling {
BEGIN_TILING_DATA_DEF(CompressorMetadataTilingData)
TILING_DATA_FIELD_DEF(uint32_t, numRows);
TILING_DATA_FIELD_DEF(uint32_t, numReqs);
TILING_DATA_FIELD_DEF(uint32_t, actualNumReqs);
TILING_DATA_FIELD_DEF(uint32_t, ropeRows);
TILING_DATA_FIELD_DEF(uint32_t, ropeDim);
TILING_DATA_FIELD_DEF(uint32_t, kvBlockTableStride);
TILING_DATA_FIELD_DEF(uint32_t, kvBlockSize);
TILING_DATA_FIELD_DEF(uint32_t, slotMappingFormat);
TILING_DATA_FIELD_DEF(uint32_t, cmpRatio);
TILING_DATA_FIELD_DEF(uint32_t, usedCoreNum);
TILING_DATA_FIELD_DEF(uint32_t, tileRows);
TILING_DATA_FIELD_DEF(uint32_t, ropeRowBytes);
TILING_DATA_FIELD_DEF(uint32_t, ropeRowBytesAligned);
TILING_DATA_FIELD_DEF(uint32_t, slotCols);
END_TILING_DATA_DEF;
REGISTER_TILING_DATA_CLASS(CompressorMetadata, CompressorMetadataTilingData)
struct CompressorMetadataCompileInfo {
uint32_t coreNum;
uint64_t ubSizePlatForm;
};
} // namespace optiling
#endif

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
*/
#include "compressor_metadata.h"
extern "C" __global__ __aicore__ void compressor_metadata(
GM_ADDR ropeCos,
GM_ADDR ropeSin,
GM_ADDR cuSeqlens,
GM_ADDR startPos,
GM_ADDR kvBlockTable,
GM_ADDR compressCos,
GM_ADDR compressSin,
GM_ADDR slotMapping,
GM_ADDR workspace,
GM_ADDR tiling)
{
REGISTER_TILING_DEFAULT(CompressorMetadata::CompressorMetadataTilingData);
GET_TILING_DATA(tilingData, tiling);
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
AscendC::TPipe pipe;
if (TILING_KEY_IS(1)) {
CompressorMetadata::CompressorMetadataKernel<float> op;
op.Init(&tilingData, &pipe);
op.Process(ropeCos, ropeSin, cuSeqlens, startPos, kvBlockTable, compressCos, compressSin, slotMapping, workspace);
} else if (TILING_KEY_IS(2)) {
CompressorMetadata::CompressorMetadataKernel<half> op;
op.Init(&tilingData, &pipe);
op.Process(ropeCos, ropeSin, cuSeqlens, startPos, kvBlockTable, compressCos, compressSin, slotMapping, workspace);
} else if (TILING_KEY_IS(3)) {
CompressorMetadata::CompressorMetadataKernel<bfloat16_t> op;
op.Init(&tilingData, &pipe);
op.Process(ropeCos, ropeSin, cuSeqlens, startPos, kvBlockTable, compressCos, compressSin, slotMapping, workspace);
}
}

View File

@@ -0,0 +1,428 @@
#ifndef COMPRESSOR_METADATA_H
#define COMPRESSOR_METADATA_H
#include "kernel_operator.h"
namespace CompressorMetadata {
using namespace AscendC;
constexpr uint32_t SLOT_MAPPING_FLAT = 1;
constexpr uint32_t ALIGN_BYTES = 32;
constexpr uint32_t BUFFER_NUM = 2;
constexpr int64_t MAX_INT32_VALUE = 0x7FFFFFFFLL;
__aicore__ inline uint32_t MinU32(uint32_t lhs, uint32_t rhs)
{
return lhs < rhs ? lhs : rhs;
}
__aicore__ inline uint32_t MaxU32(uint32_t lhs, uint32_t rhs)
{
return lhs > rhs ? lhs : rhs;
}
__aicore__ inline uint32_t AlignUpU32(uint32_t value, uint32_t align)
{
return (value + align - 1) / align * align;
}
__aicore__ inline uint32_t Int32BytesU32(uint32_t elems)
{
return elems * static_cast<uint32_t>(sizeof(int32_t));
}
__aicore__ inline void PipeMte2ToS()
{
event_t eventID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE2_S));
SetFlag<HardEvent::MTE2_S>(eventID);
WaitFlag<HardEvent::MTE2_S>(eventID);
}
__aicore__ inline void PipeMte3ToS()
{
event_t eventID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::MTE3_S));
SetFlag<HardEvent::MTE3_S>(eventID);
WaitFlag<HardEvent::MTE3_S>(eventID);
}
__aicore__ inline void PipeSToMte3()
{
event_t eventID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::S_MTE3));
SetFlag<HardEvent::S_MTE3>(eventID);
WaitFlag<HardEvent::S_MTE3>(eventID);
}
__aicore__ inline void PipeVToMte3()
{
event_t eventID = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::V_MTE3));
SetFlag<HardEvent::V_MTE3>(eventID);
WaitFlag<HardEvent::V_MTE3>(eventID);
}
struct CompressorMetadataTilingData {
uint32_t numRows;
uint32_t numReqs;
uint32_t actualNumReqs;
uint32_t ropeRows;
uint32_t ropeDim;
uint32_t kvBlockTableStride;
uint32_t kvBlockSize;
uint32_t slotMappingFormat;
uint32_t cmpRatio;
uint32_t usedCoreNum;
uint32_t tileRows;
uint32_t ropeRowBytes;
uint32_t ropeRowBytesAligned;
uint32_t slotCols;
};
template <typename T>
class CompressorMetadataKernel {
public:
__aicore__ inline CompressorMetadataKernel() {}
__aicore__ inline void Init(CompressorMetadataTilingData* tilingData, TPipe* pipe)
{
numRows_ = tilingData->numRows;
actualNumReqs_ = tilingData->actualNumReqs;
ropeRows_ = tilingData->ropeRows;
ropeDim_ = tilingData->ropeDim;
kvBlockTableStride_ = tilingData->kvBlockTableStride;
kvBlockSize_ = tilingData->kvBlockSize;
slotMappingFormat_ = tilingData->slotMappingFormat;
cmpRatio_ = tilingData->cmpRatio;
tileRows_ = tilingData->tileRows;
ropeRowBytes_ = tilingData->ropeRowBytes;
ropeRowBytesAligned_ = tilingData->ropeRowBytesAligned;
slotCols_ = tilingData->slotCols;
reqTableBytes_ = AlignUpU32(Int32BytesU32(actualNumReqs_ + 1), ALIGN_BYTES);
ropeDimAligned_ = ropeRowBytesAligned_ / sizeof(T);
ropePadElems_ = ropeDimAligned_ - ropeDim_;
slotTileBytes_ = AlignUpU32(Int32BytesU32(tileRows_ * slotCols_), ALIGN_BYTES);
blockTableTileBytes_ = AlignUpU32(Int32BytesU32(tileRows_), ALIGN_BYTES);
pipe->InitBuffer(prefixBuf_, reqTableBytes_);
pipe->InitBuffer(startPosBuf_, reqTableBytes_);
pipe->InitBuffer(cuSeqlensBuf_, reqTableBytes_);
pipe->InitBuffer(blockTableBuf_, blockTableTileBytes_);
pipe->InitBuffer(slotBuf_, slotTileBytes_);
pipe->InitBuffer(cosQueue_, BUFFER_NUM, tileRows_ * ropeRowBytesAligned_);
pipe->InitBuffer(sinQueue_, BUFFER_NUM, tileRows_ * ropeRowBytesAligned_);
}
__aicore__ inline void Process(
GM_ADDR ropeCos,
GM_ADDR ropeSin,
GM_ADDR cuSeqlens,
GM_ADDR startPos,
GM_ADDR kvBlockTable,
GM_ADDR compressCos,
GM_ADDR compressSin,
GM_ADDR slotMapping,
GM_ADDR)
{
ropeCosGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(ropeCos));
ropeSinGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(ropeSin));
cuSeqlensGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t*>(cuSeqlens));
startPosGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t*>(startPos));
kvBlockTableGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t*>(kvBlockTable));
compressCosGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(compressCos));
compressSinGm_.SetGlobalBuffer(reinterpret_cast<__gm__ T*>(compressSin));
slotMappingGm_.SetGlobalBuffer(reinterpret_cast<__gm__ int32_t*>(slotMapping));
LocalTensor<int32_t> prefixLocal = prefixBuf_.Get<int32_t>();
LocalTensor<int32_t> startPosLocal = startPosBuf_.Get<int32_t>();
LocalTensor<int32_t> cuSeqlensLocal = cuSeqlensBuf_.Get<int32_t>();
BuildCompressedPrefix(prefixLocal, startPosLocal, cuSeqlensLocal);
uint32_t validRows = static_cast<uint32_t>(prefixLocal.GetValue(actualNumReqs_));
validRows = MinU32(validRows, numRows_);
ProcessValidRows(prefixLocal, startPosLocal, validRows);
ProcessPaddingRows(validRows);
}
private:
__aicore__ inline void BuildCompressedPrefix(
LocalTensor<int32_t>& prefixLocal,
LocalTensor<int32_t>& startPosLocal,
LocalTensor<int32_t>& cuSeqlensLocal)
{
DataCopyExtParams startCopyParams{1, Int32BytesU32(actualNumReqs_), 0, 0, 0};
DataCopyExtParams cuCopyParams{1, Int32BytesU32(actualNumReqs_ + 1), 0, 0, 0};
DataCopyPadExtParams<int32_t> padParams{true, 0, 0, 0};
DataCopyPad(startPosLocal, startPosGm_, startCopyParams, padParams);
DataCopyPad(cuSeqlensLocal, cuSeqlensGm_, cuCopyParams, padParams);
PipeMte2ToS();
uint32_t prefix = 0;
prefixLocal.SetValue(0, 0);
for (uint32_t reqIdx = 0; reqIdx < actualNumReqs_; ++reqIdx) {
int64_t startPos = static_cast<int64_t>(startPosLocal.GetValue(reqIdx));
int64_t seqLen = static_cast<int64_t>(cuSeqlensLocal.GetValue(reqIdx + 1)) -
static_cast<int64_t>(cuSeqlensLocal.GetValue(reqIdx));
uint32_t compressedRows = 0;
if (startPos >= 0 && seqLen > 0) {
compressedRows = static_cast<uint32_t>(((startPos + seqLen) / cmpRatio_) - (startPos / cmpRatio_));
}
prefix += compressedRows;
prefixLocal.SetValue(reqIdx + 1, static_cast<int32_t>(prefix));
}
}
__aicore__ inline void SplitRange(uint32_t totalRows, uint32_t& begin, uint32_t& end)
{
uint32_t blockIdx = GetBlockIdx();
uint32_t blockNum = MaxU32(GetBlockNum(), 1);
uint32_t rowsPerBlock = (totalRows + blockNum - 1) / blockNum;
begin = MinU32(blockIdx * rowsPerBlock, totalRows);
end = MinU32(begin + rowsPerBlock, totalRows);
}
__aicore__ inline uint32_t FindRequest(LocalTensor<int32_t>& prefixLocal, uint32_t row)
{
uint32_t reqIdx = 0;
while (reqIdx < actualNumReqs_ && static_cast<uint32_t>(prefixLocal.GetValue(reqIdx + 1)) <= row) {
++reqIdx;
}
return reqIdx;
}
__aicore__ inline void ProcessValidRows(
LocalTensor<int32_t>& prefixLocal,
LocalTensor<int32_t>& startPosLocal,
uint32_t validRows)
{
uint32_t begin = 0;
uint32_t end = 0;
SplitRange(validRows, begin, end);
if (begin >= end) {
return;
}
uint32_t reqIdx = FindRequest(prefixLocal, begin);
uint32_t row = begin;
while (row < end && reqIdx < actualNumReqs_) {
uint32_t reqBegin = static_cast<uint32_t>(prefixLocal.GetValue(reqIdx));
uint32_t reqEnd = static_cast<uint32_t>(prefixLocal.GetValue(reqIdx + 1));
if (row >= reqEnd) {
++reqIdx;
continue;
}
uint32_t rowsInReq = MinU32(end - row, reqEnd - row);
int64_t startPos = static_cast<int64_t>(startPosLocal.GetValue(reqIdx));
uint32_t localCompressedIdx = row - reqBegin;
// KV slot uses compressed position; RoPE uses the original group-start position.
uint32_t compressedPos = static_cast<uint32_t>(startPos / cmpRatio_) + localCompressedIdx;
ProcessRequestRows(reqIdx, row, compressedPos, rowsInReq);
row += rowsInReq;
}
}
__aicore__ inline void ProcessRequestRows(
uint32_t reqIdx,
uint32_t outputRow,
uint32_t compressedPos,
uint32_t rows)
{
while (rows > 0) {
uint32_t blockOffset = compressedPos % kvBlockSize_;
uint32_t rowsToBlockEnd = kvBlockSize_ - blockOffset;
uint32_t curRows = MinU32(rows, tileRows_);
curRows = MinU32(curRows, rowsToBlockEnd);
ProcessTile(reqIdx, outputRow, compressedPos, curRows);
outputRow += curRows;
compressedPos += curRows;
rows -= curRows;
}
}
__aicore__ inline void ProcessTile(
uint32_t reqIdx,
uint32_t outputRow,
uint32_t compressedPos,
uint32_t rows)
{
uint32_t blockIdOffset = compressedPos / kvBlockSize_;
if (blockIdOffset >= kvBlockTableStride_) {
WriteInvalidTile(outputRow, rows);
return;
}
LocalTensor<int32_t> blockTableLocal = blockTableBuf_.Get<int32_t>();
DataCopyExtParams blockCopyParams{1, Int32BytesU32(1), 0, 0, 0};
DataCopyPadExtParams<int32_t> padParams{true, 0, 0, 0};
uint64_t blockTableGmOffset = static_cast<uint64_t>(reqIdx) * kvBlockTableStride_ + blockIdOffset;
DataCopyPad(blockTableLocal, kvBlockTableGm_[blockTableGmOffset], blockCopyParams, padParams);
PipeMte2ToS();
int32_t blockId = blockTableLocal.GetValue(0);
if (blockId < 0) {
WriteInvalidTile(outputRow, rows);
return;
}
uint32_t blockOffset = compressedPos % kvBlockSize_;
if (slotMappingFormat_ == SLOT_MAPPING_FLAT) {
int64_t maxSlot = static_cast<int64_t>(blockId) * kvBlockSize_ + blockOffset + rows - 1;
if (maxSlot > MAX_INT32_VALUE) {
WriteInvalidTile(outputRow, rows);
return;
}
}
uint64_t lastRopePos = (static_cast<uint64_t>(compressedPos) + rows - 1) * cmpRatio_;
if (lastRopePos >= ropeRows_) {
WriteInvalidTile(outputRow, rows);
return;
}
CopyRopeTile(outputRow, compressedPos, rows);
WriteSlotTile(outputRow, compressedPos, rows, blockId);
}
__aicore__ inline void CopyRopeTile(uint32_t outputRow, uint32_t compressedPos, uint32_t rows)
{
LocalTensor<T> cosLocal = cosQueue_.AllocTensor<T>();
LocalTensor<T> sinLocal = sinQueue_.AllocTensor<T>();
uint64_t ropePos = static_cast<uint64_t>(compressedPos) * cmpRatio_;
uint32_t srcStride = (cmpRatio_ - 1) * ropeRowBytes_;
DataCopyExtParams copyInParams{
static_cast<uint16_t>(rows), ropeRowBytes_, srcStride, 0, 0};
DataCopyPadExtParams<T> padParams{true, 0, static_cast<uint8_t>(ropePadElems_), 0};
DataCopyPad(cosLocal, ropeCosGm_[ropePos * ropeDim_], copyInParams, padParams);
DataCopyPad(sinLocal, ropeSinGm_[ropePos * ropeDim_], copyInParams, padParams);
PipeMte2ToS();
DataCopyExtParams copyOutParams{
static_cast<uint16_t>(rows), ropeRowBytes_, 0, 0, 0};
uint64_t outputBase = static_cast<uint64_t>(outputRow) * ropeDim_;
DataCopyPad(compressCosGm_[outputBase], cosLocal, copyOutParams);
DataCopyPad(compressSinGm_[outputBase], sinLocal, copyOutParams);
PipeMte3ToS();
cosQueue_.FreeTensor<T>(cosLocal);
sinQueue_.FreeTensor<T>(sinLocal);
}
__aicore__ inline void WriteSlotTile(
uint32_t outputRow,
uint32_t compressedPos,
uint32_t rows,
int32_t blockId)
{
LocalTensor<int32_t> slotLocal = slotBuf_.Get<int32_t>();
int32_t blockOffset = static_cast<int32_t>(compressedPos % kvBlockSize_);
if (slotMappingFormat_ == SLOT_MAPPING_FLAT) {
int32_t slotBase = blockId * static_cast<int32_t>(kvBlockSize_) + blockOffset;
for (uint32_t row = 0; row < rows; ++row) {
slotLocal.SetValue(row, slotBase + static_cast<int32_t>(row));
}
} else {
for (uint32_t row = 0; row < rows; ++row) {
uint32_t slotOffset = row * slotCols_;
slotLocal.SetValue(slotOffset, blockId);
slotLocal.SetValue(slotOffset + 1, blockOffset + static_cast<int32_t>(row));
}
}
DataCopyExtParams slotCopyParams{1, Int32BytesU32(rows * slotCols_), 0, 0, 0};
PipeSToMte3();
DataCopyPad(slotMappingGm_[static_cast<uint64_t>(outputRow) * slotCols_], slotLocal, slotCopyParams);
PipeMte3ToS();
}
__aicore__ inline void WriteInvalidTile(uint32_t outputRow, uint32_t rows)
{
LocalTensor<T> cosLocal = cosQueue_.AllocTensor<T>();
LocalTensor<T> sinLocal = sinQueue_.AllocTensor<T>();
Duplicate<T>(cosLocal, static_cast<T>(1.0f), rows * ropeDimAligned_);
Duplicate<T>(sinLocal, static_cast<T>(0.0f), rows * ropeDimAligned_);
PipeVToMte3();
DataCopyExtParams ropeCopyParams{
static_cast<uint16_t>(rows), ropeRowBytes_, 0, 0, 0};
uint64_t outputBase = static_cast<uint64_t>(outputRow) * ropeDim_;
DataCopyPad(compressCosGm_[outputBase], cosLocal, ropeCopyParams);
DataCopyPad(compressSinGm_[outputBase], sinLocal, ropeCopyParams);
PipeMte3ToS();
cosQueue_.FreeTensor<T>(cosLocal);
sinQueue_.FreeTensor<T>(sinLocal);
LocalTensor<int32_t> slotLocal = slotBuf_.Get<int32_t>();
if (slotMappingFormat_ == SLOT_MAPPING_FLAT) {
for (uint32_t row = 0; row < rows; ++row) {
slotLocal.SetValue(row, -1);
}
} else {
int32_t padOffset = static_cast<int32_t>(kvBlockSize_ - 1);
for (uint32_t row = 0; row < rows; ++row) {
uint32_t slotOffset = row * slotCols_;
slotLocal.SetValue(slotOffset, -1);
slotLocal.SetValue(slotOffset + 1, padOffset);
}
}
PipeSToMte3();
DataCopyExtParams slotCopyParams{1, Int32BytesU32(rows * slotCols_), 0, 0, 0};
DataCopyPad(slotMappingGm_[static_cast<uint64_t>(outputRow) * slotCols_], slotLocal, slotCopyParams);
PipeMte3ToS();
}
__aicore__ inline void ProcessPaddingRows(uint32_t validRows)
{
if (validRows >= numRows_) {
return;
}
uint32_t padRows = numRows_ - validRows;
uint32_t begin = 0;
uint32_t end = 0;
SplitRange(padRows, begin, end);
uint32_t row = validRows + begin;
uint32_t padEnd = validRows + end;
while (row < padEnd) {
uint32_t curRows = MinU32(tileRows_, padEnd - row);
WriteInvalidTile(row, curRows);
row += curRows;
}
}
uint32_t numRows_{0};
uint32_t actualNumReqs_{0};
uint32_t ropeRows_{0};
uint32_t ropeDim_{0};
uint32_t kvBlockTableStride_{0};
uint32_t kvBlockSize_{0};
uint32_t slotMappingFormat_{0};
uint32_t cmpRatio_{1};
uint32_t tileRows_{1};
uint32_t ropeRowBytes_{0};
uint32_t ropeRowBytesAligned_{0};
uint32_t slotCols_{1};
uint32_t reqTableBytes_{0};
uint32_t ropeDimAligned_{0};
uint32_t ropePadElems_{0};
uint32_t slotTileBytes_{0};
uint32_t blockTableTileBytes_{0};
TBuf<TPosition::VECCALC> prefixBuf_;
TBuf<TPosition::VECCALC> startPosBuf_;
TBuf<TPosition::VECCALC> cuSeqlensBuf_;
TBuf<TPosition::VECCALC> blockTableBuf_;
TBuf<TPosition::VECCALC> slotBuf_;
TQue<TPosition::VECOUT, BUFFER_NUM> cosQueue_;
TQue<TPosition::VECOUT, BUFFER_NUM> sinQueue_;
GlobalTensor<T> ropeCosGm_;
GlobalTensor<T> ropeSinGm_;
GlobalTensor<T> compressCosGm_;
GlobalTensor<T> compressSinGm_;
GlobalTensor<int32_t> cuSeqlensGm_;
GlobalTensor<int32_t> startPosGm_;
GlobalTensor<int32_t> kvBlockTableGm_;
GlobalTensor<int32_t> slotMappingGm_;
};
} // namespace CompressorMetadata
#endif

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
#ifndef FUSED_GDN_GATING_TORCH_ADPT_H
#define FUSED_GDN_GATING_TORCH_ADPT_H
#include <tuple>
namespace vllm_ascend {
std::tuple<at::Tensor, at::Tensor> npu_fused_gdn_gating(
const at::Tensor& A_log,
const at::Tensor& a,
const at::Tensor& b,
const at::Tensor& dt_bias,
double beta = 1.0,
double threshold = 20.0)
{
TORCH_CHECK(A_log.dim() == 1, "A_log should be 1-D [num_heads], got ", A_log.dim(), "D");
TORCH_CHECK(dt_bias.dim() == 1, "dt_bias should be 1-D [num_heads], got ", dt_bias.dim(), "D");
TORCH_CHECK(a.dim() == 2, "a should be 2-D [batch, num_heads], got ", a.dim(), "D");
TORCH_CHECK(b.dim() == 2, "b should be 2-D [batch, num_heads], got ", b.dim(), "D");
TORCH_CHECK(b.size(0) == a.size(0) && b.size(1) == a.size(1),
"a and b must have the same shape, got a=", a.sizes(), " b=", b.sizes());
TORCH_CHECK(a.scalar_type() == b.scalar_type(),
"a and b must have the same dtype, got a=", a.scalar_type(),
" b=", b.scalar_type());
TORCH_CHECK(A_log.scalar_type() == dt_bias.scalar_type(),
"A_log and dt_bias must have the same dtype, got A_log=",
A_log.scalar_type(), " dt_bias=", dt_bias.scalar_type());
TORCH_CHECK(a.size(1) == A_log.size(0),
"a second dim (num_heads) must equal A_log first dim, got a.size(1)=",
a.size(1), " A_log.size(0)=", A_log.size(0));
int64_t batch = a.size(0);
int64_t num_heads = a.size(1);
at::Tensor g = at::empty({1, batch, num_heads},
a.options().dtype(c10::kFloat));
at::Tensor beta_output = at::empty({1, batch, num_heads}, b.options());
float beta_val = static_cast<float>(beta);
float threshold_val = static_cast<float>(threshold);
EXEC_NPU_CMD(aclnnFusedGdnGating,
A_log, a, b, dt_bias,
beta_val,
threshold_val,
g, beta_output);
return std::make_tuple(g, beta_output);
}
} // namespace vllm_ascend
#endif // FUSED_GDN_GATING_TORCH_ADPT_H

View File

@@ -0,0 +1,23 @@
# Copyright (c) 2026 Huawei Technologies Co., Ltd.
add_op_to_compiled_list()
if (BUILD_OPEN_PROJECT)
target_sources(op_host_aclnnExc PRIVATE
fused_gdn_gating_def.cpp
)
endif()
add_ops_compile_options(
OP_NAME FusedGdnGating
OPTIONS --cce-auto-sync=on
-Wno-deprecated-declarations
)
if (NOT BUILD_OPS_RTY_KERNEL)
add_modules_sources(OPTYPE fused_gdn_gating ACLNNTYPE aclnn_exclude)
target_include_directories(${OPHOST_NAME}_tiling_obj PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
endif()

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating_def.cpp
* \brief OpDef registration for FusedGdnGating.
*/
#include "register/op_def_registry.h"
namespace ops {
class FusedGdnGating : public OpDef {
public:
explicit FusedGdnGating(const char *name) : OpDef(name)
{
this->Input("a_log")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_BF16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Input("a")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Input("b")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Input("dt_bias")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_BF16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_FLOAT16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Output("g")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Output("beta_output")
.ParamType(REQUIRED)
.DataType({ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Attr("beta").AttrType(OPTIONAL).Float(1.0f);
this->Attr("threshold").AttrType(OPTIONAL).Float(20.0f);
OpAICoreConfig aicConfig;
aicConfig.DynamicCompileStaticFlag(true)
.DynamicFormatFlag(true)
.DynamicRankSupportFlag(true)
.DynamicShapeSupportFlag(true)
.NeedCheckSupportFlag(false)
.ExtendCfgInfo("softsync.flag", "true");
this->AICore().AddConfig("ascend910b", aicConfig);
this->AICore().AddConfig("ascend910_93", aicConfig);
}
};
OP_ADD(FusedGdnGating);
} // namespace ops

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating_infershape.cpp
* \brief Shape and data-type inference for FusedGdnGating.
*/
#include "exe_graph/runtime/infer_shape_context.h"
#include "exe_graph/runtime/shape.h"
#include "exe_graph/runtime/storage_shape.h"
#include "register/op_impl_registry.h"
using namespace gert;
namespace ops {
namespace {
constexpr size_t INPUT_A_INDEX = 1;
constexpr size_t OUTPUT_G_INDEX = 0;
constexpr size_t OUTPUT_BETA_INDEX = 1;
constexpr size_t OUTPUT_DIM_NUM = 3;
constexpr int64_t OUTPUT_SEQ_LEN = 1;
} // namespace
static ge::graphStatus InferShapeFusedGdnGating(InferShapeContext *context)
{
if (context == nullptr) {
return ge::GRAPH_FAILED;
}
auto shapeA = context->GetInputShape(INPUT_A_INDEX);
auto shapeG = context->GetOutputShape(OUTPUT_G_INDEX);
auto shapeBeta = context->GetOutputShape(OUTPUT_BETA_INDEX);
if (shapeA == nullptr || shapeG == nullptr || shapeBeta == nullptr) {
return ge::GRAPH_FAILED;
}
if (shapeA->GetDimNum() < 2) {
return ge::GRAPH_FAILED;
}
const int64_t batch = shapeA->GetDim(0);
const int64_t numHeads = shapeA->GetDim(1);
shapeG->SetDimNum(OUTPUT_DIM_NUM);
shapeG->SetDim(0, OUTPUT_SEQ_LEN);
shapeG->SetDim(1, batch);
shapeG->SetDim(2, numHeads);
shapeBeta->SetDimNum(OUTPUT_DIM_NUM);
shapeBeta->SetDim(0, OUTPUT_SEQ_LEN);
shapeBeta->SetDim(1, batch);
shapeBeta->SetDim(2, numHeads);
return ge::GRAPH_SUCCESS;
}
static ge::graphStatus InferDataTypeFusedGdnGating(gert::InferDataTypeContext *context)
{
if (context == nullptr) {
return ge::GRAPH_FAILED;
}
ge::DataType inputADtype = context->GetInputDataType(INPUT_A_INDEX);
context->SetOutputDataType(OUTPUT_G_INDEX, ge::DT_FLOAT);
context->SetOutputDataType(OUTPUT_BETA_INDEX, inputADtype);
return ge::GRAPH_SUCCESS;
}
IMPL_OP_INFERSHAPE(FusedGdnGating)
.InferShape(InferShapeFusedGdnGating)
.InferDataType(InferDataTypeFusedGdnGating);
} // namespace ops

View File

@@ -0,0 +1,176 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating_tiling.cpp
* \brief Tiling implementation for FusedGdnGating.
*/
#include "fused_gdn_gating_tiling.h"
#include "fused_gdn_gating_tiling_utils.h"
#include "register/op_impl_registry.h"
#include "securec.h"
#include "tiling/platform/platform_ascendc.h"
#include "tiling/tiling_api.h"
#include "../op_kernel/fused_gdn_gating_tiling_data.h"
using namespace FusedGdnGating;
namespace optiling {
namespace {
constexpr uint64_t TILING_KEY_BF16 = 1;
constexpr uint64_t TILING_KEY_FP16 = 2;
constexpr uint64_t TILING_KEY_PARAM_BF16_OFFSET = 2;
constexpr uint64_t TILING_KEY_PARAM_FP16_OFFSET = 4;
constexpr size_t INPUT_INDEX_A_LOG = 0;
constexpr size_t INPUT_INDEX_A = 1;
constexpr size_t INPUT_INDEX_DT_BIAS = 3;
} // namespace
ge::graphStatus FusedGdnGatingTilingFunc(gert::TilingContext *context)
{
if (context == nullptr) {
return ge::GRAPH_FAILED;
}
auto platformInfoPtr = context->GetPlatformInfo();
if (platformInfoPtr == nullptr) {
return ge::GRAPH_FAILED;
}
auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr);
uint64_t ubSize = 0;
ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize);
uint32_t aivNum = ascendcPlatform.GetCoreNumAiv();
if (aivNum == 0) {
aivNum = 1;
}
auto *shapeA = context->GetInputShape(INPUT_INDEX_A);
if (shapeA == nullptr) {
return ge::GRAPH_FAILED;
}
const auto &storageShape = shapeA->GetStorageShape();
if (storageShape.GetDimNum() < 2) {
return ge::GRAPH_FAILED;
}
int64_t numBatches = storageShape.GetDim(0);
int64_t numHeads = storageShape.GetDim(1);
if (numBatches <= 0 || numHeads <= 0) {
return ge::GRAPH_FAILED;
}
float beta = 1.0f;
float threshold = 20.0f;
auto *attrs = context->GetAttrs();
if (attrs != nullptr) {
const float *betaAttr = attrs->GetAttrPointer<float>(0);
if (betaAttr != nullptr) { beta = *betaAttr; }
const float *thresholdAttr = attrs->GetAttrPointer<float>(1);
if (thresholdAttr != nullptr) { threshold = *thresholdAttr; }
}
auto *aDesc = context->GetInputDesc(INPUT_INDEX_A);
auto *aLogDesc = context->GetInputDesc(INPUT_INDEX_A_LOG);
auto *dtBiasDesc = context->GetInputDesc(INPUT_INDEX_DT_BIAS);
if (aDesc == nullptr || aLogDesc == nullptr || dtBiasDesc == nullptr) {
return ge::GRAPH_FAILED;
}
ge::DataType aDtype = aDesc->GetDataType();
ge::DataType aLogDtype = aLogDesc->GetDataType();
ge::DataType dtBiasDtype = dtBiasDesc->GetDataType();
if (aLogDtype != dtBiasDtype) {
return ge::GRAPH_FAILED;
}
uint64_t tilingKey = TILING_KEY_BF16;
if (aDtype == ge::DT_FLOAT16) {
tilingKey = TILING_KEY_FP16;
}
if (aLogDtype == ge::DT_BF16) {
tilingKey += TILING_KEY_PARAM_BF16_OFFSET;
} else if (aLogDtype == ge::DT_FLOAT16) {
tilingKey += TILING_KEY_PARAM_FP16_OFFSET;
}
uint32_t blockDim = static_cast<uint32_t>(numBatches);
if (blockDim > aivNum) {
blockDim = aivNum;
}
uint32_t numHeadsU32 = static_cast<uint32_t>(numHeads);
uint32_t numBatchesU32 = static_cast<uint32_t>(numBatches);
uint32_t rowsConservative = ComputeRowsPerIter(numHeadsU32, ubSize);
uint32_t rowsPerIter = rowsConservative;
// Block utilization: ensure enough chunks for all AIV cores.
{
uint32_t totalChunksForRPI = (numBatchesU32 + rowsPerIter - 1) / rowsPerIter;
if (numBatchesU32 <= rowsPerIter || totalChunksForRPI < blockDim) {
uint32_t maxRPI = numBatchesU32 / blockDim;
if (maxRPI < 1) { maxRPI = 1; }
if (maxRPI >= 128) { rowsPerIter = 128; }
else if (maxRPI >= 64) { rowsPerIter = 64; }
else if (maxRPI >= 32) { rowsPerIter = 32; }
else if (maxRPI >= 16) { rowsPerIter = 16; }
else if (maxRPI >= 8) { rowsPerIter = 8; }
else if (maxRPI >= 4) { rowsPerIter = 4; }
else if (maxRPI >= 2) { rowsPerIter = 2; }
else { rowsPerIter = 1; }
if (rowsPerIter > rowsConservative) { rowsPerIter = rowsConservative; }
}
}
const bool bulkDmaBatchOk = (numBatchesU32 > blockDim * rowsPerIter);
bool useBulkDma = bulkDmaBatchOk && CanUseBulkDma(numHeadsU32, rowsPerIter);
FusedGdnGatingTilingData td{};
td.numHeads = numHeadsU32;
td.numBatches = numBatchesU32;
td.rowsPerIter = rowsPerIter;
td.useBulkDma = useBulkDma ? 1u : 0u;
td.beta = beta;
td.threshold = threshold;
const size_t tilingSize = sizeof(FusedGdnGatingTilingData);
auto *rawTilingData = context->GetRawTilingData();
if (rawTilingData == nullptr || rawTilingData->GetCapacity() < tilingSize) {
return ge::GRAPH_FAILED;
}
errno_t rc = memcpy_s(rawTilingData->GetData(), rawTilingData->GetCapacity(),
&td, tilingSize);
if (rc != EOK) {
return ge::GRAPH_FAILED;
}
rawTilingData->SetDataSize(tilingSize);
context->SetBlockDim(blockDim);
context->SetTilingKey(tilingKey);
// No GM workspace needed.
size_t *workspaces = context->GetWorkspaceSizes(1);
if (workspaces != nullptr) {
workspaces[0] = 0;
}
return ge::GRAPH_SUCCESS;
}
ge::graphStatus TilingPrepareForFusedGdnGating(gert::TilingParseContext *context)
{
// Required by CANN tiling framework for "_pattern" registration.
(void)context;
return ge::GRAPH_SUCCESS;
}
} // namespace optiling
IMPL_OP_OPTILING(FusedGdnGating)
.Tiling(optiling::FusedGdnGatingTilingFunc)
.TilingParse<optiling::FusedGdnGatingCompileInfo>(optiling::TilingPrepareForFusedGdnGating);

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating_tiling.h
* \brief Function-style tiling declaration for FusedGdnGating.
*/
#ifndef FUSED_GDN_GATING_TILING_H
#define FUSED_GDN_GATING_TILING_H
#include <cstdint>
#include <exe_graph/runtime/tiling_context.h>
#include <exe_graph/runtime/tiling_parse_context.h>
namespace optiling {
// Required by CANN tiling framework.
struct FusedGdnGatingCompileInfo {};
ge::graphStatus FusedGdnGatingTilingFunc(gert::TilingContext *context);
ge::graphStatus TilingPrepareForFusedGdnGating(gert::TilingParseContext *context);
} // namespace optiling
#endif // FUSED_GDN_GATING_TILING_H

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating_tiling_utils.h
* \brief rowsPerIter and Bulk DMA helper functions.
*/
#ifndef FUSED_GDN_GATING_TILING_UTILS_H
#define FUSED_GDN_GATING_TILING_UTILS_H
#include <cstdint>
namespace FusedGdnGating {
// NPU hardware constants.
constexpr uint32_t VECTOR_BYTES_PER_ITER = 256;
constexpr uint32_t DATACOPY_MIN_BYTES = 32;
constexpr uint32_t BF16_PER_BLOCK = DATACOPY_MIN_BYTES / 2; // 16
constexpr uint32_t MASK_ALIGN_ELEMS = 64;
/// Align count to vector unit width (256 bytes) for given dtype size.
inline uint32_t AlignCountToVectorBytes(uint32_t count, uint32_t dtypeSize)
{
uint32_t elemsPerIter = VECTOR_BYTES_PER_ITER / dtypeSize;
return ((count + elemsPerIter - 1) / elemsPerIter) * elemsPerIter;
}
/// Check if Bulk DMA is viable: (R * nh) % 64 == 0, nh % 16 == 0.
inline bool CanUseBulkDma(uint32_t numHeads, uint32_t rowsPerIter)
{
// Condition 1: (rows_per_iter * num_heads) % 64 == 0
// fp32 vector unit processes 64 elements per repeat; the bulk operation
// must align with this granularity to avoid tail handling.
constexpr uint32_t fp32VecElems = VECTOR_BYTES_PER_ITER / 4;
if ((rowsPerIter * numHeads) % fp32VecElems != 0) {
return false;
}
// Condition 2: num_heads % 16 == 0
// DMA minimum transfer size is 32 bytes; for bf16/fp16 (2 bytes per element),
// this equals 16 elements. If num_heads is not a multiple of 16, the last
// few elements of each row require separate handling, negating the bulk benefit.
constexpr uint32_t bf16BlockElems = DATACOPY_MIN_BYTES / 2;
if (numHeads % bf16BlockElems != 0) {
return false;
}
return true;
}
/*!
* \brief Compute optimal rows_per_iter from UB budget.
*
* UB breakdown matches kernel Init(): 3 single-row fp32 constants
* + 2 multi-row fp32 constants (R * ubDim * 4 each)
* + 3 half + 6 fp32 per-row buffers (scaled by R).
* ubDim = ceil(numHeads / 16) * 16 (matching kernel DMA_ALIGN_ELEMS).
* Result clamped to power-of-2, max 128.
*/
inline uint32_t ComputeRowsPerIter(uint32_t numHeads, uint64_t ubBudget,
uint32_t ubDim = 0)
{
if (ubDim == 0) {
// Match the kernel's fp32 compute/mask alignment.
ubDim = ((numHeads + MASK_ALIGN_ELEMS - 1) / MASK_ALIGN_ELEMS) * MASK_ALIGN_ELEMS;
}
uint32_t maskUbDim = ubDim;
// 2 parameter input queues + 2 fp32 constant buffers, each 1 row.
// Use fp32 for the parameter queues as a conservative upper bound.
uint32_t sharedBytes = 4 * ubDim * static_cast<uint32_t>(sizeof(float));
// Multi-row constant buffers (precomputed once, scaled by R):
// dtBiasMultiBuf_ + negExpMultiBuf_: 2 fp32 buffers.
uint32_t constPerRowBytes = 2 * ubDim * static_cast<uint32_t>(sizeof(float));
// Per-row (per-chunk): 3 bf16/fp16 buffers + 5 fp32 buffers + 1 uint8 mask buffer.
uint32_t perRowBytes = 3 * ubDim * static_cast<uint32_t>(sizeof(int16_t)) // a, b, betaOut
+ 5 * ubDim * static_cast<uint32_t>(sizeof(float)) // g, x, betaX, tmp, betaFp32
+ 1 * maskUbDim * static_cast<uint32_t>(sizeof(uint8_t)); // threshold mask
if (perRowBytes == 0) {
return 1;
}
uint32_t maxRows = 1;
if (ubBudget > sharedBytes) {
maxRows = static_cast<uint32_t>((ubBudget - sharedBytes) / (perRowBytes + constPerRowBytes));
}
// Round down to nearest power of 2 (128, 64, 32, ..., 1).
if (maxRows >= 128) { return 128; }
if (maxRows >= 64) { return 64; }
if (maxRows >= 32) { return 32; }
if (maxRows >= 16) { return 16; }
if (maxRows >= 8) { return 8; }
if (maxRows >= 4) { return 4; }
if (maxRows >= 2) { return 2; }
return 1;
}
} // namespace FusedGdnGating
#endif // FUSED_GDN_GATING_TILING_UTILS_H

View File

@@ -0,0 +1,143 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file aclnn_fused_gdn_gating.cpp
* \brief ACLNN C-API (GetWorkspaceSize + Execute).
*/
#include <dlfcn.h>
#include "aclnn_fused_gdn_gating.h"
#include "fused_gdn_gating.h"
#include "securec.h"
#include "aclnn_kernels/common/op_error_check.h"
#include "opdev/common_types.h"
#include "opdev/op_dfx.h"
#include "opdev/op_executor.h"
#include "opdev/op_log.h"
#include "opdev/platform.h"
#include "aclnn_kernels/contiguous.h"
using namespace op;
#ifdef __cplusplus
extern "C" {
#endif
namespace {
struct FusedGdnGatingParams {
const aclTensor *aLog{nullptr};
const aclTensor *a{nullptr};
const aclTensor *b{nullptr};
const aclTensor *dtBias{nullptr};
float beta{1.0f};
float threshold{20.0f};
aclTensor *g{nullptr};
aclTensor *betaOutput{nullptr};
};
static const std::initializer_list<op::DataType> AB_TYPE_SUPPORT_LIST =
{op::DataType::DT_BF16, op::DataType::DT_FLOAT16};
static const std::initializer_list<op::DataType> FP32_TYPE_SUPPORT_LIST =
{op::DataType::DT_FLOAT};
static const std::initializer_list<op::DataType> PARAM_TYPE_SUPPORT_LIST =
{op::DataType::DT_FLOAT, op::DataType::DT_BF16, op::DataType::DT_FLOAT16};
static inline bool CheckNotNull(const FusedGdnGatingParams &params)
{
OP_CHECK_NULL(params.aLog, return false);
OP_CHECK_NULL(params.a, return false);
OP_CHECK_NULL(params.b, return false);
OP_CHECK_NULL(params.dtBias, return false);
OP_CHECK_NULL(params.g, return false);
OP_CHECK_NULL(params.betaOutput, return false);
return true;
}
static inline bool CheckDtype(const FusedGdnGatingParams &params)
{
OP_CHECK_DTYPE_NOT_SUPPORT(params.aLog, PARAM_TYPE_SUPPORT_LIST, return false);
OP_CHECK_DTYPE_NOT_SUPPORT(params.dtBias, PARAM_TYPE_SUPPORT_LIST, return false);
OP_CHECK_DTYPE_NOT_SUPPORT(params.a, AB_TYPE_SUPPORT_LIST, return false);
OP_CHECK_DTYPE_NOT_SUPPORT(params.b, AB_TYPE_SUPPORT_LIST, return false);
OP_CHECK_DTYPE_NOT_SUPPORT(params.g, FP32_TYPE_SUPPORT_LIST, return false);
OP_CHECK_DTYPE_NOT_SUPPORT(params.betaOutput, AB_TYPE_SUPPORT_LIST, return false);
OP_CHECK(params.a->GetDataType() == params.b->GetDataType(),
OP_LOGE(ACLNN_ERR_PARAM_INVALID, "a and b must have the same dtype."),
return false);
OP_CHECK(params.aLog->GetDataType() == params.dtBias->GetDataType(),
OP_LOGE(ACLNN_ERR_PARAM_INVALID, "aLog and dtBias must have the same dtype."),
return false);
OP_CHECK(params.betaOutput->GetDataType() == params.b->GetDataType(),
OP_LOGE(ACLNN_ERR_PARAM_INVALID, "betaOutput and b must have the same dtype."),
return false);
return true;
}
static aclnnStatus CheckParams(const FusedGdnGatingParams &params)
{
CHECK_RET(CheckNotNull(params), ACLNN_ERR_PARAM_NULLPTR);
CHECK_RET(CheckDtype(params), ACLNN_ERR_PARAM_INVALID);
return ACLNN_SUCCESS;
}
} // namespace
aclnnStatus aclnnFusedGdnGatingGetWorkspaceSize(
const aclTensor *aLog, const aclTensor *a, const aclTensor *b,
const aclTensor *dtBias, float beta, float threshold,
aclTensor *g, aclTensor *betaOutput,
uint64_t *workspaceSize, aclOpExecutor **executor)
{
L2_DFX_PHASE_1(aclnnFusedGdnGating,
DFX_IN(aLog, a, b, dtBias, beta, threshold),
DFX_OUT(g, betaOutput));
auto uniqueExecutor = CREATE_EXECUTOR();
CHECK_RET(uniqueExecutor.get() != nullptr, ACLNN_ERR_INNER_CREATE_EXECUTOR);
FusedGdnGatingParams params{aLog, a, b, dtBias, beta, threshold, g, betaOutput};
CHECK_RET(CheckParams(params) == ACLNN_SUCCESS, ACLNN_ERR_PARAM_INVALID);
// Bring inputs to a contiguous form that the kernel expects.
auto aLogContig = l0op::Contiguous(aLog, uniqueExecutor.get());
auto aContig = l0op::Contiguous(a, uniqueExecutor.get());
auto bContig = l0op::Contiguous(b, uniqueExecutor.get());
auto dtBiasContig = l0op::Contiguous(dtBias, uniqueExecutor.get());
CHECK_RET(aLogContig != nullptr, ACLNN_ERR_INNER_NULLPTR);
CHECK_RET(aContig != nullptr, ACLNN_ERR_INNER_NULLPTR);
CHECK_RET(bContig != nullptr, ACLNN_ERR_INNER_NULLPTR);
CHECK_RET(dtBiasContig != nullptr, ACLNN_ERR_INNER_NULLPTR);
auto result = l0op::FusedGdnGating(aLogContig, aContig, bContig, dtBiasContig,
beta, threshold, uniqueExecutor.get());
CHECK_RET(result.g != nullptr && result.beta_output != nullptr,
ACLNN_ERR_INNER_NULLPTR);
// Copy kernel results into the caller-provided output tensors.
auto vcG = l0op::ViewCopy(result.g, g, uniqueExecutor.get());
CHECK_RET(vcG != nullptr, ACLNN_ERR_INNER_NULLPTR);
auto vcBeta = l0op::ViewCopy(result.beta_output, betaOutput, uniqueExecutor.get());
CHECK_RET(vcBeta != nullptr, ACLNN_ERR_INNER_NULLPTR);
*workspaceSize = uniqueExecutor->GetWorkspaceSize();
uniqueExecutor.ReleaseTo(executor);
return ACLNN_SUCCESS;
}
aclnnStatus aclnnFusedGdnGating(void *workspace, uint64_t workspaceSize,
aclOpExecutor *executor, aclrtStream stream)
{
L2_DFX_PHASE_2(aclnnFusedGdnGating);
return CommonOpExecutorRun(workspace, workspaceSize, executor, stream);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file aclnn_fused_gdn_gating.h
* \brief ACLNN C-API for FusedGdnGating.
*/
#ifndef OP_API_ACLNN_FUSED_GDN_GATING_H
#define OP_API_ACLNN_FUSED_GDN_GATING_H
#include "aclnn/aclnn_base.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief FusedGdnGating phase-1: compute required workspace size.
* @param [in] aLog : A_log, [num_heads], dtype fp32/bf16/fp16.
* @param [in] a : a, [batch, num_heads], dtype bf16/fp16.
* @param [in] b : b, [batch, num_heads], dtype bf16/fp16.
* @param [in] dtBias : dt_bias, [num_heads], same dtype as aLog.
* @param [in] beta : softplus beta (default 1.0).
* @param [in] threshold : softplus threshold (default 20.0).
* @param [out] g : output gate, [1, batch, num_heads], dtype fp32.
* @param [out] betaOutput : sigmoid(b), [1, batch, num_heads], same dtype as a/b.
* @param [out] workspaceSize: required workspace bytes on device.
* @param [out] executor : op executor handle.
*/
__attribute__((visibility("default"))) aclnnStatus aclnnFusedGdnGatingGetWorkspaceSize(
const aclTensor *aLog, const aclTensor *a, const aclTensor *b,
const aclTensor *dtBias, float beta, float threshold,
aclTensor *g, aclTensor *betaOutput,
uint64_t *workspaceSize, aclOpExecutor **executor);
/**
* @brief FusedGdnGating phase-2: launch the kernel.
*/
__attribute__((visibility("default"))) aclnnStatus aclnnFusedGdnGating(
void *workspace, uint64_t workspaceSize,
aclOpExecutor *executor, aclrtStream stream);
#ifdef __cplusplus
}
#endif
#endif // OP_API_ACLNN_FUSED_GDN_GATING_H

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating.cpp
* \brief L0-level API for FusedGdnGating.
*/
#include "fused_gdn_gating.h"
#include "aclnn_kernels/common/op_error_check.h"
#include "opdev/make_op_executor.h"
#include "opdev/op_def.h"
#include "opdev/op_dfx.h"
#include "opdev/op_executor.h"
#include "opdev/op_log.h"
#include "opdev/shape_utils.h"
using namespace op;
namespace l0op {
OP_TYPE_REGISTER(FusedGdnGating);
static constexpr FusedGdnGatingOutput kNullOutput{nullptr, nullptr};
FusedGdnGatingOutput FusedGdnGating(const aclTensor *aLog, const aclTensor *a,
const aclTensor *b, const aclTensor *dtBias,
float beta, float threshold,
aclOpExecutor *executor)
{
L0_DFX(FusedGdnGating, aLog, a, b, dtBias, beta, threshold);
const DataType betaDtype = b->GetDataType();
const Format format = Format::FORMAT_ND;
auto g = executor->AllocTensor(DataType::DT_FLOAT, format, format);
OP_CHECK(g != nullptr, OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "g AllocTensor failed."),
return kNullOutput);
auto betaOutput = executor->AllocTensor(betaDtype, format, format);
OP_CHECK(betaOutput != nullptr,
OP_LOGE(ACLNN_ERR_INNER_NULLPTR, "beta_output AllocTensor failed."),
return kNullOutput);
auto ret = INFER_SHAPE(FusedGdnGating,
OP_INPUT(aLog, a, b, dtBias),
OP_OUTPUT(g, betaOutput),
OP_ATTR(beta, threshold));
OP_CHECK_INFERSHAPE(ret != ACLNN_SUCCESS, return kNullOutput,
"FusedGdnGating InferShape failed.");
ret = ADD_TO_LAUNCHER_LIST_AICORE(FusedGdnGating,
OP_INPUT(aLog, a, b, dtBias),
OP_OUTPUT(g, betaOutput),
OP_ATTR(beta, threshold));
OP_CHECK_ADD_TO_LAUNCHER_LIST_AICORE(ret != ACLNN_SUCCESS, return kNullOutput,
"FusedGdnGating ADD_TO_LAUNCHER_LIST_AICORE failed.");
return FusedGdnGatingOutput{g, betaOutput};
}
} // namespace l0op

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
#ifndef PTA_NPU_OP_API_FUSED_GDN_GATING_H
#define PTA_NPU_OP_API_FUSED_GDN_GATING_H
#include "opdev/op_executor.h"
#include "opdev/make_op_executor.h"
namespace l0op {
struct FusedGdnGatingOutput {
const aclTensor *g;
const aclTensor *beta_output;
};
FusedGdnGatingOutput FusedGdnGating(const aclTensor *aLog, const aclTensor *a,
const aclTensor *b, const aclTensor *dtBias,
float beta, float threshold,
aclOpExecutor *executor);
} // namespace l0op
#endif // PTA_NPU_OP_API_FUSED_GDN_GATING_H

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating.cpp
* \brief AscendC kernel entry for FusedGdnGating.
*/
#include "fused_gdn_gating.h"
#include "fused_gdn_gating_tiling_data.h"
using namespace AscendC;
using namespace FusedGdnGating;
extern "C" __global__ __aicore__ void
fused_gdn_gating(GM_ADDR a_log, GM_ADDR a, GM_ADDR b, GM_ADDR dt_bias,
GM_ADDR g, GM_ADDR beta_output,
GM_ADDR workspace, GM_ADDR tiling_gm)
{
REGISTER_TILING_DEFAULT(FusedGdnGatingTilingData);
GET_TILING_DATA(tilingData, tiling_gm);
KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
TPipe pipe;
if (TILING_KEY_IS(1)) {
KernelFusedGdnGating<bfloat16_t, float> op;
op.Init(a_log, a, b, dt_bias, g, beta_output, &tilingData, &pipe);
op.Process();
} else if (TILING_KEY_IS(2)) {
KernelFusedGdnGating<half, float> op;
op.Init(a_log, a, b, dt_bias, g, beta_output, &tilingData, &pipe);
op.Process();
} else if (TILING_KEY_IS(3)) {
KernelFusedGdnGating<bfloat16_t, bfloat16_t> op;
op.Init(a_log, a, b, dt_bias, g, beta_output, &tilingData, &pipe);
op.Process();
} else if (TILING_KEY_IS(4)) {
KernelFusedGdnGating<half, bfloat16_t> op;
op.Init(a_log, a, b, dt_bias, g, beta_output, &tilingData, &pipe);
op.Process();
} else if (TILING_KEY_IS(5)) {
KernelFusedGdnGating<bfloat16_t, half> op;
op.Init(a_log, a, b, dt_bias, g, beta_output, &tilingData, &pipe);
op.Process();
} else if (TILING_KEY_IS(6)) {
KernelFusedGdnGating<half, half> op;
op.Init(a_log, a, b, dt_bias, g, beta_output, &tilingData, &pipe);
op.Process();
}
}

View File

@@ -0,0 +1,396 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating.h
* \brief AscendC kernel for fused GDN gating.
*
* Per-row math:
* g = -exp(A_log) * softplus(cast(a,fp32) + dt_bias, beta, threshold)
* beta_output = sigmoid(cast(b, fp32)) -> cast back to InDtype
*/
#ifndef FUSED_GDN_GATING_KERNEL_H
#define FUSED_GDN_GATING_KERNEL_H
#include <type_traits>
#include "kernel_operator.h"
#include "fused_gdn_gating_tiling_data.h"
namespace FusedGdnGating {
using namespace AscendC;
// 32-byte alignment requirement for DataCopy on NPU.
constexpr uint32_t BYTES_PER_BLOCK = 32;
constexpr uint32_t BF16_PER_BLOCK = BYTES_PER_BLOCK / sizeof(int16_t); // 16
constexpr uint32_t FP32_PER_BLOCK = BYTES_PER_BLOCK / sizeof(float); // 8
constexpr uint32_t MASK_ALIGN_ELEMS = 64;
// DMA-friendly alignment: 16 elements = 32 bytes = 1 DMA block.
// Vector ops use count=numHeads_ with partial-iteration masking,
// so there is no minimum-count constraint.
constexpr uint32_t DMA_ALIGN_ELEMS = BYTES_PER_BLOCK / sizeof(int16_t); // 16
template <typename T>
__aicore__ inline T CeilDiv(T a, T b) { return (a + b - 1) / b; }
template <typename T>
__aicore__ inline T AlignUp(T a, T b) { return CeilDiv(a, b) * b; }
template <typename InDtype, typename ParamDtype>
class KernelFusedGdnGating {
public:
__aicore__ inline KernelFusedGdnGating() {}
/*!
* \brief Init kernel with GM addresses and tiling data.
*
* Argument order matches OpDef: aLogGm, aGm, bGm, dtBiasGm, gGm, betaOutputGm.
*/
__aicore__ inline void Init(GM_ADDR aLogGm, GM_ADDR aGm, GM_ADDR bGm, GM_ADDR dtBiasGm,
GM_ADDR gGm, GM_ADDR betaOutputGm,
const FusedGdnGatingTilingData *tiling, TPipe *pipe)
{
pipe_ = pipe;
numHeads_ = tiling->numHeads;
numBatches_ = tiling->numBatches;
rowsPerIter_ = tiling->rowsPerIter;
useBulkDma_ = (tiling->useBulkDma != 0);
beta_ = tiling->beta;
threshold_ = tiling->threshold;
// Aligned dimensions for UB tensors.
alignedHeadsHalf_ = AlignUp<uint32_t>(numHeads_, MASK_ALIGN_ELEMS);
alignedHeadsFloat_ = AlignUp<uint32_t>(numHeads_, MASK_ALIGN_ELEMS);
alignedHeadsMask_ = alignedHeadsFloat_;
constexpr uint32_t paramAlignElems = BYTES_PER_BLOCK / sizeof(ParamDtype);
alignedHeadsParam_ = AlignUp<uint32_t>(numHeads_, paramAlignElems);
aLogGm_.SetGlobalBuffer(reinterpret_cast<__gm__ ParamDtype *>(aLogGm), numHeads_);
dtBiasGm_.SetGlobalBuffer(reinterpret_cast<__gm__ ParamDtype *>(dtBiasGm), numHeads_);
aGm_.SetGlobalBuffer(reinterpret_cast<__gm__ InDtype *>(aGm),
static_cast<uint64_t>(numBatches_) * numHeads_);
bGm_.SetGlobalBuffer(reinterpret_cast<__gm__ InDtype *>(bGm),
static_cast<uint64_t>(numBatches_) * numHeads_);
gGm_.SetGlobalBuffer(reinterpret_cast<__gm__ float *>(gGm),
static_cast<uint64_t>(numBatches_) * numHeads_);
betaGm_.SetGlobalBuffer(reinterpret_cast<__gm__ InDtype *>(betaOutputGm),
static_cast<uint64_t>(numBatches_) * numHeads_);
// I/O queues (depth=1).
pipe_->InitBuffer(aInQue_, 1, rowsPerIter_ * alignedHeadsHalf_ * sizeof(InDtype));
pipe_->InitBuffer(bInQue_, 1, rowsPerIter_ * alignedHeadsHalf_ * sizeof(InDtype));
pipe_->InitBuffer(gOutQue_, 1, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
pipe_->InitBuffer(betaOutQue_, 1, rowsPerIter_ * alignedHeadsHalf_ * sizeof(InDtype));
// Constant queues (single-row).
pipe_->InitBuffer(aLogInQue_, 1, 1 * alignedHeadsParam_ * sizeof(ParamDtype));
pipe_->InitBuffer(dtBiasInQue_, 1, 1 * alignedHeadsParam_ * sizeof(ParamDtype));
pipe_->InitBuffer(negExpInQue_, 1, 1 * alignedHeadsFloat_ * sizeof(float));
pipe_->InitBuffer(dtBiasPreloadQue_, 1, 1 * alignedHeadsFloat_ * sizeof(float));
// Multi-row constants: dt_bias and neg_exp(A_log) replicated R times.
// Only allocated for R > 1; single-row kernels use per-row fallback.
if (rowsPerIter_ > 1) {
pipe_->InitBuffer(dtBiasMultiBuf_, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
pipe_->InitBuffer(negExpMultiBuf_, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
}
// Scratch buffers (V-only access).
pipe_->InitBuffer(xBuf_, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
pipe_->InitBuffer(betaXBuf_, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
pipe_->InitBuffer(softplusTmpBuf_, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
pipe_->InitBuffer(thresholdMaskBuf_, rowsPerIter_ * alignedHeadsMask_ * sizeof(uint8_t));
pipe_->InitBuffer(betaFp32Buf_, rowsPerIter_ * alignedHeadsFloat_ * sizeof(float));
}
__aicore__ inline void Process()
{
PreloadConstants();
uint32_t blockIdx = GetBlockIdx();
uint32_t blockNum = GetBlockNum();
if (blockNum == 0) { blockNum = 1; }
// Chunk-based task distribution.
uint32_t totalChunks = CeilDiv<uint32_t>(numBatches_, rowsPerIter_);
uint32_t chunksPerBlock = CeilDiv<uint32_t>(totalChunks, blockNum);
uint32_t chunkStart = blockIdx * chunksPerBlock;
uint32_t chunkEnd = chunkStart + chunksPerBlock;
if (chunkEnd > totalChunks) { chunkEnd = totalChunks; }
for (uint32_t chunk = chunkStart; chunk < chunkEnd; ++chunk) {
ProcessOneChunk(chunk);
}
}
private:
/*!
* \brief Preload A_log, neg_exp(A_log), dt_bias, and multi-row replicas.
*/
__aicore__ inline void PreloadConstants()
{
LocalTensor<ParamDtype> tmpALog = aLogInQue_.template AllocTensor<ParamDtype>();
dtBiasTensor_ = negExpInQue_.template AllocTensor<float>();
DataCopyExtParams paramCopyParams{1, static_cast<uint32_t>(numHeads_ * sizeof(ParamDtype)),
0, 0, 0};
DataCopyPadExtParams<ParamDtype> paramPadParams{false, 0, 0, static_cast<ParamDtype>(0)};
// Load A_log.
DataCopyPad(tmpALog, aLogGm_, paramCopyParams, paramPadParams);
aLogInQue_.template EnQue<ParamDtype>(tmpALog);
tmpALog = aLogInQue_.template DeQue<ParamDtype>();
if constexpr (std::is_same<ParamDtype, float>()) {
Adds(dtBiasTensor_, tmpALog, 0.0f, numHeads_);
} else {
Cast(dtBiasTensor_, tmpALog, RoundMode::CAST_NONE, numHeads_);
}
PipeBarrier<PIPE_V>();
// neg_exp(A_log).
Exp(dtBiasTensor_, dtBiasTensor_, numHeads_);
PipeBarrier<PIPE_V>();
Muls(dtBiasTensor_, dtBiasTensor_, -1.0f, numHeads_);
PipeBarrier<PIPE_V>();
aLogInQue_.FreeTensor(tmpALog);
negExpInQue_.template EnQue<float>(dtBiasTensor_);
dtBiasTensor_ = negExpInQue_.template DeQue<float>();
// Load dt_bias.
LocalTensor<ParamDtype> tmpDtBias = dtBiasInQue_.template AllocTensor<ParamDtype>();
dtBiasPreloaded_ = dtBiasPreloadQue_.template AllocTensor<float>();
DataCopyPad(tmpDtBias, dtBiasGm_, paramCopyParams, paramPadParams);
dtBiasInQue_.template EnQue<ParamDtype>(tmpDtBias);
tmpDtBias = dtBiasInQue_.template DeQue<ParamDtype>();
if constexpr (std::is_same<ParamDtype, float>()) {
Adds(dtBiasPreloaded_, tmpDtBias, 0.0f, numHeads_);
} else {
Cast(dtBiasPreloaded_, tmpDtBias, RoundMode::CAST_NONE, numHeads_);
}
PipeBarrier<PIPE_V>();
dtBiasInQue_.FreeTensor(tmpDtBias);
dtBiasPreloadQue_.template EnQue<float>(dtBiasPreloaded_);
dtBiasPreloaded_ = dtBiasPreloadQue_.template DeQue<float>();
// Replicate to multi-row buffers (skip for single-row kernels).
if (rowsPerIter_ > 1) {
LocalTensor<float> dtBiasMulti = dtBiasMultiBuf_.Get<float>();
LocalTensor<float> negExpMulti = negExpMultiBuf_.Get<float>();
for (uint32_t r = 0; r < rowsPerIter_; ++r) {
const uint32_t off = r * alignedHeadsFloat_;
Adds(dtBiasMulti[off], dtBiasPreloaded_, 0.0f, numHeads_);
Adds(negExpMulti[off], dtBiasTensor_, 0.0f, numHeads_);
}
PipeBarrier<PIPE_V>();
}
}
__aicore__ inline void ProcessOneChunk(uint32_t chunkIdx)
{
const uint32_t baseRow = chunkIdx * rowsPerIter_;
if (baseRow >= numBatches_) {
return;
}
const uint32_t remaining = (numBatches_ > baseRow) ? (numBatches_ - baseRow) : 0;
const uint32_t validRows = (remaining >= rowsPerIter_) ? rowsPerIter_ : remaining;
const bool isFullChunk = (validRows == rowsPerIter_);
LocalTensor<InDtype> aLocal = aInQue_.template AllocTensor<InDtype>();
LocalTensor<InDtype> bLocal = bInQue_.template AllocTensor<InDtype>();
LocalTensor<float> gLocal = gOutQue_.template AllocTensor<float>();
LocalTensor<InDtype> betaLocal = betaOutQue_.template AllocTensor<InDtype>();
// MTE2: Load input.
if (useBulkDma_ && isFullChunk) {
const uint64_t rowOffset = static_cast<uint64_t>(baseRow) * numHeads_;
const uint32_t rowBytesHalf = numHeads_ * static_cast<uint32_t>(sizeof(InDtype));
const uint32_t inputDstGap =
(alignedHeadsHalf_ - numHeads_) * static_cast<uint32_t>(sizeof(InDtype)) / BYTES_PER_BLOCK;
DataCopyExtParams bulkCopyParams{static_cast<uint16_t>(rowsPerIter_),
rowBytesHalf, 0, inputDstGap, 0};
DataCopyPadExtParams<InDtype> bulkPadParams{false, 0, 0, static_cast<InDtype>(0)};
DataCopyPad(aLocal, aGm_[rowOffset], bulkCopyParams, bulkPadParams);
DataCopyPad(bLocal, bGm_[rowOffset], bulkCopyParams, bulkPadParams);
} else {
for (uint32_t r = 0; r < validRows; ++r) {
const uint64_t rowOffset = static_cast<uint64_t>(baseRow + r) * numHeads_;
DataCopyExtParams rowCopyParams{1, static_cast<uint32_t>(numHeads_ * sizeof(InDtype)), 0, 0, 0};
DataCopyPadExtParams<InDtype> rowPadParams{false, 0, 0, static_cast<InDtype>(0)};
DataCopyPad(aLocal[r * alignedHeadsHalf_], aGm_[rowOffset], rowCopyParams, rowPadParams);
DataCopyPad(bLocal[r * alignedHeadsHalf_], bGm_[rowOffset], rowCopyParams, rowPadParams);
}
}
aInQue_.template EnQue<InDtype>(aLocal);
bInQue_.template EnQue<InDtype>(bLocal);
aLocal = aInQue_.template DeQue<InDtype>();
bLocal = bInQue_.template DeQue<InDtype>();
LocalTensor<float> x = xBuf_.Get<float>();
LocalTensor<float> betaX = betaXBuf_.Get<float>();
LocalTensor<float> softplusTmp = softplusTmpBuf_.Get<float>();
LocalTensor<uint8_t> thresholdMask = thresholdMaskBuf_.Get<uint8_t>();
LocalTensor<float> betaFp32 = betaFp32Buf_.Get<float>();
const uint32_t multiCount = validRows * alignedHeadsFloat_;
const uint32_t maskCount = validRows * alignedHeadsMask_;
// Batch Cast a→fp32, b→fp32.
Cast(x, aLocal, RoundMode::CAST_NONE, multiCount);
Cast(betaFp32, bLocal, RoundMode::CAST_NONE, multiCount);
PipeBarrier<PIPE_V>();
if (rowsPerIter_ > 1) {
// Multi-row path: dt_bias and neg_exp from precomputed buffers.
LocalTensor<float> dtBiasMulti = dtBiasMultiBuf_.Get<float>();
LocalTensor<float> negExpMulti = negExpMultiBuf_.Get<float>();
Add(x, x, dtBiasMulti, multiCount);
PipeBarrier<PIPE_V>();
Muls(betaX, x, beta_, multiCount);
PipeBarrier<PIPE_V>();
Mins(softplusTmp, betaX, threshold_, multiCount);
PipeBarrier<PIPE_V>();
Exp(softplusTmp, softplusTmp, multiCount);
PipeBarrier<PIPE_V>();
Adds(softplusTmp, softplusTmp, 1.0f, multiCount);
PipeBarrier<PIPE_V>();
Ln(softplusTmp, softplusTmp, multiCount);
PipeBarrier<PIPE_V>();
Muls(softplusTmp, softplusTmp, 1.0f / beta_, multiCount);
PipeBarrier<PIPE_V>();
CompareScalar(thresholdMask, betaX, threshold_, CMPMODE::LE, maskCount);
PipeBarrier<PIPE_V>();
Select(gLocal, thresholdMask, softplusTmp, x, SELMODE::VSEL_TENSOR_TENSOR_MODE, multiCount);
PipeBarrier<PIPE_V>();
Mul(gLocal, gLocal, negExpMulti, multiCount);
PipeBarrier<PIPE_V>();
} else {
// Single-row fallback.
Add(x, x, dtBiasPreloaded_, numHeads_);
PipeBarrier<PIPE_V>();
Muls(betaX, x, beta_, multiCount);
PipeBarrier<PIPE_V>();
Mins(softplusTmp, betaX, threshold_, multiCount);
PipeBarrier<PIPE_V>();
Exp(softplusTmp, softplusTmp, multiCount);
PipeBarrier<PIPE_V>();
Adds(softplusTmp, softplusTmp, 1.0f, multiCount);
PipeBarrier<PIPE_V>();
Ln(softplusTmp, softplusTmp, multiCount);
PipeBarrier<PIPE_V>();
Muls(softplusTmp, softplusTmp, 1.0f / beta_, multiCount);
PipeBarrier<PIPE_V>();
CompareScalar(thresholdMask, betaX, threshold_, CMPMODE::LE, maskCount);
PipeBarrier<PIPE_V>();
Select(gLocal, thresholdMask, softplusTmp, x, SELMODE::VSEL_TENSOR_TENSOR_MODE, multiCount);
PipeBarrier<PIPE_V>();
Mul(gLocal, gLocal, dtBiasTensor_, multiCount);
PipeBarrier<PIPE_V>();
}
// Numerically stable sigmoid: 1 / (1 + exp(-b)).
Muls(betaFp32, betaFp32, -1.0f, multiCount);
PipeBarrier<PIPE_V>();
Exp(betaFp32, betaFp32, multiCount);
PipeBarrier<PIPE_V>();
Duplicate(x, 1.0f, multiCount);
PipeBarrier<PIPE_V>();
Add(betaFp32, betaFp32, x, multiCount);
PipeBarrier<PIPE_V>();
Div(x, x, betaFp32, multiCount);
PipeBarrier<PIPE_V>();
Cast(betaLocal, x, RoundMode::CAST_RINT, multiCount);
PipeBarrier<PIPE_V>();
aInQue_.FreeTensor(aLocal);
bInQue_.FreeTensor(bLocal);
gOutQue_.template EnQue<float>(gLocal);
betaOutQue_.template EnQue<InDtype>(betaLocal);
// MTE3: Write output.
gLocal = gOutQue_.template DeQue<float>();
betaLocal = betaOutQue_.template DeQue<InDtype>();
if (useBulkDma_ && isFullChunk) {
const uint64_t rowOffset = static_cast<uint64_t>(baseRow) * numHeads_;
const uint32_t gSrcGap =
(alignedHeadsFloat_ - numHeads_) * static_cast<uint32_t>(sizeof(float)) / BYTES_PER_BLOCK;
const uint32_t bSrcGap =
(alignedHeadsHalf_ - numHeads_) * static_cast<uint32_t>(sizeof(InDtype)) / BYTES_PER_BLOCK;
DataCopyExtParams gOutParams{static_cast<uint16_t>(rowsPerIter_),
numHeads_ * static_cast<uint32_t>(sizeof(float)),
gSrcGap, 0, 0};
DataCopyExtParams bOutParams{static_cast<uint16_t>(rowsPerIter_),
numHeads_ * static_cast<uint32_t>(sizeof(InDtype)),
bSrcGap, 0, 0};
DataCopyPad(gGm_[rowOffset], gLocal, gOutParams);
DataCopyPad(betaGm_[rowOffset], betaLocal, bOutParams);
} else {
for (uint32_t r = 0; r < validRows; ++r) {
const uint64_t rowOffset = static_cast<uint64_t>(baseRow + r) * numHeads_;
DataCopyParams gOutParams{1, static_cast<uint16_t>(numHeads_ * sizeof(float)), 0, 0};
DataCopyParams bOutParams{1, static_cast<uint16_t>(numHeads_ * sizeof(InDtype)), 0, 0};
DataCopyPad(gGm_[rowOffset], gLocal[r * alignedHeadsFloat_], gOutParams);
DataCopyPad(betaGm_[rowOffset], betaLocal[r * alignedHeadsHalf_], bOutParams);
}
}
gOutQue_.FreeTensor(gLocal);
betaOutQue_.FreeTensor(betaLocal);
}
private:
TPipe *pipe_{nullptr};
GlobalTensor<ParamDtype> aLogGm_;
GlobalTensor<ParamDtype> dtBiasGm_;
GlobalTensor<InDtype> aGm_;
GlobalTensor<InDtype> bGm_;
GlobalTensor<float> gGm_;
GlobalTensor<InDtype> betaGm_;
TQue<QuePosition::VECIN, 1> aInQue_;
TQue<QuePosition::VECIN, 1> bInQue_;
TQue<QuePosition::VECIN, 1> aLogInQue_;
TQue<QuePosition::VECIN, 1> dtBiasInQue_;
TQue<QuePosition::VECIN, 1> negExpInQue_;
TQue<QuePosition::VECIN, 1> dtBiasPreloadQue_;
TQue<QuePosition::VECOUT, 1> gOutQue_;
TQue<QuePosition::VECOUT, 1> betaOutQue_;
TBuf<TPosition::VECCALC> dtBiasMultiBuf_;
TBuf<TPosition::VECCALC> negExpMultiBuf_;
TBuf<TPosition::VECCALC> xBuf_;
TBuf<TPosition::VECCALC> betaXBuf_;
TBuf<TPosition::VECCALC> softplusTmpBuf_;
TBuf<TPosition::VECCALC> thresholdMaskBuf_;
TBuf<TPosition::VECCALC> betaFp32Buf_;
LocalTensor<float> dtBiasTensor_; // neg_exp(A_log), 1 row
LocalTensor<float> dtBiasPreloaded_; // dt_bias, 1 row
uint32_t numHeads_{0};
uint32_t numBatches_{0};
uint32_t rowsPerIter_{1};
bool useBulkDma_{false};
uint32_t alignedHeadsHalf_{0};
uint32_t alignedHeadsFloat_{0};
uint32_t alignedHeadsMask_{0};
uint32_t alignedHeadsParam_{0};
float beta_{1.0f};
float threshold_{20.0f};
};
} // namespace FusedGdnGating
#endif // FUSED_GDN_GATING_KERNEL_H

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright contributors to the vllm-ascend project
*/
/*!
* \file fused_gdn_gating_tiling_data.h
* \brief Tiling data shared between host-side tiling and device-side kernel.
*/
#ifndef FUSED_GDN_GATING_TILING_DATA_H
#define FUSED_GDN_GATING_TILING_DATA_H
#include "kernel_tiling/kernel_tiling.h"
namespace FusedGdnGating {
#pragma pack(push, 8)
struct alignas(8) FusedGdnGatingTilingData {
uint32_t numHeads;
uint32_t numBatches;
uint32_t rowsPerIter;
uint32_t useBulkDma;
float beta;
float threshold;
};
#pragma pack(pop)
} // namespace FusedGdnGating
#endif // FUSED_GDN_GATING_TILING_DATA_H

View File

@@ -0,0 +1,19 @@
# -----------------------------------------------------------------------------------------------------------
# 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(GLOB CURRENT_DIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*)
if(NOT ENABLE_TEST AND NOT BENCHMARK)
list(REMOVE_ITEM CURRENT_DIRS tests)
endif()
foreach(SUB_DIR ${CURRENT_DIRS})
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUB_DIR}/CMakeLists.txt")
add_subdirectory(${SUB_DIR})
endif()
endforeach()

View File

@@ -0,0 +1,62 @@
# ----------------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------------
# add_ops_compile_options(
# OP_NAME SwigluClipQuant
# OPTIONS --cce-auto-sync=off
# -Wno-deprecated-declarations
# -Werror
# -mllvm -cce-aicore-hoist-movemask=false
# --op_relocatable_kernel_binary=true
# )
# set(indexer_compress_epilog_depends transformer/attention/indexer_compress_epilog PARENT_SCOPE)
# target_sources(op_host_aclnn PRIVATE
# op_host/indexer_compress_epilog_def.cpp
# )
# target_sources(optiling PRIVATE
# op_host/indexer_compress_epilog_tiling.cpp
# )
# if (NOT BUILD_OPEN_PROJECT)
# target_sources(opmaster_ct PRIVATE
# op_host/indexer_compress_epilog_tiling.cpp
# )
# endif ()
# target_include_directories(optiling PRIVATE
# ${CMAKE_CURRENT_SOURCE_DIR}/op_host
# )
# target_sources(opsproto PRIVATE
# op_host/indexer_compress_epilog_proto.cpp
# )
add_op_to_compiled_list()
if (BUILD_OPEN_PROJECT)
target_sources(op_host_aclnn PRIVATE
indexer_compress_epilog_def.cpp
)
endif()
add_ops_compile_options(
OP_NAME IndexerCompressEpilog
OPTIONS --cce-auto-sync=off
-Wno-deprecated-declarations
-mllvm -cce-aicore-hoist-movemask=false
--op_relocatable_kernel_binary=true
)
if (NOT BUILD_OPS_RTY_KERNEL)
add_modules_sources(OPTYPE indexer_compress_epilog ACLNNTYPE aclnn)
endif()

View File

@@ -0,0 +1,60 @@
/**
* 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 indexer_compress_epilog_def.cpp
* \brief
*/
#include "register/op_def_registry.h"
namespace ops {
class IndexerCompressEpilog : public OpDef {
public:
explicit IndexerCompressEpilog(const char* name) : OpDef(name)
{
this->Input("indexer_compress_cache")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E5M2})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Input("indexer_compress_cache_scale")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT8_E8M0, ge::DT_FLOAT8_E8M0, ge::DT_FLOAT8_E8M0, ge::DT_FLOAT8_E8M0})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Input("x")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT16, ge::DT_BF16})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Input("slot_mapping")
.ParamType(REQUIRED)
.DataType({ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32, ge::DT_INT32})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Output("indexer_compress_cache")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E4M3FN, ge::DT_FLOAT8_E5M2, ge::DT_FLOAT8_E5M2})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Output("indexer_compress_cache_scale")
.ParamType(REQUIRED)
.DataType({ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT, ge::DT_FLOAT8_E8M0, ge::DT_FLOAT8_E8M0, ge::DT_FLOAT8_E8M0, ge::DT_FLOAT8_E8M0})
.Format({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
.UnknownShapeFormat({ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND});
this->Attr("quant_mode").AttrType(OPTIONAL).Int(1);
this->Attr("round_scale").AttrType(OPTIONAL).Bool(true);
this->AICore().AddConfig("ascend950");
}
};
OP_ADD(IndexerCompressEpilog);
} // namespace ops

View File

@@ -0,0 +1,36 @@
/**
* 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 indexer_compress_epilog_proto.cpp
* \brief
*/
#include <graph/utils/type_utils.h>
#include <register/op_impl_registry.h>
#include "error/ops_error.h"
using namespace ge;
namespace ops {
graphStatus InferShape4IndexerCompressEpilog(gert::InferShapeContext* context)
{
return ge::GRAPH_SUCCESS;
}
graphStatus InferDtype4IndexerCompressEpilog(gert::InferDataTypeContext* context)
{
return GRAPH_SUCCESS;
}
IMPL_OP_INFERSHAPE(IndexerCompressEpilog)
.InferShape(InferShape4IndexerCompressEpilog)
.InferDataType(InferDtype4IndexerCompressEpilog);
} // namespace ops

View File

@@ -0,0 +1,236 @@
/**
* 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 indexer_compress_epilog_tiling.cpp
* \brief
*/
#include <sstream>
#include "indexer_compress_epilog_tiling.h"
using namespace ge;
namespace optiling {
namespace {
constexpr uint64_t WORKSPACE_SIZE = 32;
int64_t CeilDiv(int64_t x, int64_t y)
{
if (y != 0) {
return (x + y - 1) / y;
}
return x;
}
int64_t DownAlign(int64_t x, int64_t y) {
if (y == 0) {
return x;
}
return (x / y) * y;
}
int64_t RoundUp(int64_t x, int64_t y) {
return CeilDiv(x, y) * y;
}
constexpr int64_t INPUT_X_IDX = 2;
constexpr int64_t INPUT_SLOT_MAPPING_IDX = 3;
constexpr int64_t ATTR_QUANT_MODE_INDEX = 0;
constexpr int64_t ATTR_ROUND_SCALE_INDEX = 1;
constexpr int64_t BLOCK_SIZE = 32;
constexpr int64_t REPEAT_SIZE = 256;
constexpr int64_t DOUBLE_BUFFER = 2;
// per_block量化,每128个f16需要量化出一个scale, 因此切分尾轴时以128为factor进行切分
constexpr int64_t PER_BLOCK_FP16 = 128;
constexpr int64_t NORMAL_QUANT_MODE = 1;
constexpr int64_t SINGLE_ROW = 1;
}
ge::graphStatus IndexerCompressEpilogTiling::GetPlatformInfo()
{
auto platformInfo = context_->GetPlatformInfo();
if (platformInfo == nullptr) {
auto compileInfoPtr = context_->GetCompileInfo<IndexerCompressEpilogCompileInfo>();
OPS_ERR_IF(compileInfoPtr == nullptr, OPS_LOG_E(context_, "compile info is null"),
return ge::GRAPH_FAILED);
coreNum_ = compileInfoPtr->coreNum;
ubSize_ = compileInfoPtr->ubSize;
} else {
auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfo);
coreNum_ = ascendcPlatform.GetCoreNumAiv();
uint64_t ubSizePlatForm;
ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSizePlatForm);
ubSize_ = ubSizePlatForm;
socVersion_ = ascendcPlatform.GetSocVersion();
}
return ge::GRAPH_SUCCESS;
}
ge::graphStatus IndexerCompressEpilogTiling::GetAttr()
{
auto* attrs = context_->GetAttrs();
OPS_LOG_E_IF_NULL(context_, attrs, return ge::GRAPH_FAILED);
auto quantMode = attrs->GetAttrPointer<int64_t>(ATTR_QUANT_MODE_INDEX);
quantMode_ = quantMode == nullptr ? 1 : *quantMode;
auto roundScale = attrs->GetAttrPointer<int64_t>(ATTR_ROUND_SCALE_INDEX);
roundScale_ = roundScale == nullptr ? true : *roundScale;
return ge::GRAPH_SUCCESS;
}
ge::graphStatus IndexerCompressEpilogTiling::GetShapeAttrsInfoInner()
{
auto shapeX = context_->GetInputShape(INPUT_X_IDX);
OPS_LOG_E_IF_NULL(context_, shapeX, return ge::GRAPH_FAILED);
auto xStorageShape = shapeX->GetStorageShape();
d_ = xStorageShape.GetDim(xStorageShape.GetDimNum() - 1);
auto shapeSlotMapping = context_->GetInputShape(INPUT_SLOT_MAPPING_IDX);
OPS_LOG_E_IF_NULL(context_, shapeSlotMapping, return ge::GRAPH_FAILED);
auto slotMappingStorageShape = shapeSlotMapping->GetStorageShape();
bs_ = slotMappingStorageShape.GetDim(0);
scaleCol_ = CeilDiv(d_, PER_BLOCK_FP16);
OPS_ERR_IF(GetAttr() != ge::GRAPH_SUCCESS,
OPS_LOG_E(context_->GetNodeName(), "get attr failed."),
return ge::GRAPH_FAILED);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus IndexerCompressEpilogTiling::CalcOpTiling()
{
rowOfFormerBlock_ = CeilDiv(bs_, static_cast<int64_t>(coreNum_));
usedCoreNums_ = std::min(CeilDiv(bs_, rowOfFormerBlock_), static_cast<int64_t>(coreNum_));
rowOfTailBlock_ = bs_ - (usedCoreNums_ - 1) * rowOfFormerBlock_;
int64_t minRowPerCore = 1;
int64_t rowOnceLoop = std::min(rowOfFormerBlock_, minRowPerCore);
rowFactor_ = rowOnceLoop;
int64_t scaleByteSize = 4;
if (quantMode_ == 0) {
scaleByteSize = 1;
}
int64_t perBlockScaleElemNum = BLOCK_SIZE / scaleByteSize;
// d全载,尝试搬入更多的bs
while (rowFactor_ <= rowOfFormerBlock_) {
int64_t xSize = rowFactor_ * RoundUp(d_, 16) * 2 * DOUBLE_BUFFER;
int64_t ySize = rowFactor_ * RoundUp(d_, 32) * 1 * DOUBLE_BUFFER;
int64_t scaleSize = rowFactor_ * RoundUp(scaleCol_, perBlockScaleElemNum) * scaleByteSize * DOUBLE_BUFFER;
int64_t tmpBufferSize = RoundUp(rowFactor_, 8) * 4;
int64_t totalSize = xSize + ySize + scaleSize + tmpBufferSize;
if (totalSize > ubSize_) {
rowFactor_ = rowFactor_ - 1;
break;
}
rowFactor_ = rowFactor_ + 1;
}
if (rowFactor_ > rowOfFormerBlock_) {
rowFactor_--;
}
rowLoopOfFormerBlock_ = CeilDiv(rowOfFormerBlock_, rowFactor_);
rowLoopOfTailBlock_ = CeilDiv(rowOfTailBlock_, rowFactor_);
tailRowFactorOfFormerBlock_ = rowOfFormerBlock_ % rowFactor_ == 0 ? rowFactor_ : rowOfFormerBlock_ % rowFactor_;
tailRowFactorOfTailBlock_ = rowOfTailBlock_ % rowFactor_ == 0 ? rowFactor_ : rowOfTailBlock_ % rowFactor_;
tilingData_.set_bs(bs_);
tilingData_.set_d(d_);
tilingData_.set_scaleCol(scaleCol_);
tilingData_.set_rowOfFormerBlock(rowOfFormerBlock_);
tilingData_.set_rowOfTailBlock(rowOfTailBlock_);
tilingData_.set_rowLoopOfFormerBlock(rowLoopOfFormerBlock_);
tilingData_.set_rowLoopOfTailBlock(rowLoopOfTailBlock_);
tilingData_.set_rowFactor(rowFactor_);
tilingData_.set_tailRowFactorOfFormerBlock(tailRowFactorOfFormerBlock_);
tilingData_.set_tailRowFactorOfTailBlock(tailRowFactorOfTailBlock_);
tilingData_.set_quantMode(quantMode_);
int64_t roundScaleData = roundScale_ ? 1 : 0;
tilingData_.set_roundScale(roundScaleData);
// SINGLE_ROW_NORMAL_QUANT TILING_KEY : 10001
// SINGLE_ROW_MXFP8_QUANT TILING_KEY : 10000
// MULTI_ROW_NORMAL_QUANT TILING_KEY : 10011
// MULTI_ROW_MXFP8_QUANT TILING_KEY : 10010
tilingKey_ = 10000;
if (rowFactor_ != SINGLE_ROW) {
tilingKey_ += 10;
}
if (quantMode_ == NORMAL_QUANT_MODE) {
tilingKey_ += 1;
}
return ge::GRAPH_SUCCESS;
}
ge::graphStatus IndexerCompressEpilogTiling::DoOpTiling()
{
if (GetPlatformInfo() == ge::GRAPH_FAILED) {
return ge::GRAPH_FAILED;
}
if (GetShapeAttrsInfoInner() == ge::GRAPH_FAILED) {
return ge::GRAPH_FAILED;
}
if (CalcOpTiling() == ge::GRAPH_FAILED) {
return ge::GRAPH_FAILED;
}
if (GetWorkspaceSize() == ge::GRAPH_FAILED) {
return ge::GRAPH_FAILED;
}
if (PostTiling() == ge::GRAPH_FAILED) {
return ge::GRAPH_FAILED;
}
context_->SetTilingKey(tilingKey_);
return ge::GRAPH_SUCCESS;
}
ge::graphStatus IndexerCompressEpilogTiling::GetWorkspaceSize()
{
workspaceSize_ = WORKSPACE_SIZE;
return ge::GRAPH_SUCCESS;
}
ge::graphStatus IndexerCompressEpilogTiling::PostTiling()
{
context_->SetBlockDim(usedCoreNums_);
size_t* workspaces = context_->GetWorkspaceSizes(1);
workspaces[0] = workspaceSize_;
tilingData_.SaveToBuffer(context_->GetRawTilingData()->GetData(), context_->GetRawTilingData()->GetCapacity());
context_->GetRawTilingData()->SetDataSize(tilingData_.GetDataSize());
return ge::GRAPH_SUCCESS;
}
ge::graphStatus TilingPrepareForIndexerCompressEpilog(gert::TilingParseContext *context)
{
(void)context;
return ge::GRAPH_SUCCESS;
}
ge::graphStatus TilingForIndexerCompressEpilog(gert::TilingContext *context)
{
OPS_ERR_IF(context == nullptr, OPS_REPORT_VECTOR_INNER_ERR("IndexerCompressEpilog", "Tiling context is null"),
return ge::GRAPH_FAILED);
IndexerCompressEpilogTiling IndexerCompressEpilogTiling(context);
return IndexerCompressEpilogTiling.DoOpTiling();
}
IMPL_OP_OPTILING(IndexerCompressEpilog)
.Tiling(TilingForIndexerCompressEpilog)
.TilingParse<IndexerCompressEpilogCompileInfo>(TilingPrepareForIndexerCompressEpilog);
} // namespace optiling

View File

@@ -0,0 +1,107 @@
/**
* 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 indexer_compress_epilog_tiling.h
* \brief
*/
#ifndef INDEXER_COMPRESS_EPILOG_TILING_H
#define INDEXER_COMPRESS_EPILOG_TILING_H
#include <vector>
#include <iostream>
#include "register/op_impl_registry.h"
#include "platform/platform_infos_def.h"
#include "exe_graph/runtime/tiling_context.h"
#include "tiling/platform/platform_ascendc.h"
#include "register/op_def_registry.h"
#include "register/tilingdata_base.h"
#include "tiling/tiling_api.h"
#include "error/ops_error.h"
#include "platform/platform_info.h"
namespace optiling {
// ----------公共定义----------
struct TilingRequiredParaInfo {
const gert::CompileTimeTensorDesc *desc;
const gert::StorageShape *shape;
};
struct TilingOptionalParaInfo {
const gert::CompileTimeTensorDesc *desc;
const gert::Tensor *tensor;
};
// ----------算子TilingData定义----------
BEGIN_TILING_DATA_DEF(IndexerCompressEpilogTilingData)
TILING_DATA_FIELD_DEF(int64_t, bs);
TILING_DATA_FIELD_DEF(int64_t, d);
TILING_DATA_FIELD_DEF(int64_t, scaleCol); // 一行多少个scale
TILING_DATA_FIELD_DEF(int64_t, rowOfFormerBlock); // 头核共需要处理多少行
TILING_DATA_FIELD_DEF(int64_t, rowOfTailBlock); // 尾核共需要处理多少行
TILING_DATA_FIELD_DEF(int64_t, rowLoopOfFormerBlock); // 头核需要几次ub搬入
TILING_DATA_FIELD_DEF(int64_t, rowLoopOfTailBlock); // 尾核需要几次ub搬入
TILING_DATA_FIELD_DEF(int64_t, rowFactor); // ub一次标准处理行数
TILING_DATA_FIELD_DEF(int64_t, tailRowFactorOfFormerBlock); // 头核最后一次ub处理行数
TILING_DATA_FIELD_DEF(int64_t, tailRowFactorOfTailBlock); // 尾核最后一次ub处理行数
TILING_DATA_FIELD_DEF(int64_t, quantMode);
TILING_DATA_FIELD_DEF(int64_t, roundScale);
END_TILING_DATA_DEF;
REGISTER_TILING_DATA_CLASS(IndexerCompressEpilog, IndexerCompressEpilogTilingData)
// ----------算子CompileInfo定义----------
struct IndexerCompressEpilogCompileInfo {
uint64_t coreNum = 0;
uint64_t ubSize = 0;
};
// ----------算子Tiling入参信息解析及check类----------
class IndexerCompressEpilogTiling {
public:
explicit IndexerCompressEpilogTiling(gert::TilingContext* tilingContext) : context_(tilingContext)
{
}
~IndexerCompressEpilogTiling() = default;
ge::graphStatus GetPlatformInfo();
ge::graphStatus DoOpTiling();
ge::graphStatus GetWorkspaceSize();
ge::graphStatus PostTiling();
ge::graphStatus GetAttr();
ge::graphStatus GetShapeAttrsInfoInner();
ge::graphStatus CalcOpTiling();
private:
gert::TilingContext *context_ = nullptr;
IndexerCompressEpilogTilingData tilingData_;
uint64_t coreNum_ = 0;
uint64_t workspaceSize_ = 0;
uint64_t usedCoreNums_ = 0;
uint64_t ubSize_ = 0;
int64_t bs_ = 0;
int64_t d_ = 0;
int64_t scaleCol_ = 0;
int64_t rowOfFormerBlock_ = 0;
int64_t rowOfTailBlock_ = 0;
int64_t rowLoopOfFormerBlock_ = 0;
int64_t rowLoopOfTailBlock_ = 0;
int64_t rowFactor_ = 0;
int64_t tailRowFactorOfFormerBlock_ = 0;
int64_t tailRowFactorOfTailBlock_= 0;
int64_t quantMode_ = 1;
bool roundScale_ = true;
platform_ascendc::SocVersion socVersion_ = platform_ascendc::SocVersion::ASCEND910B;
int64_t tilingKey_ = 0;
};
} // namespace optiling
#endif // INDEXER_COMPRESS_EPILOG_TILING_H

View File

@@ -0,0 +1,71 @@
/**
* 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 swiglu_clip_quant.cpp
* \brief
*/
#include "indexer_compress_epilog_multi_row.h"
#include "indexer_compress_epilog_single_row.h"
#include "indexer_compress_epilog_multi_row_mx_fp8.h"
#include "indexer_compress_epilog_single_row_mx_fp8.h"
#define SINGLE_ROW_NORMAL_QUANT 10001
#define SINGLE_ROW_MXFP8_QUANT 10000
#define MULTI_ROW_NORMAL_QUANT 10011
#define MULTI_ROW_MXFP8_QUANT 10010
using namespace AscendC;
extern "C" __global__ __aicore__ void indexer_compress_epilog(
GM_ADDR indexer_compress_cache,
GM_ADDR indexer_compress_cache_scale,
GM_ADDR x,
GM_ADDR slot_mapping,
GM_ADDR indexer_compress_cache_out,
GM_ADDR indexer_compress_cache_scale_out,
GM_ADDR workspace,
GM_ADDR tiling)
{
if (workspace == nullptr) {
return;
}
GM_ADDR userWs = GetUserWorkspace(workspace);
if (userWs == nullptr) {
return;
}
GET_TILING_DATA(tilingData, tiling);
TPipe pipe;
int64_t oriOverflowMode = AscendC::GetCtrlSpr<FLOAT_OVERFLOW_MODE_CTRL, FLOAT_OVERFLOW_MODE_CTRL>();
if (TILING_KEY_IS(MULTI_ROW_NORMAL_QUANT)) {
IndexerCompressEpilog::IndexerCompressEpilogMultiRow<DTYPE_X, DTYPE_INDEXER_COMPRESS_CACHE, DTYPE_INDEXER_COMPRESS_CACHE_SCALE> op;
op.Init(x, slot_mapping, indexer_compress_cache, indexer_compress_cache_scale, userWs, &tilingData, &pipe);
op.Process();
return;
} else if (TILING_KEY_IS(SINGLE_ROW_NORMAL_QUANT)) {
IndexerCompressEpilog::IndexerCompressEpilogSingleRow<DTYPE_X, DTYPE_INDEXER_COMPRESS_CACHE, DTYPE_INDEXER_COMPRESS_CACHE_SCALE> op;
op.Init(x, slot_mapping, indexer_compress_cache, indexer_compress_cache_scale, userWs, &tilingData, &pipe);
op.Process();
return;
} else if (TILING_KEY_IS(MULTI_ROW_MXFP8_QUANT)){
IndexerCompressEpilog::IndexerCompressEpilogMultiRowMxFp8<DTYPE_X, DTYPE_INDEXER_COMPRESS_CACHE, DTYPE_INDEXER_COMPRESS_CACHE_SCALE> op;
op.Init(x, slot_mapping, indexer_compress_cache, indexer_compress_cache_scale, userWs, &tilingData, &pipe);
op.Process();
return;
} else if (TILING_KEY_IS(SINGLE_ROW_MXFP8_QUANT)){
IndexerCompressEpilog::IndexerCompressEpilogSingleRowMxFp8<DTYPE_X, DTYPE_INDEXER_COMPRESS_CACHE, DTYPE_INDEXER_COMPRESS_CACHE_SCALE> op;
op.Init(x, slot_mapping, indexer_compress_cache, indexer_compress_cache_scale, userWs, &tilingData, &pipe);
op.Process();
return;
}
AscendC::SetCtrlSpr<FLOAT_OVERFLOW_MODE_CTRL, FLOAT_OVERFLOW_MODE_CTRL>(oriOverflowMode);
}

View File

@@ -0,0 +1,400 @@
/**
* 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 swiglu_block_quant_base.h
* \brief
*/
#ifndef SWIGLU_BLOCK_QUANT_BASE_H
#define SWIGLU_BLOCK_QUANT_BASE_H
#include "kernel_operator.h"
namespace IndexerCompressEpilog {
using namespace AscendC;
using namespace AscendC::MicroAPI;
using AscendC::MicroAPI::MaskReg;
using AscendC::MicroAPI::RegTensor;
using AscendC::MicroAPI::UnalignReg;
constexpr int32_t BLOCK_SIZE = 32;
constexpr int32_t VL_FP32 = 64;
constexpr int32_t PER_BLOCK_FP16 = 128;
constexpr float FP8_E5M2_MAX_VALUE = 57344.0f;
constexpr float FP8_E4M3FN_MAX_VALUE = 448.0f;
constexpr float FP8_E5M2_MIN_VALUE = -57344.0f;
constexpr float FP8_E4M3FN_MIN_VALUE = -448.0f;
constexpr uint32_t FAST_LOG_SHIFT_BITS = 23U;
constexpr uint32_t FAST_LOG_AND_VALUE1 = 0xFF;
constexpr uint32_t FAST_LOG_AND_VALUE2 = (((uint32_t)1 << (uint32_t)23) - (uint32_t)1);
constexpr uint32_t INV_FP8_E5M2_MAX_VALUE = 0x37924925;
constexpr uint32_t INV_FP8_E4M3_MAX_VALUE = 0x3b124925;
#define FLOAT_OVERFLOW_MODE_CTRL 60
#ifndef INFINITY
#define INFINITY (__builtin_inff())
#endif
constexpr float POS_INFINITY = INFINITY;
constexpr float NEG_INFINITY = -INFINITY;
__aicore__ inline int32_t CeilDiv(int32_t a, int b)
{
if (b == 0) {
return a;
}
return (a + b - 1) / b;
}
__aicore__ inline int32_t CeilAlign(int32_t a, int b)
{
return CeilDiv(a, b) * b;
}
template <typename T>
__aicore__ inline int32_t RoundUp(int32_t num)
{
int32_t elemNum = BLOCK_SIZE / sizeof(T);
return CeilAlign(num, elemNum);
}
template <typename T>
__aicore__ inline int32_t RoundUp(int32_t num, int32_t elemNum)
{
return CeilAlign(num, elemNum);
}
constexpr AscendC::MicroAPI::CastTrait castTraitB162B32Even = {
AscendC::MicroAPI::RegLayout::ZERO,
AscendC::MicroAPI::SatMode::UNKNOWN,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::UNKNOWN,
};
constexpr AscendC::MicroAPI::CastTrait castTraitB322B16Even = {
AscendC::MicroAPI::RegLayout::ZERO,
AscendC::MicroAPI::SatMode::NO_SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_RINT,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitF32toFp8Even = {
AscendC::MicroAPI::RegLayout::ZERO,
AscendC::MicroAPI::SatMode::NO_SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_RINT,
};
constexpr static AscendC::MicroAPI::CastTrait castTraitU32toU8Even = {
AscendC::MicroAPI::RegLayout::ZERO,
AscendC::MicroAPI::SatMode::NO_SAT,
AscendC::MicroAPI::MaskMergeMode::ZEROING,
AscendC::RoundMode::CAST_NONE,
};
template <typename T>
__aicore__ inline void LoadInputData(RegTensor<float>& dst, __local_mem__ T* src, MaskReg pregLoop, uint32_t srcOffset)
{
if constexpr (IsSameType<T, float>::value) {
DataCopy(dst, src + srcOffset);
} else if constexpr (IsSameType<T, half>::value || IsSameType<T, bfloat16_t>::value) {
RegTensor<T> tmp;
DataCopy<T, AscendC::MicroAPI::LoadDist::DIST_UNPACK_B16>(tmp, src + srcOffset);
Cast<float, T, castTraitB162B32Even>(dst, tmp, pregLoop);
}
}
template <typename T>
__aicore__ inline void StoreOutputData(
__local_mem__ T* dst, RegTensor<float>& src, MaskReg pregLoop, uint32_t dstOffset)
{
if constexpr (IsSameType<T, float>::value) {
DataCopy(dst + dstOffset, src, pregLoop);
} else if constexpr (IsSameType<T, half>::value || IsSameType<T, bfloat16_t>::value) {
RegTensor<T> tmp;
Cast<T, float, castTraitB322B16Even>(tmp, src, pregLoop);
DataCopy<T, AscendC::MicroAPI::StoreDist::DIST_PACK_B32>(dst + dstOffset, tmp, pregLoop);
} else if constexpr (IsSameType<T, fp8_e4m3fn_t>::value || IsSameType<T, fp8_e5m2_t>::value) {
RegTensor<T> tmp;
Cast<T, float, castTraitF32toFp8Even>(tmp, src, pregLoop);
DataCopy<T, AscendC::MicroAPI::StoreDist::DIST_PACK4_B32>(dst + dstOffset, tmp, pregLoop);
}
}
template <typename T>
__aicore__ inline void StoreMxFp8Scale(
__local_mem__ T* dst, RegTensor<float>& src, MaskReg pregLoop, uint32_t dstOffset)
{
RegTensor<uint32_t> tmp;
RegTensor<uint8_t> tmp1;
ShiftRights(tmp, (RegTensor<uint32_t> &)src, static_cast<int16_t>(FAST_LOG_SHIFT_BITS), pregLoop);
Cast<uint8_t, uint32_t, castTraitU32toU8Even>(tmp1, tmp, pregLoop);
DataCopy<T, AscendC::MicroAPI::StoreDist::DIST_FIRST_ELEMENT_B8>(dst + dstOffset, (RegTensor<T> &)tmp1, pregLoop);
}
template <typename T0, typename T1, typename T2, bool roundScale = true>
__aicore__ inline void VFProcessDynamicMxFp8Quant(
const LocalTensor<T0>& yLocal, const LocalTensor<T1>& scaleLocal, const LocalTensor<T2>& xLocal,
float coeff, float fp8Min, float fp8Max, const uint16_t curRowNum, const uint32_t curColNum)
{
__local_mem__ T0* yLocalAddr = (__local_mem__ T0*)yLocal.GetPhyAddr();
__local_mem__ T1* scaleLocalAddr = (__local_mem__ T1*)scaleLocal.GetPhyAddr();
__local_mem__ T2* xLocalAddr = (__local_mem__ T2*)xLocal.GetPhyAddr();
uint16_t loopCount = CeilDiv(curColNum, VL_FP32);
uint32_t curColNumAlign = RoundUp<T2>(curColNum);
uint32_t dstCurColNumAlign = RoundUp<T0>(curColNum);
uint16_t loopCountFoldTwo = loopCount / 2;
uint16_t loopCountReminder = loopCount % 2;
uint32_t tailReminder = curColNum - (loopCount - 1) * VL_FP32;
uint32_t scaleColNumAlign = RoundUp<T1>((curColNum + 128 - 1) / 128);
uint32_t sregNum = loopCountReminder == 0 ? curColNum - loopCountFoldTwo * VL_FP32 : loopCountFoldTwo * VL_FP32;
__VEC_SCOPE__
{
RegTensor<float> x0;
RegTensor<float> x0Abs;
RegTensor<float> x1;
RegTensor<float> x1Abs;
RegTensor<float> max0;
RegTensor<float> max1;
RegTensor<float> max2;
RegTensor<uint32_t> tmp0;
RegTensor<uint32_t> tmp1;
RegTensor<uint32_t> vreg0;
RegTensor<uint32_t> vreg1;
RegTensor<uint32_t> vreg2;
RegTensor<uint32_t> vreg3;
RegTensor<uint32_t> vreg4;
RegTensor<int32_t> vreg5;
RegTensor<uint32_t> zero;
RegTensor<uint32_t> one;
RegTensor<uint32_t> tmp3;
RegTensor<float> dupScale;
MaskReg pregLoop;
MaskReg preg1 = CreateMask<T1, AscendC::MicroAPI::MaskPattern::VL1>();
MaskReg pregMerge = CreateMask<float, AscendC::MicroAPI::MaskPattern::VL1>();
MaskReg pregMain = CreateMask<float>();
MaskReg cmpMask;
Duplicate(tmp0, FAST_LOG_AND_VALUE1, pregMerge);
Duplicate(tmp1, FAST_LOG_AND_VALUE2, pregMerge);
Duplicate(zero, static_cast<uint32_t>(0), pregMerge);
Duplicate(one, static_cast<uint32_t>(1), pregMerge);
Duplicate(tmp3, static_cast<uint32_t>(127), pregMerge);
for (uint16_t i = 0; i < curRowNum; i++) {
uint32_t sreg = sregNum;
for (uint16_t j = 0; j < loopCountFoldTwo; j++) {
pregLoop = UpdateMask<float>(sreg);
LoadInputData<T2>(x0, xLocalAddr, pregMain, 2 * j * VL_FP32 + i * curColNumAlign);
LoadInputData<T2>(x1, xLocalAddr, pregLoop, (2 * j + 1) * VL_FP32 + i * curColNumAlign);
Abs(x0Abs, x0, pregMain);
Abs(x1Abs, x1, pregLoop);
ReduceMax(max0, x0Abs, pregMain);
ReduceMax(max1, x1Abs, pregLoop);
Max(max2, max0, max1, pregMerge);
Maxs(max2, max2, static_cast<float>(1e-4), pregMerge);
Muls(max2, max2, coeff, pregMerge);
if constexpr (roundScale) {
ShiftRights(vreg0, (RegTensor<uint32_t> &)max2, static_cast<int16_t>(FAST_LOG_SHIFT_BITS), pregMerge);
And(vreg1, vreg0, tmp0, pregMerge);
And(vreg2, vreg1, tmp1, pregMerge);
Compare<uint32_t, AscendC::CMPMODE::NE>(cmpMask, vreg2, zero, pregMerge);
Select(vreg4, one, zero, cmpMask);
Sub(vreg1, vreg1, tmp3, pregMerge);
Add(vreg1, vreg1, vreg4, pregMerge);
Adds(vreg5, (RegTensor<int32_t> &)vreg1, static_cast<int32_t>(127), pregMerge);
ShiftLefts((RegTensor<int32_t> &)max2, vreg5, static_cast<int16_t>(23), pregMerge);
}
Duplicate(dupScale, max2, pregMain);
Div(x0, x0, dupScale, pregMain);
Div(x1, x1, dupScale, pregLoop);
Maxs(x0, x0, fp8Min, pregMain);
Mins(x0, x0, fp8Max, pregMain);
Maxs(x1, x1, fp8Min, pregLoop);
Mins(x1, x1, fp8Max, pregLoop);
StoreOutputData<T0>(yLocalAddr, x0, pregMain, 2 * j * VL_FP32 + i * dstCurColNumAlign);
StoreOutputData<T0>(yLocalAddr, x1, pregLoop, (2 * j + 1) * VL_FP32 + i * dstCurColNumAlign);
StoreMxFp8Scale<T1>(scaleLocalAddr, max2, preg1, j + i * scaleColNumAlign);
}
// 处理尾块, 这里只有一个for循环
pregLoop = UpdateMask<float>(tailReminder);
for (uint16_t j = 0; j < loopCountReminder; j++) {
LoadInputData<T2>(x0, xLocalAddr, pregLoop, 2 * loopCountFoldTwo * VL_FP32 + i * curColNumAlign);
Abs(x0Abs, x0, pregLoop);
ReduceMax(max0, x0Abs, pregLoop);
Maxs(max2, max0, static_cast<float>(1e-4), pregMerge);
Muls(max2, max2, coeff, pregMerge);
if constexpr (roundScale) {
ShiftRights(vreg0, (RegTensor<uint32_t> &)max2, static_cast<int16_t>(FAST_LOG_SHIFT_BITS), pregMerge);
And(vreg1, vreg0, tmp0, pregMerge);
And(vreg2, vreg1, tmp1, pregMerge);
Compare<uint32_t, AscendC::CMPMODE::NE>(cmpMask, vreg2, zero, pregMerge);
Select(vreg4, one, zero, cmpMask);
Sub(vreg1, vreg1, tmp3, pregMerge);
Add(vreg1, vreg1, vreg4, pregMerge);
Adds(vreg5, (RegTensor<int32_t> &)vreg1, static_cast<int32_t>(127), pregMerge);
ShiftLefts((RegTensor<int32_t> &)max2, vreg5, static_cast<int16_t>(23), pregMerge);
}
Duplicate(dupScale, max2, pregLoop);
Div(x0, x0, dupScale, pregLoop);
Maxs(x0, x0, fp8Min, pregLoop);
Mins(x0, x0, fp8Max, pregLoop);
StoreOutputData<T0>(yLocalAddr, x0, pregLoop, 2 * loopCountFoldTwo * VL_FP32 + i * dstCurColNumAlign);
StoreMxFp8Scale<T1>(scaleLocalAddr, max2, preg1, loopCountFoldTwo + i * scaleColNumAlign);
}
}
}
}
template <typename T0, typename T1>
__aicore__ inline void VFProcessDynamicBlockQuant(
const LocalTensor<T0>& yLocal, const LocalTensor<float>& scaleLocal, const LocalTensor<T1>& xLocal,
float coeff, const uint16_t curRowNum, const uint32_t curColNum)
{
__local_mem__ T0* yLocalAddr = (__local_mem__ T0*)yLocal.GetPhyAddr();
__local_mem__ float* scaleLocalAddr = (__local_mem__ float*)scaleLocal.GetPhyAddr();
__local_mem__ T1* xLocalAddr = (__local_mem__ T1*)xLocal.GetPhyAddr();
uint16_t loopCount = CeilDiv(curColNum, VL_FP32);
uint32_t curColNumAlign = RoundUp<T1>(curColNum);
uint32_t dstCurColNumAlign = RoundUp<T0>(curColNum);
uint16_t loopCountFoldTwo = loopCount / 2;
uint16_t loopCountReminder = loopCount % 2;
uint32_t tailReminder = curColNum - (loopCount - 1) * VL_FP32;
uint32_t scaleColNumAlign = RoundUp<float>((curColNum + 128 - 1) / 128);
uint32_t sregNum = loopCountReminder == 0 ? curColNum - loopCountFoldTwo * VL_FP32 : loopCountFoldTwo * VL_FP32;
static constexpr AscendC::MicroAPI::DivSpecificMode mode = {AscendC::MicroAPI::MaskMergeMode::ZEROING, false};
uint32_t maxValueInt = 0;
if constexpr (IsSameType<T0, fp8_e5m2_t>::value) {
maxValueInt = INV_FP8_E5M2_MAX_VALUE;
} else if constexpr (IsSameType<T0, fp8_e4m3fn_t>::value) {
maxValueInt = INV_FP8_E4M3_MAX_VALUE;
}
__VEC_SCOPE__
{
RegTensor<float> xLeft;
RegTensor<float> xRight;
RegTensor<float> x0Left;
RegTensor<float> x0Right;
RegTensor<float> x1Left;
RegTensor<float> x1Right;
RegTensor<float> xAbsLeft;
RegTensor<float> xAbsRight;
RegTensor<float> xMax;
RegTensor<float> tmp;
RegTensor<float> dupScale;
RegTensor<float> scale;
RegTensor<float> scale0;
RegTensor<float> scale1;
RegTensor<float> inf;
RegTensor<float> one;
RegTensor<float> zero;
RegTensor<uint32_t> coeffReg;
MaskReg pregLoop = CreateMask<float>();
Duplicate(one, static_cast<float>(1.0f), pregLoop);
Duplicate(coeffReg, maxValueInt, pregLoop);
Duplicate(zero, 0.0f);
Duplicate(inf, 1.0f);
Div<float, &mode>(inf, inf, zero, pregLoop);
MaskReg pregMain = CreateMask<float>();
MaskReg preg1 = CreateMask<float, AscendC::MicroAPI::MaskPattern::VL1>();
MaskReg compareLeft;
MaskReg compareRight;
MaskReg compareScalar;
for (uint16_t i = 0; i < curRowNum; i++) {
uint32_t sreg = sregNum;
for (uint16_t j = 0; j < loopCountFoldTwo; j++) {
pregLoop = UpdateMask<float>(sreg);
LoadInputData<T1>(xLeft, xLocalAddr, pregMain, 2 * j * VL_FP32 + i * curColNumAlign);
LoadInputData<T1>(xRight, xLocalAddr, pregLoop, (2 * j + 1) * VL_FP32 + i * curColNumAlign);
Muls(xAbsLeft, xLeft, 0.0f, pregMain);
Compare<float, CMPMODE::NE>(compareLeft, xAbsLeft, xAbsLeft, pregMain);
MaskNot(compareLeft, compareLeft, pregMain);
Abs(xAbsLeft, xLeft, compareLeft);
ReduceMax(scale0, xAbsLeft, pregMain);
Muls(xAbsRight, xRight, 0.0f, pregLoop);
Compare<float, CMPMODE::NE>(compareRight, xAbsRight, xAbsRight, pregLoop);
MaskNot(compareRight, compareRight, pregLoop);
Abs(xAbsRight, xRight, compareRight);
ReduceMax(scale1, xAbsRight, pregLoop);
Max(scale, scale0, scale1, preg1);
CompareScalar<float, CMPMODE::NE>(compareScalar, scale, (float)0.0, preg1);
Mul(scale, scale, (RegTensor<float>&)coeffReg, compareScalar);
Min(scale, scale, inf, preg1);
Duplicate(dupScale, scale, pregMain);
DataCopy<float, AscendC::MicroAPI::StoreDist::DIST_FIRST_ELEMENT_B32>(scaleLocalAddr + j + i * scaleColNumAlign, scale, preg1);
Div<float, &mode>(x0Left, xLeft, dupScale, pregMain);
Muls(x1Left, x0Left, 0.0f, pregMain);
Compare<float, CMPMODE::NE>(compareLeft, x1Left, x1Left, pregMain);
Select(xLeft, xLeft, x0Left, compareLeft);
Div<float, &mode>(x0Right, xRight, dupScale, pregLoop);
Muls(x1Right, x0Right, 0.0f, pregLoop);
Compare<float, CMPMODE::NE>(compareRight, x1Right, x1Right, pregLoop);
Select(xRight, xRight, x0Right, compareRight);
StoreOutputData<T0>(yLocalAddr, xLeft, pregMain, 2 * j * VL_FP32 + i * dstCurColNumAlign);
StoreOutputData<T0>(yLocalAddr, xRight, pregLoop, (2 * j + 1) * VL_FP32 + i * dstCurColNumAlign);
}
// 处理尾块, 这里只有一个for循环
pregLoop = UpdateMask<float>(tailReminder);
for (uint16_t j = 0; j < loopCountReminder; j++) {
LoadInputData<T1>(xLeft, xLocalAddr, pregLoop, loopCountFoldTwo * 2 * VL_FP32 + i * curColNumAlign);
Abs(xAbsLeft, xLeft, pregLoop);
ReduceMax(scale, xAbsLeft, pregLoop);
CompareScalar<float, CMPMODE::NE>(compareScalar, scale, (float)0.0, preg1);
Mul(scale, scale, (RegTensor<float>&)coeffReg, compareScalar);
Min(scale, scale, inf, preg1);
Duplicate(dupScale, scale, pregLoop);
DataCopy<float, AscendC::MicroAPI::StoreDist::DIST_FIRST_ELEMENT_B32>(scaleLocalAddr + loopCountFoldTwo + i * scaleColNumAlign, scale, preg1);
Div<float, &mode>(x0Left, xLeft, dupScale, pregLoop);
Muls(x1Left, x0Left, 0.0f, pregLoop);
Compare<float, CMPMODE::NE>(compareLeft, x1Left, x1Left, pregLoop);
Select(xLeft, xLeft, x0Left, compareLeft);
StoreOutputData(yLocalAddr, xLeft, pregLoop, loopCountFoldTwo * 2 * VL_FP32 + i * dstCurColNumAlign);
}
}
}
}
template <typename T>
__aicore__ inline void CopyIn(
const GlobalTensor<T>& inputGm, const LocalTensor<T>& inputTensor, const uint16_t nBurst, const uint32_t copyLen,
uint32_t srcStride = 0)
{
DataCopyPadExtParams<T> dataCopyPadExtParams;
dataCopyPadExtParams.isPad = false;
dataCopyPadExtParams.leftPadding = 0;
dataCopyPadExtParams.rightPadding = 0;
dataCopyPadExtParams.paddingValue = 0;
DataCopyExtParams dataCoptExtParams;
dataCoptExtParams.blockCount = nBurst;
dataCoptExtParams.blockLen = copyLen * sizeof(T);
dataCoptExtParams.srcStride = srcStride * sizeof(T);
dataCoptExtParams.dstStride = 0;
DataCopyPad(inputTensor, inputGm, dataCoptExtParams, dataCopyPadExtParams);
}
template <typename T, AscendC::PaddingMode mode = AscendC::PaddingMode::Normal>
__aicore__ inline void CopyOut(
const LocalTensor<T>& outputTensor, const GlobalTensor<T>& outputGm, const uint16_t nBurst, const uint32_t copyLen,
uint32_t dstStride = 0)
{
DataCopyExtParams dataCopyParams;
dataCopyParams.blockCount = nBurst;
dataCopyParams.blockLen = copyLen * sizeof(T);
dataCopyParams.srcStride = 0;
dataCopyParams.dstStride = dstStride * sizeof(T);
DataCopyPad<T, mode>(outputGm, outputTensor, dataCopyParams);
}
} // namespace SwigluBlockQuant
#endif

View File

@@ -0,0 +1,149 @@
/**
* 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 indexer_compress_epilog_multi_row.h
* \brief
*/
#ifndef INDEXER_COMPRESS_EPILOG_MULTI_ROW_H
#define INDEXER_COMPRESS_EPILOG_MULTI_ROW_H
#include "kernel_operator.h"
#include "indexer_compress_epilog_base.h"
namespace IndexerCompressEpilog {
using namespace AscendC;
template <typename T0, typename T1, typename T2>
class IndexerCompressEpilogMultiRow {
public:
__aicore__ inline IndexerCompressEpilogMultiRow()
{}
__aicore__ inline void Init(
GM_ADDR x, GM_ADDR slotMapping, GM_ADDR indexerCompressCache, GM_ADDR indexerCompressCacheScale,
GM_ADDR workspace, const IndexerCompressEpilogTilingData* tilingDataPtr, TPipe* pipePtr)
{
pipe = pipePtr;
tilingData = tilingDataPtr;
xGm.SetGlobalBuffer((__gm__ T0*)x);
slotMappingGm.SetGlobalBuffer((__gm__ int32_t*)slotMapping);
indexerCompressCacheGm.SetGlobalBuffer((__gm__ T1*)indexerCompressCache);
indexerCompressCacheScaleGm.SetGlobalBuffer((__gm__ float*)indexerCompressCacheScale);
pipe->InitBuffer(xQue, 2, tilingData->rowFactor * RoundUp<T0>(tilingData->d) * sizeof(T0));
pipe->InitBuffer(indexerCompressCacheQue, 2, tilingData->rowFactor * RoundUp<T1>(tilingData->d) * sizeof(T1));
pipe->InitBuffer(
indexerCompressCacheScaleQue, 2,
tilingData->rowFactor * RoundUp<float>(tilingData->scaleCol) * sizeof(float));
pipe->InitBuffer(indexBuf, RoundUp<int32_t>(tilingData->rowFactor) * sizeof(int32_t));
indexLocal = indexBuf.Get<int32_t>();
AscendC::SetCtrlSpr<FLOAT_OVERFLOW_MODE_CTRL, FLOAT_OVERFLOW_MODE_CTRL>(0);
}
__aicore__ inline void Process()
{
SetMaxValue();
int64_t curBlockIdx = GetBlockIdx();
int64_t rowOuterLoop =
(curBlockIdx == GetBlockNum() - 1) ? tilingData->rowLoopOfTailBlock : tilingData->rowLoopOfFormerBlock;
int64_t tailRowFactor = (curBlockIdx == GetBlockNum() - 1) ? tilingData->tailRowFactorOfTailBlock :
tilingData->tailRowFactorOfFormerBlock;
int64_t xGmBaseOffset = curBlockIdx * tilingData->rowOfFormerBlock * tilingData->d;
for (int64_t rowOuterIdx = 0; rowOuterIdx < rowOuterLoop; rowOuterIdx++) {
int64_t curRowFactor = (rowOuterIdx == rowOuterLoop - 1) ? tailRowFactor : tilingData->rowFactor;
xLocal = xQue.template AllocTensor<T0>();
validIdx = 0;
for (int64_t rowInnerIdx = 0; rowInnerIdx < curRowFactor; rowInnerIdx++) {
int64_t curSlotIdx = curBlockIdx * tilingData->rowOfFormerBlock + rowOuterIdx * tilingData->rowFactor + rowInnerIdx;
int64_t slot = slotMappingGm.GetValue(curSlotIdx);
if (slot == -1) {
continue;
}
CopyIn(
xGm[xGmBaseOffset + rowOuterIdx * tilingData->rowFactor * tilingData->d +
rowInnerIdx * tilingData->d],
xLocal[validIdx * RoundUp<T0>(tilingData->d)], 1, tilingData->d);
indexLocal.SetValue(validIdx, slot);
validIdx++;
event_t eventId = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::S_MTE3));
SetFlag<HardEvent::S_MTE3>(eventId);
WaitFlag<HardEvent::S_MTE3>(eventId);
}
xQue.template EnQue(xLocal);
xLocal = xQue.template DeQue<T0>();
indexerCompressCacheLocal = indexerCompressCacheQue.template AllocTensor<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.AllocTensor<float>();
VFProcessDynamicBlockQuant(
indexerCompressCacheLocal, indexerCompressCacheScaleLocal, xLocal, maxValue, validIdx, tilingData->d);
xQue.template FreeTensor(xLocal);
indexerCompressCacheQue.template EnQue(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template EnQue(indexerCompressCacheScaleLocal);
indexerCompressCacheLocal = indexerCompressCacheQue.template DeQue<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.template DeQue<float>();
for (int64_t curValidIdx = 0; curValidIdx < validIdx; curValidIdx++) {
int64_t curSlotIdx = indexLocal.GetValue(curValidIdx);
CopyOut(
indexerCompressCacheLocal[curValidIdx * RoundUp<T1>(tilingData->d)],
indexerCompressCacheGm[curSlotIdx * tilingData->d], 1, tilingData->d);
CopyOut(
indexerCompressCacheScaleLocal[curValidIdx * RoundUp<float>(tilingData->scaleCol)],
indexerCompressCacheScaleGm[curSlotIdx * CeilDiv(tilingData->d, PER_BLOCK_FP16)], 1,
tilingData->scaleCol);
}
indexerCompressCacheQue.template FreeTensor(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template FreeTensor(indexerCompressCacheScaleLocal);
}
}
__aicore__ inline void SetMaxValue()
{
if constexpr (IsSameType<T1, fp8_e5m2_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E5M2_MAX_VALUE;
fp8Max = FP8_E5M2_MAX_VALUE;
fp8Min = FP8_E5M2_MIN_VALUE;
} else if constexpr (IsSameType<T1, fp8_e4m3fn_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E4M3FN_MAX_VALUE;
fp8Max = FP8_E4M3FN_MAX_VALUE;
fp8Min = FP8_E4M3FN_MIN_VALUE;
}
}
private:
TPipe* pipe;
const IndexerCompressEpilogTilingData* tilingData;
GlobalTensor<T0> xGm;
GlobalTensor<int32_t> slotMappingGm;
GlobalTensor<T1> indexerCompressCacheGm;
GlobalTensor<float> indexerCompressCacheScaleGm;
TQue<QuePosition::VECIN, 1> xQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheScaleQue;
TBuf<QuePosition::VECCALC> indexBuf;
LocalTensor<T0> xLocal;
LocalTensor<T1> indexerCompressCacheLocal;
LocalTensor<float> indexerCompressCacheScaleLocal;
LocalTensor<int32_t> indexLocal;
int64_t validIdx = 0;
float maxValue = 0.0f;
float fp8Min = 0.0f;
float fp8Max = 0.0f;
};
} // namespace IndexerCompressEpilog
#endif

View File

@@ -0,0 +1,156 @@
/**
* 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 indexer_compress_epilog_multi_row.h
* \brief
*/
#ifndef INDEXER_COMPRESS_EPILOG_MULTI_ROW_MX_FP8_H
#define INDEXER_COMPRESS_EPILOG_MULTI_ROW_MX_FP8_H
#include "kernel_operator.h"
#include "indexer_compress_epilog_base.h"
namespace IndexerCompressEpilog {
using namespace AscendC;
template <typename T0, typename T1, typename T2>
class IndexerCompressEpilogMultiRowMxFp8 {
public:
__aicore__ inline IndexerCompressEpilogMultiRowMxFp8()
{}
__aicore__ inline void Init(
GM_ADDR x, GM_ADDR slotMapping, GM_ADDR indexerCompressCache, GM_ADDR indexerCompressCacheScale,
GM_ADDR workspace, const IndexerCompressEpilogTilingData* tilingDataPtr, TPipe* pipePtr)
{
pipe = pipePtr;
tilingData = tilingDataPtr;
xGm.SetGlobalBuffer((__gm__ T0*)x);
slotMappingGm.SetGlobalBuffer((__gm__ int32_t*)slotMapping);
indexerCompressCacheGm.SetGlobalBuffer((__gm__ T1*)indexerCompressCache);
indexerCompressCacheScaleGm.SetGlobalBuffer((__gm__ T2*)indexerCompressCacheScale);
roundScale = (tilingData->roundScale == 1);
pipe->InitBuffer(xQue, 2, tilingData->rowFactor * RoundUp<T0>(tilingData->d) * sizeof(T0));
pipe->InitBuffer(indexerCompressCacheQue, 2, tilingData->rowFactor * RoundUp<T1>(tilingData->d) * sizeof(T1));
pipe->InitBuffer(
indexerCompressCacheScaleQue, 2,
tilingData->rowFactor * RoundUp<T2>(tilingData->scaleCol) * sizeof(T2));
pipe->InitBuffer(indexBuf, RoundUp<int32_t>(tilingData->rowFactor) * sizeof(int32_t));
indexLocal = indexBuf.Get<int32_t>();
AscendC::SetCtrlSpr<FLOAT_OVERFLOW_MODE_CTRL, FLOAT_OVERFLOW_MODE_CTRL>(0);
}
__aicore__ inline void Process()
{
SetMaxValue();
int64_t curBlockIdx = GetBlockIdx();
int64_t rowOuterLoop =
(curBlockIdx == GetBlockNum() - 1) ? tilingData->rowLoopOfTailBlock : tilingData->rowLoopOfFormerBlock;
int64_t tailRowFactor = (curBlockIdx == GetBlockNum() - 1) ? tilingData->tailRowFactorOfTailBlock :
tilingData->tailRowFactorOfFormerBlock;
int64_t xGmBaseOffset = curBlockIdx * tilingData->rowOfFormerBlock * tilingData->d;
for (int64_t rowOuterIdx = 0; rowOuterIdx < rowOuterLoop; rowOuterIdx++) {
int64_t curRowFactor = (rowOuterIdx == rowOuterLoop - 1) ? tailRowFactor : tilingData->rowFactor;
xLocal = xQue.template AllocTensor<T0>();
validIdx = 0;
for (int64_t rowInnerIdx = 0; rowInnerIdx < curRowFactor; rowInnerIdx++) {
int64_t curSlotIdx = curBlockIdx * tilingData->rowOfFormerBlock + rowOuterIdx * tilingData->rowFactor + rowInnerIdx;
int64_t slot = slotMappingGm.GetValue(curSlotIdx);
if (slot == -1) {
continue;
}
CopyIn(
xGm[xGmBaseOffset + rowOuterIdx * tilingData->rowFactor * tilingData->d +
rowInnerIdx * tilingData->d],
xLocal[validIdx * RoundUp<T0>(tilingData->d)], 1, tilingData->d);
indexLocal.SetValue(validIdx, slot);
validIdx++;
event_t eventId = static_cast<event_t>(GetTPipePtr()->FetchEventID(HardEvent::S_MTE3));
SetFlag<HardEvent::S_MTE3>(eventId);
WaitFlag<HardEvent::S_MTE3>(eventId);
}
xQue.template EnQue(xLocal);
xLocal = xQue.template DeQue<T0>();
indexerCompressCacheLocal = indexerCompressCacheQue.template AllocTensor<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.AllocTensor<T2>();
if (roundScale) {
VFProcessDynamicMxFp8Quant<T1, T2, T0, true>(
indexerCompressCacheLocal, indexerCompressCacheScaleLocal, xLocal, maxValue, fp8Min, fp8Max, validIdx, tilingData->d);
} else {
VFProcessDynamicMxFp8Quant<T1, T2, T0, false>(
indexerCompressCacheLocal, indexerCompressCacheScaleLocal, xLocal, maxValue, fp8Min, fp8Max, validIdx, tilingData->d);
}
xQue.template FreeTensor(xLocal);
indexerCompressCacheQue.template EnQue(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template EnQue(indexerCompressCacheScaleLocal);
indexerCompressCacheLocal = indexerCompressCacheQue.template DeQue<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.template DeQue<T2>();
for (int64_t curValidIdx = 0; curValidIdx < validIdx; curValidIdx++) {
int64_t curSlotIdx = indexLocal.GetValue(curValidIdx);
CopyOut(
indexerCompressCacheLocal[curValidIdx * RoundUp<T1>(tilingData->d)],
indexerCompressCacheGm[curSlotIdx * tilingData->d], 1, tilingData->d);
CopyOut(
indexerCompressCacheScaleLocal[curValidIdx * RoundUp<T2>(tilingData->scaleCol)],
indexerCompressCacheScaleGm[curSlotIdx * CeilDiv(tilingData->d, PER_BLOCK_FP16)], 1,
tilingData->scaleCol);
}
indexerCompressCacheQue.template FreeTensor(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template FreeTensor(indexerCompressCacheScaleLocal);
}
}
__aicore__ inline void SetMaxValue()
{
if constexpr (IsSameType<T1, fp8_e5m2_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E5M2_MAX_VALUE;
fp8Max = FP8_E5M2_MAX_VALUE;
fp8Min = FP8_E5M2_MIN_VALUE;
} else if constexpr (IsSameType<T1, fp8_e4m3fn_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E4M3FN_MAX_VALUE;
fp8Max = FP8_E4M3FN_MAX_VALUE;
fp8Min = FP8_E4M3FN_MIN_VALUE;
}
}
private:
TPipe* pipe;
const IndexerCompressEpilogTilingData* tilingData;
GlobalTensor<T0> xGm;
GlobalTensor<int32_t> slotMappingGm;
GlobalTensor<T1> indexerCompressCacheGm;
GlobalTensor<T2> indexerCompressCacheScaleGm;
TQue<QuePosition::VECIN, 1> xQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheScaleQue;
TBuf<QuePosition::VECCALC> indexBuf;
LocalTensor<T0> xLocal;
LocalTensor<T1> indexerCompressCacheLocal;
LocalTensor<T2> indexerCompressCacheScaleLocal;
LocalTensor<int32_t> indexLocal;
int64_t validIdx = 0;
float maxValue = 0.0f;
float fp8Min = 0.0f;
float fp8Max = 0.0f;
bool roundScale = true;
};
} // namespace IndexerCompressEpilog
#endif

View File

@@ -0,0 +1,126 @@
/**
* 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 indexer_compress_epilog_d_full_load.h
* \brief
*/
#ifndef INDEXER_COMPRESS_EPILOG_SINGLE_ROW_H
#define INDEXER_COMPRESS_EPILOG_SINGLE_ROW_H
#include "kernel_operator.h"
#include "indexer_compress_epilog_base.h"
namespace IndexerCompressEpilog {
using namespace AscendC;
template <typename T0, typename T1, typename T2>
class IndexerCompressEpilogSingleRow {
public:
__aicore__ inline IndexerCompressEpilogSingleRow()
{}
__aicore__ inline void Init(
GM_ADDR x, GM_ADDR slotMapping, GM_ADDR indexerCompressCache, GM_ADDR indexerCompressCacheScale,
GM_ADDR workspace, const IndexerCompressEpilogTilingData* tilingDataPtr, TPipe* pipePtr)
{
pipe = pipePtr;
tilingData = tilingDataPtr;
xGm.SetGlobalBuffer((__gm__ T0*)x);
slotMappingGm.SetGlobalBuffer((__gm__ int32_t*)slotMapping);
indexerCompressCacheGm.SetGlobalBuffer((__gm__ T1*)indexerCompressCache);
indexerCompressCacheScaleGm.SetGlobalBuffer((__gm__ float*)indexerCompressCacheScale);
pipe->InitBuffer(xQue, 2, tilingData->rowFactor * RoundUp<T0>(tilingData->d) * sizeof(T0));
pipe->InitBuffer(indexerCompressCacheQue, 2, tilingData->rowFactor * RoundUp<T1>(tilingData->d) * sizeof(T1));
pipe->InitBuffer(
indexerCompressCacheScaleQue, 2,
tilingData->rowFactor * RoundUp<float>(tilingData->scaleCol) * sizeof(float));
pipe->InitBuffer(indexBuf, RoundUp<int32_t>(tilingData->rowFactor) * sizeof(int32_t));
AscendC::SetCtrlSpr<FLOAT_OVERFLOW_MODE_CTRL, FLOAT_OVERFLOW_MODE_CTRL>(0);
}
__aicore__ inline void Process()
{
SetMaxValue();
int64_t curBlockIdx = GetBlockIdx();
int64_t rowOuterLoop =
(curBlockIdx == GetBlockNum() - 1) ? tilingData->rowLoopOfTailBlock : tilingData->rowLoopOfFormerBlock;
int64_t xGmBaseOffset = curBlockIdx * tilingData->rowOfFormerBlock * tilingData->d;
for (int64_t rowOuterIdx = 0; rowOuterIdx < rowOuterLoop; rowOuterIdx++) {
xLocal = xQue.template AllocTensor<T0>();
int64_t curSlotIdx = curBlockIdx * tilingData->rowOfFormerBlock + rowOuterIdx * tilingData->rowFactor;
int64_t slot = slotMappingGm.GetValue(curSlotIdx);
if (slot == -1) {
continue;
}
CopyIn(xGm[xGmBaseOffset + rowOuterIdx * tilingData->rowFactor * tilingData->d],
xLocal, 1, tilingData->d);
xQue.template EnQue(xLocal);
xLocal = xQue.template DeQue<T0>();
indexerCompressCacheLocal = indexerCompressCacheQue.template AllocTensor<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.AllocTensor<float>();
VFProcessDynamicBlockQuant(
indexerCompressCacheLocal, indexerCompressCacheScaleLocal, xLocal, maxValue, 1, tilingData->d);
xQue.template FreeTensor(xLocal);
indexerCompressCacheQue.template EnQue(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template EnQue(indexerCompressCacheScaleLocal);
indexerCompressCacheLocal = indexerCompressCacheQue.template DeQue<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.template DeQue<float>();
CopyOut(
indexerCompressCacheLocal, indexerCompressCacheGm[slot * tilingData->d], 1, tilingData->d);
CopyOut(
indexerCompressCacheScaleLocal, indexerCompressCacheScaleGm[slot * CeilDiv(tilingData->d, PER_BLOCK_FP16)], 1,
tilingData->scaleCol);
indexerCompressCacheQue.template FreeTensor(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template FreeTensor(indexerCompressCacheScaleLocal);
}
}
__aicore__ inline void SetMaxValue()
{
if constexpr (IsSameType<T1, fp8_e5m2_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E5M2_MAX_VALUE;
} else if constexpr (IsSameType<T1, fp8_e4m3fn_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E4M3FN_MAX_VALUE;
}
}
private:
TPipe* pipe;
const IndexerCompressEpilogTilingData* tilingData;
GlobalTensor<T0> xGm;
GlobalTensor<int32_t> slotMappingGm;
GlobalTensor<T1> indexerCompressCacheGm;
GlobalTensor<float> indexerCompressCacheScaleGm;
TQue<QuePosition::VECIN, 1> xQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheScaleQue;
TBuf<QuePosition::VECCALC> indexBuf;
LocalTensor<T0> xLocal;
LocalTensor<T1> indexerCompressCacheLocal;
LocalTensor<float> indexerCompressCacheScaleLocal;
float maxValue = 0.0f;
};
} // namespace IndexerCompressEpilog
#endif

View File

@@ -0,0 +1,140 @@
/**
* 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 indexer_compress_epilog_d_full_load.h
* \brief
*/
#ifndef INDEXER_COMPRESS_EPILOG_SINGLE_ROW_MX_FP8_H
#define INDEXER_COMPRESS_EPILOG_SINGLE_ROW_MX_FP8_H
#include "kernel_operator.h"
#include "indexer_compress_epilog_base.h"
namespace IndexerCompressEpilog {
using namespace AscendC;
template <typename T0, typename T1, typename T2>
class IndexerCompressEpilogSingleRowMxFp8 {
public:
__aicore__ inline IndexerCompressEpilogSingleRowMxFp8()
{}
__aicore__ inline void Init(
GM_ADDR x, GM_ADDR slotMapping, GM_ADDR indexerCompressCache, GM_ADDR indexerCompressCacheScale,
GM_ADDR workspace, const IndexerCompressEpilogTilingData* tilingDataPtr, TPipe* pipePtr)
{
pipe = pipePtr;
tilingData = tilingDataPtr;
xGm.SetGlobalBuffer((__gm__ T0*)x);
slotMappingGm.SetGlobalBuffer((__gm__ int32_t*)slotMapping);
indexerCompressCacheGm.SetGlobalBuffer((__gm__ T1*)indexerCompressCache);
indexerCompressCacheScaleGm.SetGlobalBuffer((__gm__ T2*)indexerCompressCacheScale);
roundScale = (tilingData->roundScale == 1);
pipe->InitBuffer(xQue, 2, tilingData->rowFactor * RoundUp<T0>(tilingData->d) * sizeof(T0));
pipe->InitBuffer(indexerCompressCacheQue, 2, tilingData->rowFactor * RoundUp<T1>(tilingData->d) * sizeof(T1));
pipe->InitBuffer(
indexerCompressCacheScaleQue, 2,
tilingData->rowFactor * RoundUp<T2>(tilingData->scaleCol) * sizeof(T2));
pipe->InitBuffer(indexBuf, RoundUp<int32_t>(tilingData->rowFactor) * sizeof(int32_t));
AscendC::SetCtrlSpr<FLOAT_OVERFLOW_MODE_CTRL, FLOAT_OVERFLOW_MODE_CTRL>(0);
}
__aicore__ inline void Process()
{
SetMaxValue();
int64_t curBlockIdx = GetBlockIdx();
int64_t rowOuterLoop =
(curBlockIdx == GetBlockNum() - 1) ? tilingData->rowLoopOfTailBlock : tilingData->rowLoopOfFormerBlock;
int64_t xGmBaseOffset = curBlockIdx * tilingData->rowOfFormerBlock * tilingData->d;
for (int64_t rowOuterIdx = 0; rowOuterIdx < rowOuterLoop; rowOuterIdx++) {
xLocal = xQue.template AllocTensor<T0>();
int64_t curSlotIdx = curBlockIdx * tilingData->rowOfFormerBlock + rowOuterIdx * tilingData->rowFactor;
int64_t slot = slotMappingGm.GetValue(curSlotIdx);
if (slot == -1) {
continue;
}
CopyIn(xGm[xGmBaseOffset + rowOuterIdx * tilingData->rowFactor * tilingData->d],
xLocal, 1, tilingData->d);
xQue.template EnQue(xLocal);
xLocal = xQue.template DeQue<T0>();
indexerCompressCacheLocal = indexerCompressCacheQue.template AllocTensor<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.AllocTensor<T2>();
if (roundScale) {
VFProcessDynamicMxFp8Quant<T1, T2, T0, true>(
indexerCompressCacheLocal, indexerCompressCacheScaleLocal, xLocal, maxValue, fp8Min, fp8Max, 1, tilingData->d);
} else {
VFProcessDynamicMxFp8Quant<T1, T2, T0, false>(
indexerCompressCacheLocal, indexerCompressCacheScaleLocal, xLocal, maxValue, fp8Min, fp8Max, 1, tilingData->d);
}
xQue.template FreeTensor(xLocal);
indexerCompressCacheQue.template EnQue(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template EnQue(indexerCompressCacheScaleLocal);
indexerCompressCacheLocal = indexerCompressCacheQue.template DeQue<T1>();
indexerCompressCacheScaleLocal = indexerCompressCacheScaleQue.template DeQue<T2>();
CopyOut(
indexerCompressCacheLocal, indexerCompressCacheGm[slot * tilingData->d], 1, tilingData->d);
CopyOut(
indexerCompressCacheScaleLocal, indexerCompressCacheScaleGm[slot * CeilDiv(tilingData->d, PER_BLOCK_FP16)], 1,
tilingData->scaleCol);
indexerCompressCacheQue.template FreeTensor(indexerCompressCacheLocal);
indexerCompressCacheScaleQue.template FreeTensor(indexerCompressCacheScaleLocal);
}
}
__aicore__ inline void SetMaxValue()
{
if constexpr (IsSameType<T1, fp8_e5m2_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E5M2_MAX_VALUE;
fp8Max = FP8_E5M2_MAX_VALUE;
fp8Min = FP8_E5M2_MIN_VALUE;
} else if constexpr (IsSameType<T1, fp8_e4m3fn_t>::value) {
maxValue = static_cast<float>(1.0) / FP8_E4M3FN_MAX_VALUE;
fp8Max = FP8_E4M3FN_MAX_VALUE;
fp8Min = FP8_E4M3FN_MIN_VALUE;
}
}
private:
TPipe* pipe;
const IndexerCompressEpilogTilingData* tilingData;
GlobalTensor<T0> xGm;
GlobalTensor<int32_t> slotMappingGm;
GlobalTensor<T1> indexerCompressCacheGm;
GlobalTensor<T2> indexerCompressCacheScaleGm;
TQue<QuePosition::VECIN, 1> xQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheQue;
TQue<QuePosition::VECOUT, 1> indexerCompressCacheScaleQue;
TBuf<QuePosition::VECCALC> indexBuf;
LocalTensor<T0> xLocal;
LocalTensor<T1> indexerCompressCacheLocal;
LocalTensor<T2> indexerCompressCacheScaleLocal;
float maxValue = 0.0f;
float fp8Min = 0.0f;
float fp8Max = 0.0f;
bool roundScale = true;
};
} // namespace IndexerCompressEpilog
#endif

Some files were not shown because too many files have changed in this diff Show More