Add generate.py for codegen

This commit is contained in:
Clement Lin
2025-04-14 18:11:51 +08:00
parent a664958e9d
commit 67bd9e4bb3
4 changed files with 838 additions and 154 deletions

View File

@@ -1,19 +1,59 @@
set(FLASH_ATTENTION_FWD_KNOWN_APIS "fwd")
set(FLASH_ATTENTION_FWD_ENABLE_APIS "fwd" CACHE STRING
"semicolon-separated list of APIs to generate (${FLASH_ATTENTION_FWD_KNOWN_APIS}) & link, or \"all\".")
if(FLASH_ATTENTION_FWD_ENABLE_APIS STREQUAL "all")
set(FLASH_ATTENTION_FWD_ENABLE_APIS ${FLASH_ATTENTION_FWD_KNOWN_APIS})
endif()
execute_process(
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/generate.py
--api ${FLASH_ATTENTION_FWD_ENABLE_APIS}
--working_path ${CMAKE_CURRENT_BINARY_DIR}
--list_blobs
RESULT_VARIABLE ret
)
if(ret AND NOT ret EQUAL 0)
message(FATAL_ERROR "Failed to list Flash Attention kernels via Python. ${ret}")
endif()
file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/flash_attention_fwd_blobs.txt FLASH_ATTENTION_FWD_GEN_BLOBS)
add_custom_command(
OUTPUT ${FLASH_ATTENTION_FWD_GEN_BLOBS}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/generate.py
--api ${FLASH_ATTENTION_FWD_ENABLE_APIS}
--working_path ${CMAKE_CURRENT_BINARY_DIR}
--gen_blobs
)
set(EXAMPLE_REDUCE "codegen_basic_flash_attention_fwd")
# not using add_example_executable() to add this target, since we don't want this to have
# to be included in "make all/install/check"
message("adding example ${EXAMPLE_REDUCE}")
add_executable(${EXAMPLE_REDUCE} EXCLUDE_FROM_ALL flash_attention_fwd.cpp)
target_include_directories(${EXAMPLE_REDUCE} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
add_executable(${EXAMPLE_REDUCE}
EXCLUDE_FROM_ALL
flash_attention_fwd.cpp
)
target_include_directories(${EXAMPLE_REDUCE}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(${EXAMPLE_REDUCE} PRIVATE ${FLASH_ATTENTION_FWD_GEN_BLOBS})
message("FLASH_ATTENTION_FWD_GEN_BLOBS = ${FLASH_ATTENTION_FWD_GEN_BLOBS}")
set(EXAMPLE_REDUCE_COMPILE_OPTIONS)
list(APPEND EXAMPLE_REDUCE_COMPILE_OPTIONS
-Wno-undefined-func-template
-Wno-float-equal
--offload-compress
)
# NOTE: we turn off undefined-func-template to let source compile without explicit declare function specializations
list(APPEND EXAMPLE_REDUCE_COMPILE_OPTIONS -Wno-undefined-func-template -Wno-float-equal)
target_compile_options(${EXAMPLE_REDUCE}
PRIVATE
${EXAMPLE_REDUCE_COMPILE_OPTIONS}
)
target_compile_options(${EXAMPLE_REDUCE} PRIVATE ${EXAMPLE_REDUCE_COMPILE_OPTIONS})
# TODO: we have to turn off this global prop, otherwise the progress bar generated
# by cmake will print too many files, execvp: /bin/sh: Argument list too long
# however, this property may affect global
# TODO: consider codegen a makefile by us
set_property(GLOBAL PROPERTY RULE_MESSAGES OFF)
set_property(GLOBAL PROPERTY RULE_MESSAGES OFF)

View File

@@ -131,7 +131,7 @@ int main(int argc, char* argv[])
};
float ave_time = ck_tile::flash_attention_fwd<QDataType,
QDataType,
KDataType,
VDataType,
SaccDataType,
SMPLComputeDataType,

View File

@@ -4,6 +4,7 @@
#pragma once
#include "ck_tile/core.hpp"
#include "ck_tile/host.hpp"
#include "ck_tile/ops/common.hpp"
#include "ck_tile/ops/gemm/warp/warp_gemm.hpp"
#include "ck_tile/core/tensor/tile_distribution.hpp"
@@ -142,126 +143,167 @@ struct FlashAttentionFwd
}
};
// TODO: fwd_api.cpp
template <typename SaccDataType_,
typename SMPLComputeDataType_,
typename PDataType_,
typename OaccDataType_,
index_t kBlockSize_,
index_t kHeadDim_,
index_t kM0PerBlock_,
index_t kN0PerBlock_,
index_t kK0PerBlock_,
index_t kN1PerBlock_,
index_t kK1PerBlock_>
struct flash_attention_fwd_traits_
{
using SaccDataType = ck_tile::remove_cvref_t<SaccDataType_>;
using SMPLComputeDataType = ck_tile::remove_cvref_t<SMPLComputeDataType_>;
using PDataType = ck_tile::remove_cvref_t<PDataType_>;
using OaccDataType = ck_tile::remove_cvref_t<OaccDataType_>;
// // TODO: fwd_api.cpp
// template <typename SaccDataType_,
// typename SMPLComputeDataType_,
// typename PDataType_,
// typename OaccDataType_,
// index_t kBlockSize_,
// index_t kHeadDim_,
// index_t kM0PerBlock_,
// index_t kN0PerBlock_,
// index_t kK0PerBlock_,
// index_t kN1PerBlock_,
// index_t kK1PerBlock_>
// struct flash_attention_fwd_traits_
// {
// using SaccDataType = ck_tile::remove_cvref_t<SaccDataType_>;
// using SMPLComputeDataType = ck_tile::remove_cvref_t<SMPLComputeDataType_>;
// using PDataType = ck_tile::remove_cvref_t<PDataType_>;
// using OaccDataType = ck_tile::remove_cvref_t<OaccDataType_>;
static constexpr index_t kBlockSize = kBlockSize_;
static constexpr index_t kHeadDim = kHeadDim_;
static constexpr index_t kM0PerBlock = kM0PerBlock_;
static constexpr index_t kN0PerBlock = kN0PerBlock_;
static constexpr index_t kK0PerBlock = kK0PerBlock_;
static constexpr index_t kN1PerBlock = kN1PerBlock_;
static constexpr index_t kK1PerBlock = kK1PerBlock_;
// static constexpr index_t kBlockSize = kBlockSize_;
// static constexpr index_t kHeadDim = kHeadDim_;
// static constexpr index_t kM0PerBlock = kM0PerBlock_;
// static constexpr index_t kN0PerBlock = kN0PerBlock_;
// static constexpr index_t kK0PerBlock = kK0PerBlock_;
// static constexpr index_t kN1PerBlock = kN1PerBlock_;
// static constexpr index_t kK1PerBlock = kK1PerBlock_;
static constexpr ck_tile::index_t kWarpPerCu = 8; // 2 warps per SIMD
static constexpr ck_tile::index_t kWarpPerBlock = kBlockSize / warpSize;
static constexpr ck_tile::index_t kBlockPerCu = kWarpPerCu / kWarpPerBlock;
};
// static constexpr ck_tile::index_t kWarpPerCu = 8; // 2 warps per SIMD
// static constexpr ck_tile::index_t kWarpPerBlock = kBlockSize / warpSize;
// static constexpr ck_tile::index_t kBlockPerCu = kWarpPerCu / kWarpPerBlock;
// };
// TODO: fwd_api.cpp, fwd_common.cpp
template <typename SaccDataType,
typename SMPLComputeDataType,
typename PDataType,
typename OaccDataType,
index_t kBlockSize,
index_t kHeadDim,
index_t kM0PerBlock,
index_t kN0PerBlock,
index_t kK0PerBlock,
index_t kN1PerBlock,
index_t kK1PerBlock>
using traits_ = flash_attention_fwd_traits_<SaccDataType,
SMPLComputeDataType,
PDataType,
OaccDataType,
kBlockSize,
kHeadDim,
kM0PerBlock,
kN0PerBlock,
kK0PerBlock,
kN1PerBlock,
kK1PerBlock>;
// // TODO: fwd_api.cpp, fwd_common.cpp
// template <typename SaccDataType,
// typename SMPLComputeDataType,
// typename PDataType,
// typename OaccDataType,
// index_t kBlockSize,
// index_t kHeadDim,
// index_t kM0PerBlock,
// index_t kN0PerBlock,
// index_t kK0PerBlock,
// index_t kN1PerBlock,
// index_t kK1PerBlock>
// using traits_ = flash_attention_fwd_traits_<SaccDataType,
// SMPLComputeDataType,
// PDataType,
// OaccDataType,
// kBlockSize,
// kHeadDim,
// kM0PerBlock,
// kN0PerBlock,
// kK0PerBlock,
// kN1PerBlock,
// kK1PerBlock>;
// // fw_api.cpp
// // Note: this internal API only declare, not define here, otherwise will block `make -j`
// template <typename QDataType,
// typename KDataType,
// typename VDataType,
// typename ODataType,
// typename Traits_>
// float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
// const ck_tile::stream_config& stream_config);
// Note: this internal API only declare, not define here, otherwise will block `make -j`
template <typename QDataType,
typename KDataType,
typename VDataType,
typename ODataType,
typename Traits_>
float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
const ck_tile::stream_config& stream_config);
// TODO: fwd_common.cpp
template <typename QDataType,
typename KDataType,
typename VDataType,
typename ODataType,
typename Traits_>
float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
const ck_tile::stream_config& stream_config) {
using SaccDataType = typename Traits_::SaccDataType;
using SMPLComputeDataType = typename Traits_::SMPLComputeDataType;
using PDataType = typename Traits_::PDataType;
using OaccDataType = typename Traits_::OaccDataType;
// // TODO: fwd_common.cpp
// template <typename QDataType,
// typename KDataType,
// typename VDataType,
// typename ODataType,
// typename Traits_>
// float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
// const ck_tile::stream_config& stream_config) {
// using SaccDataType = typename Traits_::SaccDataType;
// using SMPLComputeDataType = typename Traits_::SMPLComputeDataType;
// using PDataType = typename Traits_::PDataType;
// using OaccDataType = typename Traits_::OaccDataType;
index_t kGridSize = a.Batch * (a.M0 / Traits_::kM0PerBlock) * (a.N1 / Traits_::kN1PerBlock);
// index_t kGridSize = a.Batch * (a.M0 / Traits_::kM0PerBlock) * (a.N1 / Traits_::kN1PerBlock);
std::cout << "grid size " << kGridSize << std::endl;
// std::cout << "grid size " << kGridSize << std::endl;
// return ck_tile::launch_kernel(stream_config,
// ck_tile::make_kernel<Traits_::kBlockSize, Traits_::kBlockPerCu>(
// ck_tile::FlashAttentionFwd<QDataType,
// KDataType,
// VDataType,
// SaccDataType,
// SMPLComputeDataType,
// PDataType,
// OaccDataType,
// ODataType,
// Traits_::kBlockSize,
// Traits_::kHeadDim,
// Traits_::kM0PerBlock,
// Traits_::kN0PerBlock,
// Traits_::kK0PerBlock,
// Traits_::kN1PerBlock,
// Traits_::kK1PerBlock>{},
// kGridSize,
// Traits_::kBlockSize,
// 0,
// a.q_ptr,
// a.k_ptr,
// a.v_ptr,
// a.o_ptr,
// a.M0,
// a.N0,
// a.K0,
// a.N1,
// a.Batch,
// a.strideQ, // StrideQ
// a.strideK, // StrideK
// a.strideV, // StrideV
// a.strideO, // StrideO
// a.batchStrideQ, // BatchStrideQ
// a.batchStrideK, // BatchStrideK
// a.batchStrideV, // BatchStrideV
// a.batchStrideO)); // BatchStrideO
// }
// // TODO: change to only declare
// // TODO: fwd_api.cpp
// template <typename QDataType,
// typename KDataType,
// typename VDataType,
// typename SaccDataType,
// typename SMPLComputeDataType,
// typename PDataType,
// typename OaccDataType,
// typename ODataType>
// float flash_attention_fwd(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
// const ck_tile::stream_config& stream_config) {
// constexpr ck_tile::index_t kM0PerBlock = 128;
// constexpr ck_tile::index_t kN0PerBlock = 128;
// constexpr ck_tile::index_t kK0PerBlock = 32;
// constexpr ck_tile::index_t kN1PerBlock = 128;
// constexpr ck_tile::index_t kK1PerBlock = 32;
// constexpr ck_tile::index_t kBlockSize = 256;
// constexpr ck_tile::index_t kHeadDim = 128;
// return flash_attention_fwd_<QDataType,
// KDataType,
// VDataType,
// ODataType,
// traits_<SaccDataType,
// SMPLComputeDataType,
// PDataType,
// OaccDataType,
// kBlockSize,
// kHeadDim,
// kM0PerBlock,
// kN0PerBlock,
// kK0PerBlock,
// kN1PerBlock,
// kK1PerBlock>>
// (a, stream_config);
// }
return ck_tile::launch_kernel(stream_config,
ck_tile::make_kernel<Traits_::kBlockSize, Traits_::kBlockPerCu>(
ck_tile::FlashAttentionFwd<QDataType,
KDataType,
VDataType,
SaccDataType,
SMPLComputeDataType,
PDataType,
OaccDataType,
ODataType,
Traits_::kBlockSize,
Traits_::kHeadDim,
Traits_::kM0PerBlock,
Traits_::kN0PerBlock,
Traits_::kK0PerBlock,
Traits_::kN1PerBlock,
Traits_::kK1PerBlock>{},
kGridSize,
Traits_::kBlockSize,
0,
a.q_ptr,
a.k_ptr,
a.v_ptr,
a.o_ptr,
a.M0,
a.N0,
a.K0,
a.N1,
a.Batch,
a.strideQ, // StrideQ
a.strideK, // StrideK
a.strideV, // StrideV
a.strideO, // StrideO
a.batchStrideQ, // BatchStrideQ
a.batchStrideK, // BatchStrideK
a.batchStrideV, // BatchStrideV
a.batchStrideO)); // BatchStrideO
}
// TODO: change to only declare
// TODO: fwd_api.cpp
@@ -274,33 +316,7 @@ template <typename QDataType,
typename OaccDataType,
typename ODataType>
float flash_attention_fwd(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
const ck_tile::stream_config& stream_config) {
constexpr ck_tile::index_t kM0PerBlock = 128;
constexpr ck_tile::index_t kN0PerBlock = 128;
constexpr ck_tile::index_t kK0PerBlock = 32;
constexpr ck_tile::index_t kN1PerBlock = 128;
constexpr ck_tile::index_t kK1PerBlock = 32;
const stream_config& stream_config);
constexpr ck_tile::index_t kBlockSize = 256;
constexpr ck_tile::index_t kHeadDim = 128;
return flash_attention_fwd_<QDataType,
KDataType,
VDataType,
ODataType,
traits_<SaccDataType,
SMPLComputeDataType,
PDataType,
OaccDataType,
kBlockSize,
kHeadDim,
kM0PerBlock,
kN0PerBlock,
kK0PerBlock,
kN1PerBlock,
kK1PerBlock>>
(a, stream_config);
}
} // namespace ck_tile

