[jit kernel] support dtype as a cpp template parameter (#16452)
This commit is contained in:
@@ -119,10 +119,15 @@ __global__ void fused_qknorm(const QKNormParams __grid_constant__ params) {
|
||||
PDLTriggerSecondary<kUsePDL>(); // launch secondary kernel
|
||||
}
|
||||
|
||||
template <int64_t kHeadDim, bool kUsePDL>
|
||||
template <int64_t kHeadDim, bool kUsePDL, typename DType>
|
||||
struct QKNormKernel {
|
||||
template <typename PackedFloat, typename Float>
|
||||
static constexpr auto qknorm_kernel = fused_qknorm<kHeadDim, kUsePDL, PackedFloat, Float>;
|
||||
static_assert(
|
||||
std::is_same_v<DType, half> || std::is_same_v<DType, nv_bfloat16>,
|
||||
"Unsupported DType: QKNormKernel only supports half and nv_bfloat16.");
|
||||
using DType2 = host::PackedDType<DType, 2>::type;
|
||||
|
||||
// only initialize once (static variable) to avoid overhead
|
||||
static constexpr auto kernel = fused_qknorm<kHeadDim, kUsePDL, DType2, DType>;
|
||||
|
||||
static void
|
||||
run(const tvm::ffi::TensorView q,
|
||||
@@ -141,19 +146,27 @@ struct QKNormKernel {
|
||||
auto dtype = SymbolicDType{};
|
||||
auto device = SymbolicDevice{};
|
||||
|
||||
/*
|
||||
* We need the .template disambiguator here because this call happens in a dependent context.
|
||||
* After switching to with_dtype<DType>(...) (where DType is a template parameter), the chained expression becomes
|
||||
* dependent. In C++, when calling a member function template via ./-> on a dependent expression, the compiler may
|
||||
* otherwise parse <kDLCUDA> as the < operator instead of template arguments. Adding .template forces correct
|
||||
* parsing and fixes compilation errors (often seen with NVCC/clang). Ref:
|
||||
* https://en.cppreference.com/w/cpp/language/dependent_name
|
||||
*/
|
||||
TensorMatcher({N, Q, D}) // q input
|
||||
.with_strides({Sq, D, 1})
|
||||
.with_dtype<nv_bfloat16, half>(dtype)
|
||||
.with_device<kDLCUDA>(device)
|
||||
.with_dtype<DType>(dtype)
|
||||
.template with_device<kDLCUDA>(device)
|
||||
.verify(q);
|
||||
TensorMatcher({N, K, D}) // k input
|
||||
.with_strides({Sk, D, 1})
|
||||
.with_dtype<nv_bfloat16, half>(dtype)
|
||||
.with_device<kDLCUDA>(device)
|
||||
.with_dtype<DType>(dtype)
|
||||
.template with_device<kDLCUDA>(device)
|
||||
.verify(k);
|
||||
TensorMatcher({D}) // weight
|
||||
.with_dtype<nv_bfloat16, half>(dtype)
|
||||
.with_device<kDLCUDA>(device)
|
||||
.with_dtype<DType>(dtype)
|
||||
.template with_device<kDLCUDA>(device)
|
||||
.verify(q_weight)
|
||||
.verify(k_weight);
|
||||
|
||||
@@ -177,19 +190,10 @@ struct QKNormKernel {
|
||||
.num_tokens = num_tokens,
|
||||
};
|
||||
|
||||
// only initialize once (static variable) to avoid overhead
|
||||
static constexpr auto bf16_kernel = qknorm_kernel<nv_bfloat162, nv_bfloat16>;
|
||||
static constexpr auto fp16_kernel = qknorm_kernel<half2, half>;
|
||||
static const uint32_t kMaxOccupancyTable[2] = {
|
||||
runtime::get_blocks_per_sm(fp16_kernel, kThreadsPerBlock),
|
||||
runtime::get_blocks_per_sm(bf16_kernel, kThreadsPerBlock),
|
||||
};
|
||||
static const uint32_t max_occupancy = runtime::get_blocks_per_sm(kernel, kThreadsPerBlock);
|
||||
static const uint32_t kNumSM = runtime::get_sm_count(device.unwrap().device_id);
|
||||
|
||||
// choose kernel based on dtype
|
||||
const bool use_bf16 = dtype.is_type<nv_bfloat16>();
|
||||
const auto kernel = use_bf16 ? bf16_kernel : fp16_kernel;
|
||||
const auto max_occupancy = kMaxOccupancyTable[use_bf16 ? 1 : 0];
|
||||
const auto num_works = (num_qo_heads + num_kv_heads) * num_tokens;
|
||||
const auto needed_blocks = div_ceil(num_works, kWarpsPerBlock);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user