View File

@@ -0,0 +1,628 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
import argparse
from enum import IntEnum
from pathlib import Path
import sys
from typing import List, Optional, Any
import functools
import itertools
import copy
from dataclasses import dataclass
# def get_if_str(idx, total, last_else=True):
# if idx == 0:
# return 'if'
# elif idx < total - 1:
# return 'else if'
# else:
# return 'else' if last_else else 'else if'
def get_if_str(size_, total, last_else=True):
if size_ == "small":
return 'if'
else:
return 'else if'
DATA_TYPE_MAP = {'fp32': 'float',
'fp16': 'ck_tile::half_t',
'bf16': 'ck_tile::bf16_t'}
def BOOL_MAP(b_) -> str:
return 'true' if b_ else 'false'
class FlashAttentionFwdCodegen:
API_TRAITS_DEFINE = """
template <typename SaccDataType_,
typename SMPLComputeDataType_,
typename PDataType_,
typename OaccDataType_,
index_t kBlockSize_,
index_t kHeadDim_,
index_t kM0PerBlock_,
index_t kN0PerBlock_,
index_t kK0PerBlock_,
index_t kN1PerBlock_,
index_t kK1PerBlock_>
struct flash_attention_fwd_traits_
{
using SaccDataType = ck_tile::remove_cvref_t<SaccDataType_>;
using SMPLComputeDataType = ck_tile::remove_cvref_t<SMPLComputeDataType_>;
using PDataType = ck_tile::remove_cvref_t<PDataType_>;
using OaccDataType = ck_tile::remove_cvref_t<OaccDataType_>;
static constexpr index_t kBlockSize = kBlockSize_;
static constexpr index_t kHeadDim = kHeadDim_;
static constexpr index_t kM0PerBlock = kM0PerBlock_;
static constexpr index_t kN0PerBlock = kN0PerBlock_;
static constexpr index_t kK0PerBlock = kK0PerBlock_;
static constexpr index_t kN1PerBlock = kN1PerBlock_;
static constexpr index_t kK1PerBlock = kK1PerBlock_;
static constexpr ck_tile::index_t kWarpPerCu = 8; // 2 warps per SIMD
static constexpr ck_tile::index_t kWarpPerBlock = kBlockSize / warpSize;
static constexpr ck_tile::index_t kBlockPerCu = kWarpPerCu / kWarpPerBlock;
};
template <typename SaccDataType,
typename SMPLComputeDataType,
typename PDataType,
typename OaccDataType,
ck_tile::index_t kBlockSize,
ck_tile::index_t kHeadDim,
ck_tile::index_t kM0PerBlock,
ck_tile::index_t kN0PerBlock,
ck_tile::index_t kK0PerBlock,
ck_tile::index_t kN1PerBlock,
ck_tile::index_t kK1PerBlock>
using traits_ = flash_attention_fwd_traits_<SaccDataType,
SMPLComputeDataType,
PDataType,
OaccDataType,
kBlockSize,
kHeadDim,
kM0PerBlock,
kN0PerBlock,
kK0PerBlock,
kN1PerBlock,
kK1PerBlock>;
"""
# API_COMMON_HEADER = """
# // SPDX-License-Identifier: MIT
# // Copyright (c) 2024, Advanced Micro Devices, Inc. All rights reserved.
# #include <ck_tile/core.hpp>
# #include "flash_attention_fwd.hpp"
# #include <iostream>
# #pragma once
# using S = ck_tile::stream_config;
# using A = FlashAttnArgs;
# {F_traits_define}
# template <typename QDataType,
# typename KDataType,
# typename VDataType,
# typename ODataType,
# typename Traits_>
# float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
# const ck_tile::stream_config& stream_config) {{
# using SaccDataType = typename Traits_::SaccDataType;
# using SMPLComputeDataType = typename Traits_::SMPLComputeDataType;
# using PDataType = typename Traits_::PDataType;
# using OaccDataType = typename Traits_::OaccDataType;
# index_t kGridSize = a.Batch * (a.M0 / Traits_::kM0PerBlock) * (a.N1 / Traits_::kN1PerBlock);
# if(stream_config.log_level_ > 0)
# std::cout << ", " << "FlashAttentionFwd<" << Traits_::kBlockSize << "," << Traits_::kHeadDim << ">" << std::flush;
# return ck_tile::launch_kernel(stream_config,
# ck_tile::make_kernel<Traits_::kBlockSize, Traits_::kBlockPerCu>(
# ck_tile::FlashAttentionFwd<QDataType,
# KDataType,
# VDataType,
# SaccDataType,
# SMPLComputeDataType,
# PDataType,
# OaccDataType,
# ODataType,
# Traits_::kBlockSize,
# Traits_::kHeadDim,
# Traits_::kM0PerBlock,
# Traits_::kN0PerBlock,
# Traits_::kK0PerBlock,
# Traits_::kN1PerBlock,
# Traits_::kK1PerBlock>{{}},
# kGridSize,
# Traits_::kBlockSize,
# 0,
# a.q_ptr,
# a.k_ptr,
# a.v_ptr,
# a.o_ptr,
# a.M0,
# a.N0,
# a.K0,
# a.N1,
# a.Batch,
# a.strideQ, // StrideQ
# a.strideK, // StrideK
# a.strideV, // StrideV
# a.strideO, // StrideO
# a.batchStrideQ, // BatchStrideQ
# a.batchStrideK, // BatchStrideK
# a.batchStrideV, // BatchStrideV
# a.batchStrideO)); // BatchStrideO
# }}
# """
API_BASE = """
// SPDX-License-Identifier: MIT
// Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
#include <ck_tile/core.hpp>
#include "flash_attention_fwd.hpp"
namespace ck_tile {{
{F_traits_define}
// Note: this internal API only declare, not define here, otherwise will block `make -j`
template <typename QDataType,
typename KDataType,
typename VDataType,
typename ODataType,
typename Traits_>
float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
const ck_tile::stream_config& stream_config);
template <typename QDataType,
typename KDataType,
typename VDataType,
typename SaccDataType,
typename SMPLComputeDataType,
typename PDataType,
typename OaccDataType,
typename ODataType>
float flash_attention_fwd(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
const ck_tile::stream_config& stream_config) {{
float r = -1;
{F_dispatch}
return r;
}}
template float flash_attention_fwd<ck_tile::half_t, ck_tile::half_t, ck_tile::half_t, float, float, ck_tile::half_t, float, ck_tile::half_t>(
const FlashAttnArgs<ck_tile::half_t, ck_tile::half_t, ck_tile::half_t, ck_tile::half_t>&,
const ck_tile::stream_config&);
}}
"""
# API_PER_DTYPE = """ {F_if}(std::is_same_v<QDataType, {F_q_type}> && std::is_same_v<KDataType, {F_k_type}> && std::is_same_v<VDataType, {F_v_type}> && std::is_same_v<ODataType, {F_o_type}>) {{
# {F_per_size_case}
# }}
# """
# API_PER_SIZE_CASE = """ {F_if} {F_SIZE_COND} {{
# {F_inner_dispatch}
# }}
# """
API_INNER_CASE = """ {F_if} {F_VEC_COND}
r = flash_attention_fwd_<QDataType, KDataType, VDataType, ODataType, traits_<{F_trait_name}>>(a, stream_config);
"""
INSTANCE_BASE = """
// SPDX-License-Identifier: MIT
// Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
#include "flash_attention_fwd_api_common.hpp"
namespace ck_tile {
// clang-format off
//
{F_instance_def}
// clang-format on
}
"""
def __init__(self, working_path, kernel_filter):
self.working_path = working_path
self.kernel_filter = kernel_filter
@dataclass
class h_traits:
F_SaccDataType: str
F_SMPLComputeDataType: str
F_PDataType: str
F_OaccDataType: str
F_kBlockSize: int
F_kHeadDim: int
F_kM0PerBlock: int
F_kN0PerBlock: int
F_kK0PerBlock: int
F_kN1PerBlock: int
F_kK1PerBlock: int
@property
def trait_name(self) -> str:
return (f"{DATA_TYPE_MAP[self.F_SaccDataType]}, "
f"{DATA_TYPE_MAP[self.F_SMPLComputeDataType]}, "
f"{DATA_TYPE_MAP[self.F_PDataType]}, "
f"{DATA_TYPE_MAP[self.F_OaccDataType]}, "
f"{self.F_kBlockSize}, {self.F_kHeadDim}, "
f"{self.F_kM0PerBlock}, {self.F_kN0PerBlock}, {self.F_kK0PerBlock}, "
f"{self.F_kN1PerBlock}, {self.F_kK1PerBlock}")
@property
def def_name(self) -> str:
return (f"template float flash_attention_fwd_<{DATA_TYPE_MAP['fp16']}, "
f"{DATA_TYPE_MAP['fp16']}, {DATA_TYPE_MAP['fp16']}, {DATA_TYPE_MAP['fp16']}, "
f"traits_<{self.trait_name}>>(const FlashAttnArgs<{DATA_TYPE_MAP['fp16']}, "
f"{DATA_TYPE_MAP['fp16']}, {DATA_TYPE_MAP['fp16']}, {DATA_TYPE_MAP['fp16']}>&, "
"const ck_tile::stream_config&);")
@dataclass
class h_instance:
F_DataTypePair: str # "q,k,v,o"
F_SizeCategory: str # "small", "medium", "large"
instance_list: List[Any] # List[h_traits]
INSTANCE_BASE = """
// SPDX-License-Identifier: MIT
// Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
#include "flash_attention_fwd_api_common.hpp"
namespace ck_tile {{
// clang-format off
//
{F_instance_def}
// clang-format on
}}
"""
@property
def name(self) -> str:
q_type, k_type, v_type, o_type = self.F_DataTypePair.split(',')
dtype_str = f"{q_type}_{k_type}_{v_type}_{o_type}"
return f"flash_attention_fwd_{dtype_str}_{self.F_SizeCategory}"
@property
def content(self) -> str:
instance_defs = '\n'.join(ins.def_name for ins in self.instance_list)
return self.INSTANCE_BASE.format(F_instance_def=instance_defs)
@property
def name_api(self) -> str:
return "flash_attention_fwd_api"
@property
def name_common_header(self) -> str:
return "flash_attention_fwd_api_common"
@property
def content_common_header(self) -> str:
return f"""// SPDX-License-Identifier: MIT
// Copyright (c) 2025, Advanced Micro Devices, Inc. All rights reserved.
#pragma once
#include "flash_attention_fwd.hpp"
namespace ck_tile {{
template <typename SaccDataType_,
typename SMPLComputeDataType_,
typename PDataType_,
typename OaccDataType_,
index_t kBlockSize_,
index_t kHeadDim_,
index_t kM0PerBlock_,
index_t kN0PerBlock_,
index_t kK0PerBlock_,
index_t kN1PerBlock_,
index_t kK1PerBlock_>
struct flash_attention_fwd_traits_
{{
using SaccDataType = ck_tile::remove_cvref_t<SaccDataType_>;
using SMPLComputeDataType = ck_tile::remove_cvref_t<SMPLComputeDataType_>;
using PDataType = ck_tile::remove_cvref_t<PDataType_>;
using OaccDataType = ck_tile::remove_cvref_t<OaccDataType_>;
static constexpr index_t kBlockSize = kBlockSize_;
static constexpr index_t kHeadDim = kHeadDim_;
static constexpr index_t kM0PerBlock = kM0PerBlock_;
static constexpr index_t kN0PerBlock = kN0PerBlock_;
static constexpr index_t kK0PerBlock = kK0PerBlock_;
static constexpr index_t kN1PerBlock = kN1PerBlock_;
static constexpr index_t kK1PerBlock = kK1PerBlock_;
static constexpr ck_tile::index_t kWarpPerCu = 8; // 2 warps per SIMD
static constexpr ck_tile::index_t kWarpPerBlock = kBlockSize / warpSize;
static constexpr ck_tile::index_t kBlockPerCu = kWarpPerCu / kWarpPerBlock;
}};
template <typename SaccDataType,
typename SMPLComputeDataType,
typename PDataType,
typename OaccDataType,
ck_tile::index_t kBlockSize,
ck_tile::index_t kHeadDim,
ck_tile::index_t kM0PerBlock,
ck_tile::index_t kN0PerBlock,
ck_tile::index_t kK0PerBlock,
ck_tile::index_t kN1PerBlock,
ck_tile::index_t kK1PerBlock>
using traits_ = flash_attention_fwd_traits_<SaccDataType,
SMPLComputeDataType,
PDataType,
OaccDataType,
kBlockSize,
kHeadDim,
kM0PerBlock,
kN0PerBlock,
kK0PerBlock,
kN1PerBlock,
kK1PerBlock>;
template <typename QDataType,
typename KDataType,
typename VDataType,
typename ODataType,
typename Traits_>
float flash_attention_fwd_(const FlashAttnArgs<QDataType, KDataType, VDataType, ODataType>& a,
const ck_tile::stream_config& stream_config) {{
using SaccDataType = typename Traits_::SaccDataType;
using SMPLComputeDataType = typename Traits_::SMPLComputeDataType;
using PDataType = typename Traits_::PDataType;
using OaccDataType = typename Traits_::OaccDataType;
index_t kGridSize = a.Batch * (a.M0 / Traits_::kM0PerBlock) * (a.N1 / Traits_::kN1PerBlock);
if(stream_config.log_level_ > 0)
std::cout << ", " << "FlashAttentionFwd<" << Traits_::kBlockSize << "," << Traits_::kHeadDim << ">" << std::flush;
return ck_tile::launch_kernel(stream_config,
ck_tile::make_kernel<Traits_::kBlockSize, Traits_::kBlockPerCu>(
ck_tile::FlashAttentionFwd<QDataType,
KDataType,
VDataType,
SaccDataType,
SMPLComputeDataType,
PDataType,
OaccDataType,
ODataType,
Traits_::kBlockSize,
Traits_::kHeadDim,
Traits_::kM0PerBlock,
Traits_::kN0PerBlock,
Traits_::kK0PerBlock,
Traits_::kN1PerBlock,
Traits_::kK1PerBlock>{{}},
kGridSize,
Traits_::kBlockSize,
0,
a.q_ptr,
a.k_ptr,
a.v_ptr,
a.o_ptr,
a.M0,
a.N0,
a.K0,
a.N1,
a.Batch,
a.strideQ, // StrideQ
a.strideK, // StrideK
a.strideV, // StrideV
a.strideO, // StrideO
a.batchStrideQ, // BatchStrideQ
a.batchStrideK, // BatchStrideK
a.batchStrideV, // BatchStrideV
a.batchStrideO)); // BatchStrideO
}}
}}
"""
def content_api(self, args) -> str:
# Sort based on dtype
t_dtype_dict = {}
blobs = self.get_blobs(args)
for blob in blobs:
if blob.F_DataTypePair not in t_dtype_dict:
t_dtype_dict[blob.F_DataTypePair] = {}
if blob.F_SizeCategory not in t_dtype_dict[blob.F_DataTypePair]:
t_dtype_dict[blob.F_DataTypePair][blob.F_SizeCategory] = []
t_dtype_dict[blob.F_DataTypePair][blob.F_SizeCategory].append(blob)
d_str = ''
for i_d, dtype_ in enumerate(t_dtype_dict):
blob_per_t = t_dtype_dict[dtype_]
size_str = ''
for i_size, size_ in enumerate(blob_per_t):
blob_per_size = blob_per_t[size_]
inner_str = ""
for i_b, b_ in enumerate(blob_per_size):
for i_ins, ins in enumerate(b_.instance_list):
idx_in_size = i_b * len(b_.instance_list) + i_ins
len_in_size = sum(len(b.instance_list) for b in blob_per_size)
size_cond = ""
if size_ == "small":
size_cond = "(a.M0 < 2048 && a.N0 < 2048)"
elif size_ == "medium":
size_cond = "(a.M0 >= 2048 && a.N0 >= 2048 && a.M0 < 4096 && a.N0 < 4096)"
else: # large
size_cond = "(a.M0 >= 4096 || a.N0 >= 4096)"
inner_str += self.API_INNER_CASE.format(
# F_if=get_if_str(idx_in_size, len_in_size, False),
F_if=get_if_str(size_, len_in_size, False),
F_VEC_COND=size_cond,
F_trait_name=ins.trait_name
)
# size_str += self.API_PER_SIZE_CASE.format(
# F_if=get_if_str(i_size, len(blob_per_t)),
# F_SIZE_COND=size_cond,
# F_inner_dispatch=inner_str
# )
size_str += inner_str
# q_type, k_type, v_type, o_type = dtype_.split(',')
# d_str += self.API_PER_DTYPE.format(
# F_if=get_if_str(i_d, len(t_dtype_dict)),
# F_q_type=DATA_TYPE_MAP[q_type],
# F_k_type=DATA_TYPE_MAP[k_type],
# F_v_type=DATA_TYPE_MAP[v_type],
# F_o_type=DATA_TYPE_MAP[o_type],
# F_per_size_case=size_str
# )
d_str += size_str
api_base = self.API_BASE.format(
F_traits_define=self.API_TRAITS_DEFINE,
F_dispatch=d_str
)
return api_base
def get_blobs(self, args):
h_traits = self.h_traits
h_instance = self.h_instance
# Define kernel configurations for different size categories
trait_dict = {
"small": [
h_traits('fp32', 'fp32', 'fp32', 'fp32', 128, 128, 128, 128, 32, 128, 32),
# h_traits('fp32', 'fp32', 'fp32', 'fp32', 256, 128, 128, 64, 32, 64, 32),
],
"medium": [
h_traits('fp32', 'fp32', 'fp32', 'fp32', 128, 128, 128, 128, 32, 128, 32),
# h_traits('fp32', 'fp32', 'fp32', 'fp32', 256, 128, 256, 128, 32, 128, 32),
],
"large": [
h_traits('fp32', 'fp32', 'fp32', 'fp32', 256, 128, 128, 128, 32, 128, 32),
# h_traits('fp32', 'fp32', 'fp32', 'fp32', 512, 128, 256, 256, 32, 256, 32),
]
}
# Toy example only support fp16
dtype_combinations = [
"fp16,fp16,fp16,fp16"
# "bf16,bf16,bf16,bf16"
]
total_blob = []
for dtype_pair in dtype_combinations:
for size_category in trait_dict:
traits = trait_dict[size_category]
# Convert data types for the current dtype_pair
q_type, k_type, v_type, o_type = dtype_pair.split(',')
current_traits = []
for t in traits:
new_t = copy.copy(t)
new_t.F_SaccDataType = 'fp32' # accumulation in fp32
new_t.F_SMPLComputeDataType = 'fp32' # softmax compute in fp32
new_t.F_PDataType = q_type
new_t.F_OaccDataType = 'fp32' # output accumulation in fp32
current_traits.append(new_t)
total_blob.append(h_instance(dtype_pair, size_category, current_traits))
return total_blob
def list_blobs(self, args) -> None:
w_p = Path(self.working_path)
list_p = w_p / 'flash_attention_fwd_blobs.txt'
blobs = self.get_blobs(args)
with list_p.open('w') as list_f:
# API related files
list_f.write(str(w_p / (self.name_api + ".cpp")) + "\n")
list_f.write(str(w_p / (self.name_common_header + ".hpp")) + "\n")
# Kernel instance files
for b in blobs:
list_f.write(str(w_p / (b.name + ".cpp")) + "\n")
def gen_blobs(self, args) -> None:
w_p = Path(self.working_path)
w_str = self.content_api(args)
(w_p / (self.name_api + ".cpp")).write_text(w_str)
(w_p / (self.name_common_header + ".hpp")).write_text(self.content_common_header)
blobs = self.get_blobs(args)
for b in blobs:
(w_p / (b.name + ".cpp")).write_text(b.content)
def list_blobs(args):
api_list = args.api.split(',')
for api in api_list:
if api == 'fwd':
FlashAttentionFwdCodegen(args.working_path, args.filter).list_blobs(args)
def gen_blobs(args):
api_list = args.api.split(',')
for api in api_list:
if api == 'fwd':
FlashAttentionFwdCodegen(args.working_path, args.filter).gen_blobs(args)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog="generate",
description="gen API for Flash Attention kernel",
)
parser.add_argument(
"-a",
"--api",
default='fwd',
required=False,
help="supply API(s) to generate (default: fwd). separated by comma."
)
parser.add_argument(
"-w",
"--working_path",
default="./",
required=False,
help="the path where all the blobs are going to be generated"
)
parser.add_argument(
"-l",
"--list_blobs",
action='store_true',
help="list all the kernels to a file"
)
parser.add_argument(
"-g",
"--gen_blobs",
action='store_true',
help="generate all kernels into different tile"
)
parser.add_argument(
"-f",
"--filter",
required=False,
help="filter out kernels that need to generate"
)
args = parser.parse_args()
if (args.gen_blobs and args.list_blobs) or ((not args.gen_blobs) and (not args.list_blobs)):
print('gen_blobs/list_blobs must specify only one option')
sys.exit()
p = Path(args.working_path)
if not p.exists():
p.mkdir()
if args.list_blobs:
list_blobs(args)
else:
gen_blobs(args)