[MUSA][18/N] Add MUSA-optimized kernel implementations for hot ops (#23255)
Signed-off-by: Joey-gvwal <joey_gvwal@yeah.net> Co-authored-by: R0CKSTAR <yeahdongcn@gmail.com>
This commit is contained in:
@@ -72,6 +72,12 @@ TORCH_LIBRARY_EXPAND(sgl_kernel, m) {
|
||||
m.def("concat_mla_k(Tensor! k, Tensor k_nope, Tensor k_rope) -> ()");
|
||||
m.impl("concat_mla_k", torch::kMUSA, &concat_mla_k);
|
||||
|
||||
m.def(
|
||||
"rotary_embedding(Tensor positions, Tensor! query,"
|
||||
" Tensor!? key, int head_size,"
|
||||
" Tensor cos_sin_cache, bool is_neox) -> ()");
|
||||
m.impl("rotary_embedding", torch::kMUSA, &rotary_embedding);
|
||||
|
||||
/*
|
||||
* From csrc/gemm
|
||||
*/
|
||||
@@ -272,6 +278,42 @@ TORCH_LIBRARY_EXPAND(sgl_kernel, m) {
|
||||
m.def("top_p_renorm_probs(Tensor probs, Tensor! renorm_probs, Tensor? maybe_top_p_arr, float top_p_val) -> ()");
|
||||
m.impl("top_p_renorm_probs", torch::kMUSA, &top_p_renorm_probs);
|
||||
|
||||
/*
|
||||
* From csrc/musa
|
||||
*/
|
||||
m.def(
|
||||
"musa_batched_rotary_embedding_contiguous(Tensor! positions, Tensor! query, Tensor! key, "
|
||||
"int head_size, Tensor! cos_sin_cache, bool is_neox, int rot_dim, Tensor! cos_sin_cache_offsets) -> ()");
|
||||
m.impl("musa_batched_rotary_embedding_contiguous", torch::kMUSA, &batched_rotary_embedding_contiguous);
|
||||
|
||||
m.def(
|
||||
"musa_rotary_embedding_contiguous(Tensor! positions, Tensor! query, Tensor! key, "
|
||||
"int head_size, Tensor! cos_sin_cache, bool is_neox) -> ()");
|
||||
m.impl("musa_rotary_embedding_contiguous", torch::kMUSA, &rotary_embedding_contiguous);
|
||||
|
||||
m.def(
|
||||
"musa_fused_moe_gemv(Tensor! A, Tensor! B, Tensor! C, Tensor? A_scale, Tensor? B_scale,"
|
||||
"Tensor! topk_weights, Tensor! topk_ids, bool mul_routed_weight, int topk, bool use_int4_w4a16,"
|
||||
"bool use_swigelu) -> ()");
|
||||
m.impl("fused_moe_gemv", torch::kMUSA, &fused_moe_gemv);
|
||||
|
||||
m.def(
|
||||
"musa_fused_gemv(Tensor! A, Tensor! B, Tensor! C, Tensor? A_scale, Tensor? B_scale,"
|
||||
"bool use_int4_w4a16, bool use_swigelu, bool use_rms_norm, Tensor? gamma,"
|
||||
"float eps) -> ()");
|
||||
m.impl("musa_fused_gemv", torch::kMUSA, &musa_fused_gemv);
|
||||
|
||||
m.def(
|
||||
"musa_fused_mul_add(Tensor! output, Tensor! self, Tensor! bias,"
|
||||
"float scale) -> ()");
|
||||
m.impl("musa_fused_mul_add", torch::kMUSA, &fused_mul_add);
|
||||
|
||||
m.def(
|
||||
"musa_top_k_top_p_sampling_from_probs(Tensor probs, Tensor output, Tensor? maybe_indices, Tensor? "
|
||||
"maybe_top_k_arr, "
|
||||
"float top_k_val, Tensor? maybe_top_p_arr, float top_p_val, bool deterministic, Generator? gen) -> ()");
|
||||
m.impl("musa_top_k_top_p_sampling_from_probs", torch::kMUSA, &musa_top_k_top_p_sampling_from_probs);
|
||||
|
||||
/*
|
||||
* From csrc/memory
|
||||
*/
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
|
||||
GEN_VECTYPE(float16_t, Half8, 16, 8);
|
||||
GEN_VECTYPE(bfloat16_t, Bhalf8, 16, 8);
|
||||
GEN_VECTYPE(float, float8, 32, 8);
|
||||
template <typename type>
|
||||
class Dtype;
|
||||
|
||||
@@ -309,6 +310,7 @@
|
||||
DEF_VECT(bfloat16_t, Bhalf8);
|
||||
DEF_VECT(float16_t, Half8);
|
||||
DEF_VECT(float, float4);
|
||||
DEF_VECT(float, float8);
|
||||
|
||||
enum class VarUpdateMode { WELFORD, WELFORD_ONLY_MEAN, CHAN, CHAN_ONLY_MEAN };
|
||||
|
||||
@@ -396,7 +398,10 @@
|
||||
size_t n_idx = tx * vlen;
|
||||
size_t n_step = (size_t)blockDim.x * vlen;
|
||||
|
||||
extern __shared__ ComputeType smem[];
|
||||
|
||||
using SrcVec = VecType<SrcDtype, vlen * sizeof(SrcDtype) * 8>;
|
||||
using ComputeVec = VecType<ComputeType, vlen * sizeof(ComputeType) * 8>;
|
||||
|
||||
ComputeType var = 0;
|
||||
const SrcDtype* __restrict p_src = input + m_idx * N;
|
||||
@@ -406,6 +411,7 @@
|
||||
bool m_valid = m_idx < M;
|
||||
if (m_valid) {
|
||||
for (size_t j = n_idx; j < N; j += n_step) {
|
||||
ComputeVec x_vec;
|
||||
SrcVec curr, res_vec, fused_vec;
|
||||
#if ((defined __MUSA_ARCH__) && (__MUSA_ARCH__ == 220))
|
||||
curr = *(SrcVec *)(p_src+j);
|
||||
@@ -416,10 +422,13 @@
|
||||
#endif
|
||||
#pragma unroll
|
||||
for (int k = 0; k < vlen; k++) {
|
||||
fused_vec.val_.elem[k] = curr.val_.elem[k] + res_vec.val_.elem[k];
|
||||
var += (ComputeType)fused_vec.val_.elem[k] * (ComputeType)fused_vec.val_.elem[k];
|
||||
ComputeType x = (ComputeType)curr.val_.elem[k] + (ComputeType)res_vec.val_.elem[k];
|
||||
var += x * x;
|
||||
fused_vec.val_.elem[k] = (SrcDtype)x;
|
||||
x_vec.val_.elem[k] = x;
|
||||
}
|
||||
*(SrcVec*)(p_res + j) = fused_vec;
|
||||
*(ComputeVec*)(smem + j) = x_vec;
|
||||
}
|
||||
}
|
||||
AllReduceOp<ComputeType, BLOCK_X, BLOCK_Y, 1> all_reduce_op;
|
||||
@@ -430,17 +439,17 @@
|
||||
bool with_weight = (weight != NULL);
|
||||
if (with_weight) {
|
||||
for (size_t j = n_idx; j < N; j += n_step) {
|
||||
SrcVec fused_vec, weight_val, dst;
|
||||
SrcVec weight_val, dst;
|
||||
ComputeVec x_vec;
|
||||
x_vec = *(ComputeVec *)(smem + j);
|
||||
#if ((defined __MUSA_ARCH__) && (__MUSA_ARCH__ == 220))
|
||||
fused_vec = *(SrcVec *)(p_res+j);
|
||||
weight_val = *(SrcVec *)(weight+j);
|
||||
weight_val = *(SrcVec *)(weight + j);
|
||||
#elif ((defined __MUSA_ARCH__) && (__MUSA_ARCH__ == 310))
|
||||
fused_vec = SrcVec::load_byp_slc(p_res, j);
|
||||
weight_val = SrcVec::load_byp_slc(weight, j);
|
||||
#endif
|
||||
#pragma unroll
|
||||
for (int k = 0; k < vlen; k++) {
|
||||
dst.val_.elem[k] = (SrcDtype)((ComputeType)fused_vec.val_.elem[k] * inv_var *
|
||||
dst.val_.elem[k] = (SrcDtype)(x_vec.val_.elem[k] * inv_var *
|
||||
(ComputeType)weight_val.val_.elem[k]);
|
||||
}
|
||||
*(SrcVec*)(p_dst + j) = dst;
|
||||
@@ -458,7 +467,7 @@
|
||||
dim3 grid_size{nr_blocks, 1, 1}; \
|
||||
LayerNorm##_KERN##KernelVlen<_SRC_DTYPE, float, \
|
||||
block_x, block_y, _VLEN> \
|
||||
<<<grid_size, block_size, 0, stream>>>( \
|
||||
<<<grid_size, block_size, n * sizeof(float), stream>>>( \
|
||||
static_cast<_SRC_DTYPE*>(input), \
|
||||
static_cast<_SRC_DTYPE*>(residual), \
|
||||
static_cast<_SRC_DTYPE*>(weight), \
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dtype.muh"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <musa_runtime.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
||||
/// Returns the ceiling of (a / b)
|
||||
__device__ __host__ __forceinline__ constexpr int ceil_div(int a, int b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
template <typename T> __device__ __host__ inline T sigmoid(T x) {
|
||||
return 1.f / (1.f + __expf(-x));
|
||||
}
|
||||
|
||||
#define MUDNN_ATTR_INLINE inline __attribute__((always_inline))
|
||||
|
||||
#if defined(__MUSA_ARCH__) && __MUSA_ARCH__ == 310 // MUSIFY_EXCL_LINE
|
||||
#define __SYNCTHREADS_LM __syncthreads_lm()
|
||||
#else
|
||||
#define __SYNCTHREADS_LM __syncthreads()
|
||||
#endif
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <musa_runtime.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// #include "mudnn_config.h"
|
||||
// #include "mudnn/api/impl_base.h"
|
||||
#include "musa/integer_subbyte.h"
|
||||
|
||||
#ifdef __MTGPU__
|
||||
#include <musa_robust.h>
|
||||
#endif
|
||||
|
||||
#include "musa_fp16.h"
|
||||
#include "musa_bf16.h"
|
||||
#include "musa_fp8.h"
|
||||
#include <torch/all.h>
|
||||
|
||||
namespace musa {
|
||||
namespace dnn {
|
||||
|
||||
template <typename T> struct sizeof_bits {
|
||||
static constexpr size_t value = sizeof(T) * 8;
|
||||
};
|
||||
|
||||
|
||||
template <int Bits, bool Signed>
|
||||
struct sizeof_bits<integer_subbyte<Bits, Signed>> {
|
||||
static constexpr size_t value = Bits;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static constexpr int sizeof_bits_v = sizeof_bits<T>::value;
|
||||
|
||||
typedef __half float16_t;
|
||||
#if defined(__MUSACC__) && (__MUSA_ARCH__ >= 220 || !defined(__MUSA_ARCH__))
|
||||
typedef __mt_bfloat16 bfloat16_t;
|
||||
#else
|
||||
typedef int16_t bfloat16_t;
|
||||
#endif
|
||||
|
||||
#define MACRO_UNROLL _Pragma("unroll")
|
||||
|
||||
#define ATTR_ALIGNED(v) __attribute__((aligned(v)))
|
||||
#define SELF_VEC_DEF(BASE_TYPE, VEC_TYPE_V2, VEC_TYPE_V4) \
|
||||
struct ATTR_ALIGNED(sizeof(BASE_TYPE) * 2) VEC_TYPE_V2 { \
|
||||
__device__ VEC_TYPE_V2() {} \
|
||||
__device__ VEC_TYPE_V2(const VEC_TYPE_V2 &t) { \
|
||||
this->x = t.x; \
|
||||
this->y = t.y; \
|
||||
} \
|
||||
BASE_TYPE x, y; \
|
||||
}; \
|
||||
\
|
||||
__device__ __forceinline__ VEC_TYPE_V2 make_##VEC_TYPE_V2(BASE_TYPE x, \
|
||||
BASE_TYPE y) { \
|
||||
VEC_TYPE_V2 t; \
|
||||
t.x = x, t.y = y; \
|
||||
return t; \
|
||||
} \
|
||||
\
|
||||
struct ATTR_ALIGNED(sizeof(BASE_TYPE) * 4) VEC_TYPE_V4 { \
|
||||
__device__ VEC_TYPE_V4() {} \
|
||||
__device__ VEC_TYPE_V4(const VEC_TYPE_V4 &t) { \
|
||||
this->x = t.x; \
|
||||
this->y = t.y; \
|
||||
this->z = t.z; \
|
||||
this->w = t.w; \
|
||||
} \
|
||||
BASE_TYPE x, y, z, w; \
|
||||
}; \
|
||||
\
|
||||
__device__ __forceinline__ VEC_TYPE_V4 make_##VEC_TYPE_V4( \
|
||||
BASE_TYPE x, BASE_TYPE y, BASE_TYPE z, BASE_TYPE w) { \
|
||||
VEC_TYPE_V4 t; \
|
||||
t.x = x, t.y = y, t.z = z, t.w = w; \
|
||||
return t; \
|
||||
}
|
||||
|
||||
SELF_VEC_DEF(float16_t, Half2, Half4)
|
||||
#if defined(__MUSACC__) && (__MUSA_ARCH__ >= 220 || !defined(__MUSA_ARCH__))
|
||||
SELF_VEC_DEF(bfloat16_t, Bhalf2, Bhalf4)
|
||||
#endif
|
||||
|
||||
#define GEN_VECTYPE(_CTYPE, _VECTYPE, _BYTES, _VLEN) \
|
||||
struct ATTR_ALIGNED(_BYTES) _VECTYPE { \
|
||||
__device__ _VECTYPE() {} \
|
||||
__device__ _VECTYPE(const _VECTYPE &t) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < _VLEN; i++) { \
|
||||
this->arr[i] = t.arr[i]; \
|
||||
} \
|
||||
} \
|
||||
_CTYPE arr[_VLEN]; \
|
||||
}
|
||||
GEN_VECTYPE(float16_t, Half8, 16, 8);
|
||||
GEN_VECTYPE(signed char, Char8, 8, 8);
|
||||
GEN_VECTYPE(uint8_t, Uint8, 8, 8);
|
||||
GEN_VECTYPE(int16_t, Short8, 16, 8);
|
||||
GEN_VECTYPE(int16_t, Short16, 32, 16);
|
||||
GEN_VECTYPE(uint16_t, Ushort8, 16, 8);
|
||||
GEN_VECTYPE(uint32_t, UInt8, 32, 8);
|
||||
GEN_VECTYPE(uint32_t, UInt16, 64, 16);
|
||||
GEN_VECTYPE(uint64_t, ULong8, 64, 8);
|
||||
GEN_VECTYPE(uint64_t, ULong16, 128, 16);
|
||||
GEN_VECTYPE(float, Float8, 32, 8);
|
||||
#if defined(__MUSACC__) && (__MUSA_ARCH__ >= 220 || !defined(__MUSA_ARCH__))
|
||||
GEN_VECTYPE(bfloat16_t, Bhalf8, 16, 8);
|
||||
GEN_VECTYPE(bfloat16_t, Bhalf16, 32, 16);
|
||||
GEN_VECTYPE(bfloat16_t, Bhalf32, 64, 32);
|
||||
#endif
|
||||
GEN_VECTYPE(int32_t, Int8, 32, 8);
|
||||
GEN_VECTYPE(int64_t, Long8, 64, 8);
|
||||
GEN_VECTYPE(int64_t, Long16, 128, 16);
|
||||
GEN_VECTYPE(signed char, Char16, 16, 16);
|
||||
GEN_VECTYPE(float16_t, Half16, 32, 16);
|
||||
GEN_VECTYPE(float, Float16, 64, 16);
|
||||
GEN_VECTYPE(int32_t, Int16, 64, 16);
|
||||
|
||||
template <typename type> class Dtype;
|
||||
|
||||
#define INST(_type, _vec2, _vec4) \
|
||||
template <> class Dtype<_type> { \
|
||||
public: \
|
||||
using Scalar = _type; \
|
||||
using Vec2 = _vec2; \
|
||||
using Vec4 = _vec4; \
|
||||
static __device__ __forceinline__ Vec2 make_vec2(_type x, _type y) { \
|
||||
return make_##_vec2(x, y); \
|
||||
} \
|
||||
static __device__ __forceinline__ Vec4 make_vec4(_type x, _type y, \
|
||||
_type z, _type w) { \
|
||||
return make_##_vec4(x, y, z, w); \
|
||||
} \
|
||||
}
|
||||
|
||||
INST(float, float2, float4);
|
||||
INST(float16_t, Half2, Half4);
|
||||
#if defined(__MUSACC__) && (__MUSA_ARCH__ >= 220 || !defined(__MUSA_ARCH__))
|
||||
INST(bfloat16_t, Bhalf2, Bhalf4);
|
||||
#endif
|
||||
INST(int32_t, int2, int4);
|
||||
INST(uint32_t, uint2, uint4);
|
||||
INST(int8_t, char2, char4);
|
||||
INST(uint8_t, uchar2, uchar4);
|
||||
INST(int16_t, short2, short4);
|
||||
INST(uint16_t, ushort2, ushort4);
|
||||
INST(int64_t, long2, long4);
|
||||
INST(uint64_t, ulong2, ulong4);
|
||||
INST(double, double2, double4);
|
||||
|
||||
#undef INST
|
||||
|
||||
template <typename T> struct DeduceVectorizedType {
|
||||
using Type = T;
|
||||
};
|
||||
|
||||
template <> struct DeduceVectorizedType<bool> {
|
||||
using Type = int8_t;
|
||||
};
|
||||
|
||||
template <> struct DeduceVectorizedType<half> {
|
||||
using Type = _Float16;
|
||||
};
|
||||
|
||||
#if defined(__MUSACC__) && (__MUSA_ARCH__ >= 220 || !defined(__MUSA_ARCH__))
|
||||
template <> struct DeduceVectorizedType<bfloat16_t> {
|
||||
using Type = _Float16;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__MUSA_ARCH__) && __MUSA_ARCH__ == 310 // MUSIFY_EXCL_LINE
|
||||
#define LD_BYP_SLC(_BITS, _BYTES) \
|
||||
VecType dst; \
|
||||
const BaseType *addr = ptr + idx; \
|
||||
__lsu_ld_cache_hint(addr,0,2,1,1); \
|
||||
return dst;
|
||||
#else
|
||||
#define LD_BYP_SLC(_BITS, _BYTES) return *(VecType *)(ptr + idx);
|
||||
#endif
|
||||
|
||||
template <typename T, int bits = 16 * 8> struct VecType;
|
||||
|
||||
#define DEF_VECT(_CTYPE, _VECTYPE) \
|
||||
template <> struct VecType<_CTYPE, sizeof(_VECTYPE) * 8> { \
|
||||
static constexpr int vec_bytes = sizeof(_VECTYPE); \
|
||||
static constexpr int bit_per_byte = 8; \
|
||||
using BaseType = _CTYPE; \
|
||||
using RobustTypePtr = __musa::robust_ptr<_CTYPE>; \
|
||||
using Ttype = _VECTYPE; \
|
||||
static constexpr int bits = vec_bytes * bit_per_byte; \
|
||||
static constexpr int vlen = bits / (sizeof(BaseType) * bit_per_byte); \
|
||||
using VectorizedType = typename DeduceVectorizedType<BaseType>::Type; \
|
||||
typedef VectorizedType VxTy __attribute__((vector_size(vec_bytes))); \
|
||||
template <typename OffsetType> \
|
||||
static __device__ __forceinline__ VecType load(const BaseType *ptr, \
|
||||
OffsetType idx) { \
|
||||
return *(VecType *)(ptr + idx); \
|
||||
} \
|
||||
template <typename OffsetType> \
|
||||
static __device__ __forceinline__ VecType \
|
||||
load_byp_slc(const BaseType *ptr, OffsetType idx) { \
|
||||
if constexpr (vec_bytes == 16) { \
|
||||
LD_BYP_SLC(128, 16); \
|
||||
} else if constexpr (vec_bytes == 8) { \
|
||||
LD_BYP_SLC(64, 8); \
|
||||
} else if constexpr (vec_bytes == 4) { \
|
||||
LD_BYP_SLC(32, 4); \
|
||||
} else if constexpr (vec_bytes == 2) { \
|
||||
LD_BYP_SLC(16, 2); \
|
||||
} else { \
|
||||
LD_BYP_SLC(8, 1); \
|
||||
} \
|
||||
} \
|
||||
template <typename OffsetType> \
|
||||
static __device__ __forceinline__ VecType \
|
||||
robust_load(const RobustTypePtr ptr, OffsetType idx) { \
|
||||
return __musa::robust_load<VecType, BaseType>(ptr, idx); \
|
||||
} \
|
||||
\
|
||||
template <typename OffsetType> \
|
||||
static __device__ __forceinline__ void \
|
||||
store(BaseType *ptr, OffsetType idx, const VecType &dst) { \
|
||||
*(VecType *)(ptr + idx) = dst; \
|
||||
} \
|
||||
template <typename OffsetType> \
|
||||
static __device__ __forceinline__ void \
|
||||
robust_store(RobustTypePtr ptr, OffsetType idx, const VecType &dst) { \
|
||||
__musa::robust_store<VecType, BaseType>(dst, ptr, idx); \
|
||||
} \
|
||||
\
|
||||
__device__ VecType() { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
this->val_.elem[i] = 0; \
|
||||
} \
|
||||
} \
|
||||
__device__ VecType(const VecType &t) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
this->val_.elem[i] = t.val_.elem[i]; \
|
||||
} \
|
||||
} \
|
||||
__device__ VecType &operator=(const VecType &t) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
this->val_.elem[i] = t.val_.elem[i]; \
|
||||
} \
|
||||
return *this; \
|
||||
} \
|
||||
__device__ VecType(_CTYPE val) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
this->val_.elem[i] = val; \
|
||||
} \
|
||||
} \
|
||||
template <typename SrcVecType> \
|
||||
friend __device__ VecType operator+(VecType lhs, const SrcVecType &rhs) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
lhs.val_.elem[i] += static_cast<BaseType>(rhs.val_.elem[i]); \
|
||||
} \
|
||||
return lhs; \
|
||||
} \
|
||||
friend __device__ VecType operator+(VecType lhs, const _CTYPE &rhs) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
lhs.val_.elem[i] += rhs; \
|
||||
} \
|
||||
return lhs; \
|
||||
} \
|
||||
friend __device__ VecType operator-(VecType lhs, const VecType &rhs) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
lhs.val_.elem[i] -= rhs.val_.elem[i]; \
|
||||
} \
|
||||
return lhs; \
|
||||
} \
|
||||
friend __device__ VecType operator*(VecType lhs, const VecType &rhs) { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
lhs.val_.elem[i] *= rhs.val_.elem[i]; \
|
||||
} \
|
||||
return lhs; \
|
||||
} \
|
||||
template <typename Func> __device__ VecType &apply() { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
this->val_.elem[i] = Func::apply(this->val_.elem[i]); \
|
||||
} \
|
||||
return *this; \
|
||||
} \
|
||||
template <typename SrcVecType> \
|
||||
static __device__ VecType cvt(const SrcVecType &src) { \
|
||||
VecType dst; \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
dst.val_.elem[i] = (BaseType)(src.val_.elem[i]); \
|
||||
} \
|
||||
return dst; \
|
||||
} \
|
||||
union U { \
|
||||
__device__ U() { \
|
||||
MACRO_UNROLL \
|
||||
for (int i = 0; i < sizeof(Ttype) / sizeof(BaseType); i++) { \
|
||||
this->elem[i] = 0; \
|
||||
} \
|
||||
} \
|
||||
Ttype storage; \
|
||||
BaseType elem[sizeof(Ttype) / sizeof(BaseType)]; \
|
||||
VxTy vt_elem; \
|
||||
}; \
|
||||
U val_{}; \
|
||||
}
|
||||
DEF_VECT(float16_t, float16_t);
|
||||
DEF_VECT(float16_t, Half2);
|
||||
DEF_VECT(float16_t, Half4);
|
||||
DEF_VECT(float16_t, Half8);
|
||||
DEF_VECT(float16_t, Half16);
|
||||
#if defined(__MUSACC__) && (__MUSA_ARCH__ >= 220 || !defined(__MUSA_ARCH__))
|
||||
DEF_VECT(bfloat16_t, bfloat16_t);
|
||||
DEF_VECT(bfloat16_t, Bhalf2);
|
||||
DEF_VECT(bfloat16_t, Bhalf4);
|
||||
DEF_VECT(bfloat16_t, Bhalf8);
|
||||
DEF_VECT(bfloat16_t, Bhalf16);
|
||||
DEF_VECT(bfloat16_t, Bhalf32);
|
||||
#endif
|
||||
DEF_VECT(bool, char);
|
||||
DEF_VECT(bool, char2);
|
||||
DEF_VECT(bool, char3);
|
||||
DEF_VECT(bool, char4);
|
||||
DEF_VECT(bool, Char8);
|
||||
DEF_VECT(bool, Char16);
|
||||
DEF_VECT(int8_t, int8_t);
|
||||
DEF_VECT(int8_t, char2);
|
||||
DEF_VECT(int8_t, char3);
|
||||
DEF_VECT(int8_t, char4);
|
||||
DEF_VECT(int8_t, Char8);
|
||||
DEF_VECT(int8_t, Char16);
|
||||
DEF_VECT(uint8_t, uint8_t);
|
||||
DEF_VECT(uint8_t, uchar2);
|
||||
DEF_VECT(uint8_t, uchar3);
|
||||
DEF_VECT(uint8_t, uchar4);
|
||||
DEF_VECT(uint8_t, uint4);
|
||||
DEF_VECT(uint8_t, Uint8);
|
||||
DEF_VECT(int16_t, int16_t);
|
||||
DEF_VECT(int16_t, short2);
|
||||
DEF_VECT(int16_t, short3);
|
||||
DEF_VECT(int16_t, short4);
|
||||
DEF_VECT(int16_t, Short8);
|
||||
DEF_VECT(int16_t, Short16);
|
||||
DEF_VECT(uint16_t, ushort);
|
||||
DEF_VECT(uint16_t, ushort2);
|
||||
DEF_VECT(uint16_t, ushort3);
|
||||
DEF_VECT(uint16_t, ushort4);
|
||||
DEF_VECT(uint16_t, Ushort8);
|
||||
DEF_VECT(int32_t, int);
|
||||
DEF_VECT(int32_t, int2);
|
||||
DEF_VECT(int32_t, int3);
|
||||
DEF_VECT(int32_t, int4);
|
||||
DEF_VECT(int32_t, Int8);
|
||||
DEF_VECT(int32_t, Int16);
|
||||
DEF_VECT(uint32_t, uint);
|
||||
DEF_VECT(uint32_t, uint2);
|
||||
DEF_VECT(uint32_t, uint3);
|
||||
DEF_VECT(uint32_t, uint4);
|
||||
DEF_VECT(uint32_t, UInt8);
|
||||
DEF_VECT(uint32_t, UInt16);
|
||||
DEF_VECT(uint64_t, uint64_t);
|
||||
DEF_VECT(uint64_t, ulong2);
|
||||
DEF_VECT(uint64_t, ulong3);
|
||||
DEF_VECT(uint64_t, ulong4);
|
||||
DEF_VECT(uint64_t, ULong8);
|
||||
DEF_VECT(uint64_t, ULong16);
|
||||
DEF_VECT(int64_t, int64_t);
|
||||
DEF_VECT(int64_t, long2);
|
||||
DEF_VECT(int64_t, long3);
|
||||
DEF_VECT(int64_t, long4);
|
||||
DEF_VECT(int64_t, Long8);
|
||||
DEF_VECT(int64_t, Long16);
|
||||
DEF_VECT(float, float);
|
||||
DEF_VECT(float, float2);
|
||||
DEF_VECT(float, float3);
|
||||
DEF_VECT(float, float4);
|
||||
DEF_VECT(float, Float8);
|
||||
DEF_VECT(float, Float16);
|
||||
|
||||
DEF_VECT(double, double);
|
||||
DEF_VECT(double, double2);
|
||||
DEF_VECT(double, double3);
|
||||
DEF_VECT(double, double4);
|
||||
|
||||
#undef DEF_VECT
|
||||
#undef MACRO_UNROLL
|
||||
template <typename T> struct ComputeDType {
|
||||
using Type = typename std::conditional<
|
||||
(sizeof(T) >= 4), T,
|
||||
typename std::conditional<
|
||||
std::is_integral<T>::value,
|
||||
typename std::conditional<std::is_unsigned<T>::value, uint32_t,
|
||||
int32_t>::type,
|
||||
float>::type>::type;
|
||||
};
|
||||
template <> struct ComputeDType<bool> {
|
||||
using Type = bool;
|
||||
};
|
||||
|
||||
// static inline bool check_qint8_only_scale(CTR x) {
|
||||
// if (x.type == TensorImpl::Type::QINT8) {
|
||||
// size_t zp_size = x.quant_desc.zero_point.size();
|
||||
// return zp_size == 0 || (zp_size == 1 && x.quant_desc.zero_point[0] == 0);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
template <typename T, typename... Types>
|
||||
bool check_qint8_only_scale(T x0, Types... xn) {
|
||||
bool ok = check_qint8_only_scale(x0);
|
||||
return ok && check_qint8_only_scale(xn...);
|
||||
}
|
||||
#define CHECK_QINT8(ARGS...) \
|
||||
{ \
|
||||
if (!check_qint8_only_scale(ARGS)) { \
|
||||
return fail::NOT_SUPPORTED() << "qint8 only support zero_point==0"; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace musa
|
||||
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <musa_runtime.h>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <musa_bf16.h>
|
||||
#include <musa_fp16.h>
|
||||
|
||||
#include "common.muh"
|
||||
#include "dtype.muh"
|
||||
#include "torch_musa/csrc/core/MUSAGuard.h"
|
||||
#include "torch_musa/csrc/core/MUSAStream.h"
|
||||
#include "torch_musa/csrc/aten/musa/MUSAContext.h"
|
||||
|
||||
using namespace musa::dnn;
|
||||
|
||||
#if defined(__MUSA_ARCH__) && __MUSA_ARCH__ == 310
|
||||
#define ThreadNumPerWarp 32
|
||||
#else
|
||||
#define ThreadNumPerWarp 128
|
||||
#endif
|
||||
|
||||
#define SYNC_IF_NEEDED() \
|
||||
if constexpr (BLOCK_N * BLOCK_K > ThreadNumPerWarp) { \
|
||||
__SYNCTHREADS_LM; \
|
||||
}
|
||||
|
||||
#define CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, _IS_FP8, _IS_RMSNORM) \
|
||||
if (scale_k_group_tile == 128) { \
|
||||
musa_gemv_kernel<_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, block_n, block_k, iobit, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, false, false, _IS_FP8, 128, _IS_RMSNORM> \
|
||||
<<<grid_size, block_size, shmem_size, stream>>>( \
|
||||
static_cast<_CDTYPE*>(C.data_ptr()), \
|
||||
static_cast<_ADTYPE*>(A.data_ptr()), \
|
||||
static_cast<_BDTYPE*>(B.data_ptr()), \
|
||||
static_cast<int*>(topk_ids_ptr), \
|
||||
static_cast<_TOPK_WEIGHT_DTYPE*>(topk_weights_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(a_scale_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(b_scale_ptr), \
|
||||
topk, expert_offset_stride, nr_n, hidden_size, num_experts, half_n_idx, scale_k_len, \
|
||||
static_cast<bfloat16_t*>(rms_gamma_ptr), static_cast<float*>(rms_sum_out_ptr), static_cast<int*>(rms_count_ptr), eps); \
|
||||
} else { \
|
||||
musa_gemv_kernel<_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, block_n, block_k, iobit, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, false, false, _IS_FP8, 64, _IS_RMSNORM> \
|
||||
<<<grid_size, block_size, shmem_size, stream>>>( \
|
||||
static_cast<_CDTYPE*>(C.data_ptr()), \
|
||||
static_cast<_ADTYPE*>(A.data_ptr()), \
|
||||
static_cast<_BDTYPE*>(B.data_ptr()), \
|
||||
static_cast<int*>(topk_ids_ptr), \
|
||||
static_cast<_TOPK_WEIGHT_DTYPE*>(topk_weights_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(a_scale_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(b_scale_ptr), \
|
||||
topk, expert_offset_stride, nr_n, hidden_size, num_experts, half_n_idx, scale_k_len, \
|
||||
static_cast<bfloat16_t*>(rms_gamma_ptr), static_cast<float*>(rms_sum_out_ptr), static_cast<int*>(rms_count_ptr), eps); \
|
||||
} \
|
||||
return;
|
||||
|
||||
#define RUN_SCALE_ROUTE_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, _IS_FP8) \
|
||||
if (mul_routed_weight) { \
|
||||
if (use_swigelu) { \
|
||||
CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, true, true, _IS_FP8, false) \
|
||||
} else if(use_rms_norm) { \
|
||||
CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, true, false, _IS_FP8, true) \
|
||||
} else { \
|
||||
CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, true, false, _IS_FP8, false) \
|
||||
} \
|
||||
} else { \
|
||||
if (use_swigelu) { \
|
||||
CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, false, true, _IS_FP8, false) \
|
||||
} else if (use_rms_norm) { \
|
||||
CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, false, false, _IS_FP8, true) \
|
||||
} else { \
|
||||
CAL_MOE_GEMV_FP8(_ADTYPE, _BDTYPE, _CDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, false, false, _IS_FP8, false) \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CAL_MOE_GEMV_W4A16(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, _IS_RMS_NROM) \
|
||||
if (is_pergroup_scale) { \
|
||||
if (scale_k_group_tile == 128) { \
|
||||
musa_gemv_kernel<_ADTYPE, _BDTYPE, _ADTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, block_n, block_k, iobit, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, true, true, false, 128, _IS_RMS_NROM> \
|
||||
<<<grid_size, block_size, shmem_size, stream>>>( \
|
||||
static_cast<_ADTYPE*>(C.data_ptr()), \
|
||||
static_cast<_ADTYPE*>(A.data_ptr()), \
|
||||
static_cast<_BDTYPE*>(B.data_ptr()), \
|
||||
static_cast<int*>(topk_ids_ptr), \
|
||||
static_cast<_TOPK_WEIGHT_DTYPE*>(topk_weights_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(a_scale_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(b_scale_ptr), \
|
||||
topk, expert_offset_stride, nr_n, hidden_size, num_experts, half_n_idx, scale_k_len, \
|
||||
static_cast<bfloat16_t*>(rms_gamma_ptr), static_cast<float*>(rms_sum_out_ptr), static_cast<int*>(rms_count_ptr), eps); \
|
||||
return; \
|
||||
} else { \
|
||||
musa_gemv_kernel<_ADTYPE, _BDTYPE, _ADTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, block_n, block_k, iobit, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, true, true, false, 64, _IS_RMS_NROM> \
|
||||
<<<grid_size, block_size, shmem_size, stream>>>( \
|
||||
static_cast<_ADTYPE*>(C.data_ptr()), \
|
||||
static_cast<_ADTYPE*>(A.data_ptr()), \
|
||||
static_cast<_BDTYPE*>(B.data_ptr()), \
|
||||
static_cast<int*>(topk_ids_ptr), \
|
||||
static_cast<_TOPK_WEIGHT_DTYPE*>(topk_weights_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(a_scale_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(b_scale_ptr), \
|
||||
topk, expert_offset_stride, nr_n, hidden_size, num_experts, half_n_idx, scale_k_len, \
|
||||
static_cast<bfloat16_t*>(rms_gamma_ptr), static_cast<float*>(rms_sum_out_ptr), static_cast<int*>(rms_count_ptr), eps); \
|
||||
return; \
|
||||
} \
|
||||
} else { \
|
||||
musa_gemv_kernel<_ADTYPE, _BDTYPE, _ADTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, block_n, block_k, iobit, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, true, false, false, 1, _IS_RMS_NROM> \
|
||||
<<<grid_size, block_size, shmem_size, stream>>>( \
|
||||
static_cast<_ADTYPE*>(C.data_ptr()), \
|
||||
static_cast<_ADTYPE*>(A.data_ptr()), \
|
||||
static_cast<_BDTYPE*>(B.data_ptr()), \
|
||||
static_cast<int*>(topk_ids_ptr), \
|
||||
static_cast<_TOPK_WEIGHT_DTYPE*>(topk_weights_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(a_scale_ptr), \
|
||||
static_cast<_SCALE_DTYPE*>(b_scale_ptr), \
|
||||
topk, expert_offset_stride, nr_n, hidden_size, num_experts, half_n_idx, scale_k_len, \
|
||||
static_cast<bfloat16_t*>(rms_gamma_ptr), static_cast<float*>(rms_sum_out_ptr), static_cast<int*>(rms_count_ptr), eps); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#define CAL_MOE_GEMV(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, _IS_RMS_NROM) \
|
||||
musa_gemv_kernel<_ADTYPE, _BDTYPE, _ADTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, block_n, block_k, iobit, _IS_MUL_ROUTED_WEIGHT, _IS_SWGELU, false, false, false, 1, _IS_RMS_NROM> \
|
||||
<<<grid_size, block_size, shmem_size, stream>>>( \
|
||||
static_cast<_ADTYPE*>(C.data_ptr()), \
|
||||
static_cast<_ADTYPE*>(A.data_ptr()), \
|
||||
static_cast<_BDTYPE*>(B.data_ptr()), \
|
||||
static_cast<int*>(topk_ids_ptr), \
|
||||
static_cast<_TOPK_WEIGHT_DTYPE*>(topk_weights_ptr), \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
topk, expert_offset_stride, nr_n, hidden_size, num_experts, half_n_idx, scale_k_len, \
|
||||
static_cast<bfloat16_t*>(rms_gamma_ptr), static_cast<float*>(rms_sum_out_ptr), static_cast<int*>(rms_count_ptr), eps); \
|
||||
return;
|
||||
|
||||
#define RUN_SCALE_ROUTE(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, _CAL_FUNC) \
|
||||
if (mul_routed_weight) { \
|
||||
if (use_swigelu) { \
|
||||
_CAL_FUNC(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, true, true, false) \
|
||||
} else if (use_rms_norm) { \
|
||||
_CAL_FUNC(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, true, false, true) \
|
||||
} else { \
|
||||
_CAL_FUNC(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, true, false, false) \
|
||||
} \
|
||||
} else { \
|
||||
if (use_swigelu) { \
|
||||
_CAL_FUNC(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, false, true, false) \
|
||||
} else if (use_rms_norm) { \
|
||||
_CAL_FUNC(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, false, false, true) \
|
||||
} else { \
|
||||
_CAL_FUNC(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _SCALE_DTYPE, false, false, false) \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RUN_ROUNTE_WEIGHT(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, _CAL_FUNC) \
|
||||
if (!B_scale.has_value() || B_scale->scalar_type() == at::ScalarType::Float) { \
|
||||
RUN_SCALE_ROUTE(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, float, _CAL_FUNC) \
|
||||
} else if (B_scale.has_value() && B_scale->scalar_type() == at::ScalarType::BFloat16) { \
|
||||
RUN_SCALE_ROUTE(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, bfloat16_t, _CAL_FUNC) \
|
||||
} else if (B_scale.has_value() && B_scale->scalar_type() == at::ScalarType::Half) { \
|
||||
RUN_SCALE_ROUTE(_ADTYPE, _BDTYPE, _TOPK_WEIGHT_DTYPE, float16_t, _CAL_FUNC) \
|
||||
}
|
||||
|
||||
#define GEN_LAUNCH_KERN_GEMV(_BLK_N, _BLK_K) \
|
||||
{ \
|
||||
launch_kernel = [&]() { \
|
||||
constexpr int block_n = _BLK_N; \
|
||||
constexpr int block_k = _BLK_K; \
|
||||
TORCH_CHECK(nr_n % block_n == 0, "gemv n need align"); \
|
||||
TORCH_CHECK(hidden_size % block_k == 0, "gemv k need align"); \
|
||||
dim3 block_size{block_n * block_k, 1, 1}; \
|
||||
dim3 grid_size{(uint32_t)ceil_div(reduce_size, block_n), (uint32_t)topk, (uint32_t)bseqlen}; \
|
||||
int shmem_size = block_n * sizeof(float) * block_k; \
|
||||
if (use_int4_w4a16) { \
|
||||
if (A.scalar_type() == at::ScalarType::BFloat16) { \
|
||||
RUN_ROUNTE_WEIGHT(bfloat16_t, int8_t, float, CAL_MOE_GEMV_W4A16) \
|
||||
} else if (A.scalar_type() == at::ScalarType::Half) { \
|
||||
RUN_ROUNTE_WEIGHT(float16_t, int8_t, float, CAL_MOE_GEMV_W4A16) \
|
||||
} \
|
||||
} else if (is_fp8) { \
|
||||
if (A.dtype() == at::ScalarType::BFloat16) { \
|
||||
RUN_SCALE_ROUTE_FP8(bfloat16_t, __mt_fp8_e4m3, bfloat16_t, float, float, true) \
|
||||
} else { \
|
||||
RUN_SCALE_ROUTE_FP8(__mt_fp8_e4m3, __mt_fp8_e4m3, bfloat16_t, float, float, true) \
|
||||
} \
|
||||
} else { \
|
||||
if (A.scalar_type() == at::ScalarType::BFloat16) { \
|
||||
RUN_ROUNTE_WEIGHT(bfloat16_t, bfloat16_t, float, CAL_MOE_GEMV) \
|
||||
} else if (A.scalar_type() == at::ScalarType::Half) { \
|
||||
RUN_ROUNTE_WEIGHT(float16_t, float16_t, float, CAL_MOE_GEMV) \
|
||||
} \
|
||||
} \
|
||||
TORCH_CHECK(false, "no support on moe gemv"); \
|
||||
}; \
|
||||
}
|
||||
|
||||
#define GEN_LAUNCH_KERN(_BLK_N, _BLK_K) \
|
||||
{ \
|
||||
launch_kernel = [&]() { \
|
||||
constexpr int block_n = _BLK_N; \
|
||||
constexpr int block_k = _BLK_K; \
|
||||
TORCH_CHECK(nr_n % block_n == 0, "gemv n need align"); \
|
||||
TORCH_CHECK(hidden_size % block_k == 0, "gemv k need align"); \
|
||||
dim3 block_size{block_n * block_k, 1, 1}; \
|
||||
dim3 grid_size{(uint32_t)ceil_div(reduce_size, block_n), (uint32_t)topk, (uint32_t)bseqlen}; \
|
||||
int shmem_size = block_n * sizeof(float) * block_k; \
|
||||
if (use_int4_w4a16) { \
|
||||
if (A.scalar_type() == at::ScalarType::BFloat16) { \
|
||||
if (topk_weights.scalar_type() == at::ScalarType::Float) { \
|
||||
RUN_ROUNTE_WEIGHT(bfloat16_t, int8_t, float, CAL_MOE_GEMV_W4A16) \
|
||||
} else if (topk_weights.scalar_type() == at::ScalarType::BFloat16) { \
|
||||
RUN_ROUNTE_WEIGHT(bfloat16_t, int8_t, bfloat16_t, CAL_MOE_GEMV_W4A16) \
|
||||
} \
|
||||
} else if (A.scalar_type() == at::ScalarType::Half) { \
|
||||
if (topk_weights.scalar_type() == at::ScalarType::Float) { \
|
||||
RUN_ROUNTE_WEIGHT(float16_t, int8_t, float, CAL_MOE_GEMV_W4A16) \
|
||||
} else if (topk_weights.scalar_type() == at::ScalarType::Half) { \
|
||||
RUN_ROUNTE_WEIGHT(float16_t, int8_t, float16_t, CAL_MOE_GEMV_W4A16) \
|
||||
} \
|
||||
} \
|
||||
} else if (is_fp8) { \
|
||||
if (A.dtype() == at::ScalarType::BFloat16) { \
|
||||
RUN_SCALE_ROUTE_FP8(bfloat16_t, __mt_fp8_e4m3, bfloat16_t, float, float, true) \
|
||||
} else { \
|
||||
RUN_SCALE_ROUTE_FP8(__mt_fp8_e4m3, __mt_fp8_e4m3, bfloat16_t, float, float, true) \
|
||||
} \
|
||||
} else { \
|
||||
if (A.scalar_type() == at::ScalarType::BFloat16) { \
|
||||
if (topk_weights.scalar_type() == at::ScalarType::Float) { \
|
||||
RUN_ROUNTE_WEIGHT(bfloat16_t, bfloat16_t, float, CAL_MOE_GEMV) \
|
||||
} else if (topk_weights.scalar_type() == at::ScalarType::BFloat16) { \
|
||||
RUN_ROUNTE_WEIGHT(bfloat16_t, bfloat16_t, bfloat16_t, CAL_MOE_GEMV) \
|
||||
} \
|
||||
} else if (A.scalar_type() == at::ScalarType::Half) { \
|
||||
if (topk_weights.scalar_type() == at::ScalarType::Float) { \
|
||||
RUN_ROUNTE_WEIGHT(float16_t, float16_t, float, CAL_MOE_GEMV) \
|
||||
} else if (topk_weights.scalar_type() == at::ScalarType::Half) { \
|
||||
RUN_ROUNTE_WEIGHT(float16_t, float16_t, float16_t, CAL_MOE_GEMV) \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
TORCH_CHECK(false, "no support on moe gemv"); \
|
||||
}; \
|
||||
}
|
||||
|
||||
template <typename AType, typename BType, typename CType, typename ScoreType, typename ScaleType,
|
||||
int BLOCK_N, int BLOCK_K, int iobit, bool mul_routed_weight, bool is_swigelu,
|
||||
bool is_w4a16, bool is_per_group_scale, bool is_fp8, int scale_block, bool use_rms_norm>
|
||||
__global__ void musa_gemv_kernel(
|
||||
CType *c_ptr,
|
||||
const AType *a_ptr,
|
||||
const BType *b_ptr,
|
||||
int *expert_idx_table,
|
||||
ScoreType *score_ptr,
|
||||
ScaleType *scale_a,
|
||||
ScaleType *scale_b,
|
||||
int topk,
|
||||
int expert_offset_stride,
|
||||
int n, int k,
|
||||
int nr_expert,
|
||||
int half_n_idx,
|
||||
int scale_k_len,
|
||||
bfloat16_t *gamma,
|
||||
float* sum_out,
|
||||
volatile int *count,
|
||||
float eps) {
|
||||
|
||||
constexpr int bits_of_byte = 8;
|
||||
constexpr int half_blockn = BLOCK_N / 2;
|
||||
constexpr int b_vec_bits = 128;
|
||||
constexpr int Vlen = is_w4a16 ? b_vec_bits / 4 : b_vec_bits / (sizeof(BType) * bits_of_byte);
|
||||
constexpr int w4a16_shift = is_w4a16 ? 2 : 1;
|
||||
constexpr int scale_k_load_cntdown_init = ceil_div(scale_block, (BLOCK_K * Vlen));
|
||||
constexpr bool fuse_castfp8 = (is_fp8 && !std::is_same_v<AType, __mt_fp8_e4m3>);
|
||||
|
||||
using AVecSType = std::conditional_t<std::is_same_v<AType, __mt_fp8_e4m3>, uint8_t, AType>;
|
||||
using BVecSType = std::conditional_t<is_fp8, uint8_t, BType>;
|
||||
using v16f32_t = float __attribute__((vector_size(64)));
|
||||
using v8f32_t = float __attribute__((vector_size(32)));
|
||||
using AVec = typename std::conditional_t<
|
||||
is_w4a16,
|
||||
v16f32_t,
|
||||
typename std::conditional_t<
|
||||
fuse_castfp8,
|
||||
v8f32_t,
|
||||
typename VecType<AVecSType, 128>::Ttype
|
||||
>
|
||||
>;
|
||||
using BVec = typename VecType<BVecSType, b_vec_bits>::Ttype;
|
||||
using fp8x4_vec = unsigned char __attribute__((vector_size(4)));
|
||||
|
||||
int token_idx = blockIdx.z;
|
||||
int expert_idx = blockIdx.y;
|
||||
int real_expert_idx = 0;
|
||||
int t_n_idx = threadIdx.x / BLOCK_K;
|
||||
int t_k_idx = threadIdx.x % BLOCK_K;
|
||||
int n_idx = blockIdx.x * BLOCK_N + t_n_idx;
|
||||
|
||||
if (expert_idx_table != nullptr) {
|
||||
real_expert_idx = expert_idx_table[token_idx * topk + expert_idx];
|
||||
if (real_expert_idx < 0 || real_expert_idx >= nr_expert) {
|
||||
if constexpr (is_swigelu) {
|
||||
if (n_idx < half_n_idx) {
|
||||
int offsets = (token_idx * topk + expert_idx) * half_n_idx + n_idx;
|
||||
c_ptr[offsets] = 0;
|
||||
}
|
||||
} else {
|
||||
int offsets = (token_idx * topk + expert_idx) * half_n_idx * 2 + n_idx;
|
||||
c_ptr[offsets] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int thread_sum_len = is_fp8 ? Vlen / 4 : Vlen;
|
||||
float cur_thread_sum[thread_sum_len];
|
||||
int scale_k_load_cntdown = scale_k_load_cntdown_init;
|
||||
|
||||
extern __shared__ float shared_array[];
|
||||
|
||||
if constexpr (is_swigelu) {
|
||||
if (t_n_idx < half_blockn) {
|
||||
n_idx = blockIdx.x * half_blockn + t_n_idx;
|
||||
} else {
|
||||
n_idx = blockIdx.x * half_blockn + t_n_idx - half_blockn + half_n_idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma unroll
|
||||
for (int i = 0; i < thread_sum_len; i++) {
|
||||
cur_thread_sum[i] = 0.0f;
|
||||
}
|
||||
|
||||
float scale_a_val = 1.0f;
|
||||
float scale_b_val = 1.0f;
|
||||
int scale_a_offset = 0;
|
||||
int scale_b_offset = 0;
|
||||
|
||||
if constexpr (is_w4a16) {
|
||||
scale_b_offset = (real_expert_idx * n + n_idx) * scale_k_len + t_k_idx * Vlen / scale_block;
|
||||
if constexpr (is_swigelu) {
|
||||
scale_b_offset = (real_expert_idx * 2 * n + n_idx) * scale_k_len + t_k_idx * Vlen / scale_block;
|
||||
}
|
||||
scale_b_val = scale_b[scale_b_offset];
|
||||
scale_k_load_cntdown -= 1;
|
||||
} else if (is_fp8) {
|
||||
scale_a_offset = token_idx * scale_k_len + t_k_idx * Vlen / scale_block;
|
||||
scale_b_offset = (real_expert_idx * n + n_idx) / scale_block * scale_k_len + t_k_idx * Vlen / scale_block;
|
||||
if constexpr (is_swigelu) {
|
||||
scale_b_offset = (real_expert_idx * 2 * n + n_idx) / scale_block * scale_k_len + t_k_idx * Vlen / scale_block;
|
||||
}
|
||||
if constexpr (!fuse_castfp8) {
|
||||
scale_a_val = scale_a[scale_a_offset];
|
||||
}
|
||||
scale_b_val = scale_b[scale_b_offset];
|
||||
scale_k_load_cntdown -= 1;
|
||||
}
|
||||
|
||||
const BType *b_base_ptr = b_ptr + ((size_t)real_expert_idx * expert_offset_stride + n_idx * k + t_k_idx * Vlen) / w4a16_shift;
|
||||
|
||||
for (int k_idx = 0; k_idx < k; k_idx += Vlen * BLOCK_K) {
|
||||
AType a_reg[Vlen];
|
||||
BType b_reg[Vlen / w4a16_shift];
|
||||
*(AVec *)(a_reg) = *(AVec *)(a_ptr + token_idx * k + t_k_idx * Vlen + k_idx);
|
||||
*(BVec *)(b_reg) = *(BVec *)(b_base_ptr + k_idx / w4a16_shift);
|
||||
|
||||
if constexpr (is_w4a16 && !is_fp8) {
|
||||
float b_reg_float[Vlen];
|
||||
#pragma unroll
|
||||
for (int i = 0; i < Vlen / 2; i++) {
|
||||
if constexpr (is_per_group_scale) {
|
||||
uint8_t read_u8 = b_reg[i];
|
||||
b_reg_float[i * 2 + 0] = scale_b_val * ((float)(read_u8 & 0xF) - 8.f);
|
||||
b_reg_float[i * 2 + 1] = scale_b_val * ((float)(read_u8 >> 4) - 8.f);
|
||||
} else {
|
||||
int8_t read_s8 = b_reg[i];
|
||||
b_reg_float[i * 2 + 0] = scale_b_val * (float)((int8_t)(read_s8 << 4));
|
||||
b_reg_float[i * 2 + 1] = scale_b_val * (float)((int8_t)(read_s8 & 0xF0));
|
||||
}
|
||||
}
|
||||
if constexpr (is_per_group_scale) {
|
||||
if (scale_k_load_cntdown == 0 && (k_idx + Vlen * BLOCK_K) < k) {
|
||||
scale_b_offset += ceil_div(BLOCK_K * Vlen, scale_block);
|
||||
scale_b_val = scale_b[scale_b_offset];
|
||||
scale_k_load_cntdown = scale_k_load_cntdown_init;
|
||||
}
|
||||
scale_k_load_cntdown -= 1;
|
||||
}
|
||||
#pragma unroll
|
||||
for (int i = 0; i < thread_sum_len; i++) {
|
||||
cur_thread_sum[i] += b_reg_float[i] * (float)a_reg[i];
|
||||
}
|
||||
} else if constexpr (is_fp8) {
|
||||
float scale_val = scale_a_val * scale_b_val;
|
||||
if (scale_k_load_cntdown == 0 && (k_idx + Vlen * BLOCK_K) < k) {
|
||||
scale_a_offset += ceil_div(BLOCK_K * Vlen, scale_block);
|
||||
scale_b_offset += ceil_div(BLOCK_K * Vlen, scale_block);
|
||||
if constexpr (!fuse_castfp8) {
|
||||
scale_a_val = scale_a[scale_a_offset];
|
||||
}
|
||||
scale_b_val = scale_b[scale_b_offset];
|
||||
scale_k_load_cntdown = scale_k_load_cntdown_init;
|
||||
}
|
||||
scale_k_load_cntdown -= 1;
|
||||
for (int i = 0; i < thread_sum_len; i++) {
|
||||
typedef _Float16 _half_v4 __attribute__((ext_vector_type(4)));
|
||||
typedef _Float32 _float_v4 __attribute__((ext_vector_type(4)));
|
||||
_half_v4 a_halfv4;
|
||||
_half_v4 b_halfv4;
|
||||
_float_v4 a_float4;
|
||||
_float_v4 b_float4;
|
||||
if constexpr (fuse_castfp8) {
|
||||
b_halfv4 = __musa_e4m32f16_rn_bst4(reinterpret_cast<const fp8x4_vec*>(b_reg)[i]);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; j++) {
|
||||
cur_thread_sum[i] += scale_val * float(a_reg[i * 4 + j]) * (b_halfv4[j]);
|
||||
}
|
||||
} else {
|
||||
a_halfv4 = __musa_e4m32f16_rn_bst4(reinterpret_cast<const fp8x4_vec*>(a_reg)[i]);
|
||||
b_halfv4 = __musa_e4m32f16_rn_bst4(reinterpret_cast<const fp8x4_vec*>(b_reg)[i]);
|
||||
#pragma unroll
|
||||
for (int j = 0; j < 4; j++) {
|
||||
cur_thread_sum[i] += scale_val * (a_halfv4[j]) * (b_halfv4[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#pragma unroll
|
||||
for (int i = 0; i < thread_sum_len; i++) {
|
||||
cur_thread_sum[i] += (float)b_reg[i] * (float)a_reg[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float rst = 0;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < thread_sum_len; i++) {
|
||||
rst += cur_thread_sum[i];
|
||||
}
|
||||
|
||||
if constexpr (is_w4a16 && !is_per_group_scale) {
|
||||
rst = rst / 16.f;
|
||||
}
|
||||
|
||||
if constexpr (BLOCK_K > 1) {
|
||||
shared_array[threadIdx.x] = rst;
|
||||
SYNC_IF_NEEDED()
|
||||
if (threadIdx.x < BLOCK_N) {
|
||||
rst = 0;
|
||||
#pragma unroll
|
||||
for (int i = 0; i < BLOCK_K; i++) {
|
||||
rst += shared_array[threadIdx.x * BLOCK_K + i];
|
||||
}
|
||||
}
|
||||
if constexpr (is_swigelu) {
|
||||
SYNC_IF_NEEDED()
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (BLOCK_N > ThreadNumPerWarp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (threadIdx.x < BLOCK_N) {
|
||||
int dst_n_idx = blockIdx.x * BLOCK_N + threadIdx.x;
|
||||
if constexpr (is_swigelu) {
|
||||
dst_n_idx = blockIdx.x * half_blockn + threadIdx.x;
|
||||
}
|
||||
|
||||
if constexpr (mul_routed_weight) {
|
||||
float score = (float)score_ptr[token_idx * topk + expert_idx];
|
||||
rst = rst * score;
|
||||
}
|
||||
|
||||
if constexpr (is_swigelu) {
|
||||
shared_array[threadIdx.x] = rst;
|
||||
if (threadIdx.x < half_blockn) {
|
||||
float b = shared_array[threadIdx.x + half_blockn];
|
||||
rst = rst * sigmoid(rst) * b;
|
||||
c_ptr[token_idx * topk * n + expert_idx * n + dst_n_idx] = rst;
|
||||
}
|
||||
} else if constexpr (use_rms_norm) {
|
||||
float rms = rst * rst;
|
||||
int count_val = 0;
|
||||
|
||||
for (int offset = 1; offset < BLOCK_N; offset *= 2) {
|
||||
float peer = __shfl_xor_sync(BLOCK_N, rms, offset);
|
||||
rms += peer;
|
||||
}
|
||||
|
||||
if (threadIdx.x == 0) {
|
||||
atomicAdd(sum_out, rms);
|
||||
__threadfence_block();
|
||||
atomicAdd((int*)(count), 1);
|
||||
}
|
||||
|
||||
while (count_val < gridDim.x) {
|
||||
count_val = count[0];
|
||||
}
|
||||
|
||||
rms = sum_out[0];
|
||||
rst = rst * rsqrtf(rms / n + eps) * float(gamma[dst_n_idx]);
|
||||
c_ptr[token_idx * topk * n + expert_idx * n + dst_n_idx] = rst;
|
||||
} else {
|
||||
c_ptr[token_idx * topk * n + expert_idx * n + dst_n_idx] = rst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockConfig {
|
||||
int block_n;
|
||||
int block_k;
|
||||
float score;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
void musa_fused_gemv(
|
||||
torch::Tensor &A,
|
||||
torch::Tensor &B,
|
||||
torch::Tensor &C,
|
||||
const c10::optional<torch::Tensor> &A_scale,
|
||||
const c10::optional<torch::Tensor> &B_scale,
|
||||
bool use_int4_w4a16,
|
||||
bool use_swigelu,
|
||||
bool use_rms_norm,
|
||||
const c10::optional<torch::Tensor> &gamma,
|
||||
double eps) {
|
||||
|
||||
TORCH_CHECK(A.dim() == 2, "A must be dim 2.")
|
||||
TORCH_CHECK(B.dim() == 2, "B must be dim 2.")
|
||||
|
||||
bool mul_routed_weight = false;
|
||||
int topk = 1;
|
||||
int32_t bseqlen = A.size(0);
|
||||
int32_t hidden_size = A.size(1);
|
||||
int32_t num_experts = 1;
|
||||
int32_t reduce_size = B.size(0);
|
||||
bool is_fp8 = false;
|
||||
|
||||
if (B.dtype() == torch::kFloat8_e4m3fn) {
|
||||
is_fp8 = true;
|
||||
}
|
||||
|
||||
int current_arch = at::musa::getMUSAArch();
|
||||
if (current_arch < 300) {
|
||||
if (is_fp8) {
|
||||
TORCH_CHECK(false, "gemv moe not support Float8_e4m3fn on MUSA arch ", current_arch);
|
||||
}
|
||||
}
|
||||
|
||||
const at::musa::OptionalMUSAGuard device_guard(device_of(A));
|
||||
musaStream_t stream = at::musa::getCurrentMUSAStream();
|
||||
|
||||
void *topk_ids_ptr = nullptr;
|
||||
void *topk_weights_ptr = nullptr;
|
||||
void *a_scale_ptr = nullptr;
|
||||
void *b_scale_ptr = nullptr;
|
||||
|
||||
if (A_scale.has_value()) {
|
||||
a_scale_ptr = A_scale.value().data_ptr();
|
||||
}
|
||||
if (B_scale.has_value()) {
|
||||
b_scale_ptr = B_scale.value().data_ptr();
|
||||
}
|
||||
|
||||
void *rms_gamma_ptr = nullptr;
|
||||
void *rms_sum_out_ptr = nullptr;
|
||||
void *rms_count_ptr = nullptr;
|
||||
|
||||
if (use_rms_norm && gamma.has_value()) {
|
||||
torch::Tensor sum_out = torch::zeros({1}, A.options().dtype(torch::kFloat));
|
||||
torch::Tensor count = torch::zeros({1}, A.options().dtype(torch::kInt));
|
||||
rms_gamma_ptr = gamma.value().data_ptr();
|
||||
rms_sum_out_ptr = sum_out.data_ptr();
|
||||
rms_count_ptr = count.data_ptr();
|
||||
}
|
||||
|
||||
int device;
|
||||
musaGetDevice(&device);
|
||||
musaDeviceProp device_prop;
|
||||
musaGetDeviceProperties(&device_prop, device);
|
||||
int num_mp = device_prop.multiProcessorCount;
|
||||
int expert_offset_stride = reduce_size * hidden_size;
|
||||
int half_n_idx = reduce_size / 2;
|
||||
int scale_k_len = 1;
|
||||
int scale_k_group_tile = 128;
|
||||
|
||||
if (use_int4_w4a16 || is_fp8) {
|
||||
scale_k_len = B_scale->size(1);
|
||||
if (scale_k_len != 1) {
|
||||
scale_k_group_tile = ceil_div(hidden_size, scale_k_len);
|
||||
TORCH_CHECK(scale_k_group_tile == 128 || scale_k_group_tile == 64, "scale_k_group_tile only support 128 or 64");
|
||||
}
|
||||
}
|
||||
|
||||
bool is_pergroup_scale = scale_k_len != 1;
|
||||
int nr_n = use_swigelu ? reduce_size / 2 : reduce_size;
|
||||
|
||||
std::function<void()> launch_kernel;
|
||||
|
||||
BlockConfig configs[] = {
|
||||
{8, 16, 0.f, false},
|
||||
{16, 8, 0.f, false},
|
||||
{32, 4, 0.f, false},
|
||||
{4, 32, 0.f, false},
|
||||
};
|
||||
|
||||
constexpr int iobit = 128;
|
||||
const int bits_of_byte = 8;
|
||||
const int vlen = use_int4_w4a16 ?
|
||||
(iobit / 4):
|
||||
(iobit / (torch::elementSize(B.scalar_type()) * bits_of_byte));
|
||||
|
||||
float target_ratio = static_cast<float>(reduce_size) / hidden_size;
|
||||
|
||||
for (auto& config : configs) {
|
||||
int load_size = config.block_k * vlen;
|
||||
config.valid = (reduce_size % config.block_n == 0) && (hidden_size % load_size == 0) && (load_size % scale_k_group_tile == 0);
|
||||
|
||||
if (config.valid) {
|
||||
float block_ratio = static_cast<float>(config.block_n) / config.block_k;
|
||||
config.score = 1.0f / (1.0f + fabsf(block_ratio - target_ratio));
|
||||
}
|
||||
}
|
||||
|
||||
BlockConfig best_config_storage;
|
||||
if (current_arch < 300) {
|
||||
best_config_storage = {128, 1, -1.0f, false};
|
||||
} else {
|
||||
best_config_storage = {32, 1, -1.0f, false};
|
||||
}
|
||||
BlockConfig* best_config = &best_config_storage;
|
||||
for (auto& config : configs) {
|
||||
if (config.valid && config.score > best_config->score) {
|
||||
best_config = &config;
|
||||
}
|
||||
}
|
||||
|
||||
switch (best_config->block_n) {
|
||||
case 4:
|
||||
switch (best_config->block_k) {
|
||||
case 32: GEN_LAUNCH_KERN_GEMV(4, 32); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=4");
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
switch (best_config->block_k) {
|
||||
case 16: GEN_LAUNCH_KERN_GEMV(8, 16); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=8");
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
switch (best_config->block_k) {
|
||||
case 8: GEN_LAUNCH_KERN_GEMV(16, 8); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=16");
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (best_config->block_k) {
|
||||
case 4: GEN_LAUNCH_KERN_GEMV(32, 4); break;
|
||||
case 1: GEN_LAUNCH_KERN_GEMV(32, 1); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=32");
|
||||
}
|
||||
break;
|
||||
case 128:
|
||||
switch (best_config->block_k) {
|
||||
case 1: GEN_LAUNCH_KERN_GEMV(128, 1);
|
||||
break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=128");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
TORCH_CHECK(false, "Unsupported block configuration");
|
||||
}
|
||||
|
||||
launch_kernel();
|
||||
}
|
||||
|
||||
void fused_moe_gemv(
|
||||
torch::Tensor &A,
|
||||
torch::Tensor &B,
|
||||
torch::Tensor &C,
|
||||
const c10::optional<torch::Tensor> &A_scale,
|
||||
const c10::optional<torch::Tensor> &B_scale,
|
||||
torch::Tensor &topk_weights,
|
||||
torch::Tensor &topk_ids,
|
||||
bool mul_routed_weight,
|
||||
int64_t topk,
|
||||
bool use_int4_w4a16,
|
||||
bool use_swigelu) {
|
||||
|
||||
TORCH_CHECK(A.dim() == 2, "A must be dim 2.")
|
||||
TORCH_CHECK(B.dim() == 3, "B must be dim 3.")
|
||||
|
||||
int32_t bseqlen = A.size(0);
|
||||
bool is_fp8 = false;
|
||||
if (B.dtype() == torch::kFloat8_e4m3fn) {
|
||||
is_fp8 = true;
|
||||
}
|
||||
|
||||
bool use_rms_norm = false;
|
||||
void *rms_gamma_ptr = nullptr;
|
||||
void *rms_sum_out_ptr = nullptr;
|
||||
void *rms_count_ptr = nullptr;
|
||||
float eps = 1e-6;
|
||||
|
||||
int current_arch = at::musa::getMUSAArch();
|
||||
if (current_arch < 300) {
|
||||
if (is_fp8) {
|
||||
TORCH_CHECK(false, "gemv moe not support Float8_e4m3fn on MUSA arch ", current_arch);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t hidden_size = A.size(1);
|
||||
int32_t num_experts = B.size(0);
|
||||
int32_t reduce_size = B.size(1);
|
||||
|
||||
const at::musa::OptionalMUSAGuard device_guard(device_of(A));
|
||||
musaStream_t stream = at::musa::getCurrentMUSAStream();
|
||||
|
||||
void *topk_ids_ptr = topk_ids.data_ptr();
|
||||
void *topk_weights_ptr = topk_weights.data_ptr();
|
||||
void *a_scale_ptr = nullptr;
|
||||
void *b_scale_ptr = nullptr;
|
||||
|
||||
if (A_scale.has_value()) {
|
||||
a_scale_ptr = A_scale.value().data_ptr();
|
||||
}
|
||||
if (B_scale.has_value()) {
|
||||
b_scale_ptr = B_scale.value().data_ptr();
|
||||
}
|
||||
|
||||
int device;
|
||||
musaGetDevice(&device);
|
||||
musaDeviceProp device_prop;
|
||||
musaGetDeviceProperties(&device_prop, device);
|
||||
int num_mp = device_prop.multiProcessorCount;
|
||||
int expert_offset_stride = reduce_size * hidden_size;
|
||||
int half_n_idx = reduce_size / 2;
|
||||
int scale_k_len = 1;
|
||||
int scale_k_group_tile = 128;
|
||||
|
||||
bool is_pergroup_scale = false;
|
||||
if (use_int4_w4a16 || is_fp8) {
|
||||
scale_k_len = B_scale->size(2);
|
||||
if (scale_k_len != 1) {
|
||||
is_pergroup_scale = true;
|
||||
scale_k_group_tile = ceil_div(hidden_size, scale_k_len);
|
||||
TORCH_CHECK(scale_k_group_tile == 128 || scale_k_group_tile == 64, "scale_k_group_tile only support 128 or 64");
|
||||
}
|
||||
}
|
||||
|
||||
int nr_n = use_swigelu ? reduce_size / 2 : reduce_size;
|
||||
|
||||
std::function<void()> launch_kernel;
|
||||
|
||||
BlockConfig configs[] = {
|
||||
{8, 16, 0.f, false},
|
||||
{16, 8, 0.f, false},
|
||||
{32, 4, 0.f, false},
|
||||
{4, 32, 0.f, false},
|
||||
};
|
||||
|
||||
constexpr int iobit = 128;
|
||||
const int bits_of_byte = 8;
|
||||
const int vlen = use_int4_w4a16 ?
|
||||
(iobit / 4):
|
||||
(iobit / (torch::elementSize(B.scalar_type()) * bits_of_byte));
|
||||
|
||||
float target_ratio = static_cast<float>(reduce_size) / hidden_size;
|
||||
|
||||
for (auto& config : configs) {
|
||||
int load_size = config.block_k * vlen;
|
||||
config.valid = (reduce_size % config.block_n == 0) && (hidden_size % load_size == 0) && (load_size % scale_k_group_tile == 0);
|
||||
|
||||
if (config.valid) {
|
||||
float block_ratio = static_cast<float>(config.block_n) / config.block_k;
|
||||
config.score = 1.0f / (1.0f + fabsf(block_ratio - target_ratio));
|
||||
}
|
||||
}
|
||||
|
||||
BlockConfig best_config_storage;
|
||||
if (current_arch < 300) {
|
||||
best_config_storage = {128, 1, -1.0f, false};
|
||||
} else {
|
||||
best_config_storage = {32, 1, -1.0f, false};
|
||||
}
|
||||
BlockConfig* best_config = &best_config_storage;
|
||||
|
||||
for (auto& config : configs) {
|
||||
if (config.valid && config.score > best_config->score) {
|
||||
best_config = &config;
|
||||
}
|
||||
}
|
||||
|
||||
switch (best_config->block_n) {
|
||||
case 4:
|
||||
switch (best_config->block_k) {
|
||||
case 32: GEN_LAUNCH_KERN(4, 32); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=4");
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
switch (best_config->block_k) {
|
||||
case 16: GEN_LAUNCH_KERN(8, 16); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=8");
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
switch (best_config->block_k) {
|
||||
case 8: GEN_LAUNCH_KERN(16, 8); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=16");
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (best_config->block_k) {
|
||||
case 4: GEN_LAUNCH_KERN(32, 4); break;
|
||||
case 1: GEN_LAUNCH_KERN(32, 1); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=32");
|
||||
}
|
||||
break;
|
||||
case 128:
|
||||
switch (best_config->block_k) {
|
||||
case 1: GEN_LAUNCH_KERN(128, 1); break;
|
||||
default: TORCH_CHECK(false, "Unsupported block_k for block_n=128");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
TORCH_CHECK(false, "Unsupported block configuration");
|
||||
}
|
||||
|
||||
launch_kernel();
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <torch/all.h>
|
||||
|
||||
#include "musa.h"
|
||||
#include "musa/dispatch_utils.h"
|
||||
#include "torch_musa/csrc/core/MUSAGuard.h"
|
||||
#include "torch_musa/csrc/core/MUSAStream.h"
|
||||
|
||||
template <typename scalar_t, bool IS_NEOX>
|
||||
inline __device__ void apply_token_rotary_embedding_contiguous(
|
||||
scalar_t* __restrict__ arr, const scalar_t* __restrict__ cos_ptr,
|
||||
const scalar_t* __restrict__ sin_ptr, int rot_offset, int embed_dim,
|
||||
int64_t head_stride) {
|
||||
int x_index, y_index;
|
||||
scalar_t cos, sin;
|
||||
if (IS_NEOX) {
|
||||
// GPT-NeoX style rotary embedding.
|
||||
x_index = rot_offset;
|
||||
y_index = embed_dim + rot_offset;
|
||||
cos = MUSA_LDG(cos_ptr + x_index);
|
||||
sin = MUSA_LDG(sin_ptr + x_index);
|
||||
} else {
|
||||
// GPT-J style rotary embedding.
|
||||
x_index = 2 * rot_offset;
|
||||
y_index = 2 * rot_offset + 1;
|
||||
cos = MUSA_LDG(cos_ptr + x_index / 2);
|
||||
sin = MUSA_LDG(sin_ptr + x_index / 2);
|
||||
}
|
||||
|
||||
scalar_t* x_ptr = arr + x_index * head_stride;
|
||||
scalar_t* y_ptr = arr + y_index * head_stride;
|
||||
|
||||
const scalar_t x = *x_ptr;
|
||||
const scalar_t y = *y_ptr;
|
||||
*x_ptr = x * cos - y * sin;
|
||||
*y_ptr = y * cos + x * sin;
|
||||
}
|
||||
|
||||
template <typename scalar_t, bool IS_NEOX>
|
||||
inline __device__ void apply_rotary_embedding_contiguous(
|
||||
scalar_t* __restrict__ query, // [num_tokens, num_heads, head_size]
|
||||
scalar_t* __restrict__ key, // [num_tokens, num_kv_heads, head_size]
|
||||
const scalar_t* cache_ptr, const int head_size, const int num_heads,
|
||||
const int num_kv_heads, const int rot_dim, const int token_idx,
|
||||
const int64_t query_token_stride, const int64_t query_head_stride,
|
||||
const int64_t query_dim_stride,
|
||||
const int64_t key_token_stride, const int64_t key_head_stride,
|
||||
const int64_t key_dim_stride) {
|
||||
const int embed_dim = rot_dim / 2;
|
||||
const scalar_t* cos_ptr = cache_ptr;
|
||||
const scalar_t* sin_ptr = cache_ptr + embed_dim;
|
||||
|
||||
const int nq = num_heads * embed_dim;
|
||||
for (int i = threadIdx.x; i < nq; i += blockDim.x) {
|
||||
const int head_idx = i / embed_dim;
|
||||
const int rot_offset = i % embed_dim;
|
||||
|
||||
scalar_t* head_query = query +
|
||||
token_idx * query_token_stride +
|
||||
head_idx * query_head_stride;
|
||||
|
||||
apply_token_rotary_embedding_contiguous<scalar_t, IS_NEOX>(
|
||||
head_query, cos_ptr, sin_ptr, rot_offset, embed_dim, query_dim_stride);
|
||||
}
|
||||
|
||||
const int nk = num_kv_heads * embed_dim;
|
||||
for (int i = threadIdx.x; i < nk; i += blockDim.x) {
|
||||
const int head_idx = i / embed_dim;
|
||||
const int rot_offset = i % embed_dim;
|
||||
|
||||
scalar_t* head_key = key +
|
||||
token_idx * key_token_stride +
|
||||
head_idx * key_head_stride;
|
||||
|
||||
apply_token_rotary_embedding_contiguous<scalar_t, IS_NEOX>(
|
||||
head_key, cos_ptr, sin_ptr, rot_offset, embed_dim, key_dim_stride);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename scalar_t, bool IS_NEOX>
|
||||
__global__ void rotary_embedding_kernel_contiguous(
|
||||
const int64_t* __restrict__ positions, // [num_tokens]
|
||||
scalar_t* __restrict__ query, // [num_tokens, num_heads, head_size]
|
||||
scalar_t* __restrict__ key, // [num_tokens, num_kv_heads, head_size]
|
||||
const scalar_t* __restrict__ cos_sin_cache, // [max_position, 2, rot_dim // 2]
|
||||
const int rot_dim,
|
||||
const int64_t query_token_stride, const int64_t query_head_stride,
|
||||
const int64_t query_dim_stride,
|
||||
const int64_t key_token_stride, const int64_t key_head_stride,
|
||||
const int64_t key_dim_stride,
|
||||
const int num_heads, const int num_kv_heads, const int head_size) {
|
||||
// Each thread block is responsible for one token.
|
||||
const int token_idx = blockIdx.x;
|
||||
int64_t pos = positions[token_idx];
|
||||
const scalar_t* cache_ptr = cos_sin_cache + pos * rot_dim;
|
||||
|
||||
apply_rotary_embedding_contiguous<scalar_t, IS_NEOX>(
|
||||
query, key, cache_ptr, head_size, num_heads, num_kv_heads, rot_dim,
|
||||
token_idx,
|
||||
query_token_stride, query_head_stride, query_dim_stride,
|
||||
key_token_stride, key_head_stride, key_dim_stride);
|
||||
}
|
||||
|
||||
template <typename scalar_t, bool IS_NEOX>
|
||||
__global__ void batched_rotary_embedding_kernel_contiguous(
|
||||
const int64_t* __restrict__ positions, // [num_tokens]
|
||||
scalar_t* __restrict__ query, // [num_tokens, num_heads, head_size]
|
||||
scalar_t* __restrict__ key, // [num_tokens, num_kv_heads, head_size]
|
||||
const scalar_t* __restrict__ cos_sin_cache, // [max_position, 2, rot_dim // 2]
|
||||
const int64_t* __restrict__ cos_sin_cache_offsets, // [num_tokens]
|
||||
const int rot_dim,
|
||||
const int64_t query_token_stride, const int64_t query_head_stride,
|
||||
const int64_t query_dim_stride, // stride for each dimension
|
||||
const int64_t key_token_stride, const int64_t key_head_stride,
|
||||
const int64_t key_dim_stride,
|
||||
const int num_heads, const int num_kv_heads, const int head_size) {
|
||||
// Each thread block is responsible for one token.
|
||||
const int token_idx = blockIdx.x;
|
||||
int64_t pos = positions[token_idx];
|
||||
int64_t cos_sin_cache_offset = cos_sin_cache_offsets[token_idx];
|
||||
const scalar_t* cache_ptr =
|
||||
cos_sin_cache + (cos_sin_cache_offset + pos) * rot_dim;
|
||||
|
||||
apply_rotary_embedding_contiguous<scalar_t, IS_NEOX>(
|
||||
query, key, cache_ptr, head_size, num_heads, num_kv_heads, rot_dim,
|
||||
token_idx,
|
||||
query_token_stride, query_head_stride, query_dim_stride,
|
||||
key_token_stride, key_head_stride, key_dim_stride);
|
||||
}
|
||||
|
||||
|
||||
void rotary_embedding_contiguous(
|
||||
torch::Tensor& positions, // [num_tokens]
|
||||
torch::Tensor& query, // [num_tokens, num_heads, head_size]
|
||||
torch::Tensor& key, // [num_tokens, num_kv_heads, head_size]
|
||||
int64_t head_size,
|
||||
torch::Tensor& cos_sin_cache, // [max_position, rot_dim]
|
||||
bool is_neox) {
|
||||
int64_t num_tokens = positions.size(0);
|
||||
|
||||
TORCH_CHECK(query.dim() == 3, "query must be 3D [num_tokens, num_heads, head_size]");
|
||||
TORCH_CHECK(key.dim() == 3, "key must be 3D [num_tokens, num_kv_heads, head_size]");
|
||||
TORCH_CHECK(query.size(0) == num_tokens && key.size(0) == num_tokens,
|
||||
"query, key and positions must have the same number of tokens");
|
||||
|
||||
int64_t query_token_stride = query.stride(0);
|
||||
int64_t query_head_stride = query.stride(1);
|
||||
int64_t query_dim_stride = query.stride(2);
|
||||
|
||||
int64_t key_token_stride = key.stride(0);
|
||||
int64_t key_head_stride = key.stride(1);
|
||||
int64_t key_dim_stride = key.stride(2);
|
||||
|
||||
int num_heads = query.size(1);
|
||||
int num_kv_heads = key.size(1);
|
||||
int rot_dim = cos_sin_cache.size(1);
|
||||
|
||||
dim3 grid(num_tokens);
|
||||
dim3 block(std::min<int64_t>(num_heads * rot_dim / 2, 512));
|
||||
|
||||
const at::musa::OptionalMUSAGuard device_guard(device_of(query));
|
||||
const musaStream_t stream = at::musa::getCurrentMUSAStream();
|
||||
|
||||
MUSA_DISPATCH_FLOATING_TYPES(query.scalar_type(), "rotary_embedding_contiguous", [&] {
|
||||
if (is_neox) {
|
||||
rotary_embedding_kernel_contiguous<scalar_t, true><<<grid, block, 0, stream>>>(
|
||||
positions.data_ptr<int64_t>(),
|
||||
query.data_ptr<scalar_t>(),
|
||||
key.data_ptr<scalar_t>(),
|
||||
cos_sin_cache.data_ptr<scalar_t>(),
|
||||
rot_dim,
|
||||
query_token_stride, query_head_stride, query_dim_stride,
|
||||
key_token_stride, key_head_stride, key_dim_stride,
|
||||
num_heads, num_kv_heads, head_size);
|
||||
} else {
|
||||
rotary_embedding_kernel_contiguous<scalar_t, false><<<grid, block, 0, stream>>>(
|
||||
positions.data_ptr<int64_t>(),
|
||||
query.data_ptr<scalar_t>(),
|
||||
key.data_ptr<scalar_t>(),
|
||||
cos_sin_cache.data_ptr<scalar_t>(),
|
||||
rot_dim,
|
||||
query_token_stride, query_head_stride, query_dim_stride,
|
||||
key_token_stride, key_head_stride, key_dim_stride,
|
||||
num_heads, num_kv_heads, head_size);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void batched_rotary_embedding_contiguous(
|
||||
torch::Tensor& positions, // [num_tokens]
|
||||
torch::Tensor& query, // [num_tokens, num_heads, head_size]
|
||||
torch::Tensor& key, // [num_tokens, num_kv_heads, head_size]
|
||||
int64_t head_size,
|
||||
torch::Tensor& cos_sin_cache, // [max_position, rot_dim]
|
||||
bool is_neox, int64_t rot_dim,
|
||||
torch::Tensor& cos_sin_cache_offsets // [num_tokens]
|
||||
) {
|
||||
int64_t num_tokens = cos_sin_cache_offsets.size(0);
|
||||
|
||||
|
||||
TORCH_CHECK(positions.size(0) == num_tokens,
|
||||
"positions must have the same num_tokens as cos_sin_cache_offsets");
|
||||
TORCH_CHECK(query.dim() == 3, "query must be 3D [num_tokens, num_heads, head_size]");
|
||||
TORCH_CHECK(key.dim() == 3, "key must be 3D [num_tokens, num_kv_heads, head_size]");
|
||||
|
||||
int64_t query_token_stride = query.stride(0);
|
||||
int64_t query_head_stride = query.stride(1);
|
||||
int64_t query_dim_stride = query.stride(2);
|
||||
|
||||
int64_t key_token_stride = key.stride(0);
|
||||
int64_t key_head_stride = key.stride(1);
|
||||
int64_t key_dim_stride = key.stride(2);
|
||||
|
||||
int num_heads = query.size(1);
|
||||
int num_kv_heads = key.size(1);
|
||||
|
||||
dim3 grid(num_tokens);
|
||||
dim3 block(std::min<int64_t>(num_heads * rot_dim / 2, 512));
|
||||
|
||||
const at::musa::OptionalMUSAGuard device_guard(device_of(query));
|
||||
const musaStream_t stream = at::musa::getCurrentMUSAStream();
|
||||
|
||||
MUSA_DISPATCH_FLOATING_TYPES(query.scalar_type(), "batched_rotary_embedding_contiguous", [&] {
|
||||
if (is_neox) {
|
||||
batched_rotary_embedding_kernel_contiguous<scalar_t, true><<<grid, block, 0, stream>>>(
|
||||
positions.data_ptr<int64_t>(),
|
||||
query.data_ptr<scalar_t>(),
|
||||
key.data_ptr<scalar_t>(),
|
||||
cos_sin_cache.data_ptr<scalar_t>(),
|
||||
cos_sin_cache_offsets.data_ptr<int64_t>(),
|
||||
rot_dim,
|
||||
query_token_stride, query_head_stride, query_dim_stride,
|
||||
key_token_stride, key_head_stride, key_dim_stride,
|
||||
num_heads, num_kv_heads, head_size);
|
||||
} else {
|
||||
batched_rotary_embedding_kernel_contiguous<scalar_t, false><<<grid, block, 0, stream>>>(
|
||||
positions.data_ptr<int64_t>(),
|
||||
query.data_ptr<scalar_t>(),
|
||||
key.data_ptr<scalar_t>(),
|
||||
cos_sin_cache.data_ptr<scalar_t>(),
|
||||
cos_sin_cache_offsets.data_ptr<int64_t>(),
|
||||
rot_dim,
|
||||
query_token_stride, query_head_stride, query_dim_stride,
|
||||
key_token_stride, key_head_stride, key_dim_stride,
|
||||
num_heads, num_kv_heads, head_size);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <torch/all.h>
|
||||
|
||||
#include "musa/dispatch_utils.h"
|
||||
#include "musa.h"
|
||||
#include "torch_musa/csrc/aten/musa/MUSADtype.muh"
|
||||
#include "torch_musa/csrc/core/MUSAGuard.h"
|
||||
#include "torch_musa/csrc/core/MUSAStream.h"
|
||||
|
||||
|
||||
typedef __half float16_t;
|
||||
typedef __mt_bfloat16 bfloat16_t;
|
||||
|
||||
__device__ __host__ __forceinline__ constexpr int64_t ceil_div(int64_t a,
|
||||
int64_t b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
template <typename scalar_t, int64_t vlen, int iobit = 128>
|
||||
__global__ void FusedMulAdd(scalar_t *out, scalar_t *self, scalar_t *bias,
|
||||
const scalar_t scale, const int64_t elem) {
|
||||
constexpr int bits_of_byte = 8;
|
||||
using Vec =
|
||||
at::musa::VecType<scalar_t, vlen * sizeof(scalar_t) * bits_of_byte>;
|
||||
|
||||
int64_t tid = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;
|
||||
int64_t grid_stride = (int64_t)gridDim.x * blockDim.x;
|
||||
int64_t grid_stride_vec = grid_stride * vlen;
|
||||
|
||||
for (int64_t offset = tid * vlen; offset < elem; offset += grid_stride_vec) {
|
||||
Vec t = Vec::load(self, offset);
|
||||
Vec b = Vec::load(bias, offset);
|
||||
Vec o;
|
||||
#pragma unroll
|
||||
for (int k = 0; k < vlen; ++k) {
|
||||
o.val_.elem[k] = b.val_.elem[k] + t.val_.elem[k] * scale;
|
||||
}
|
||||
Vec::store(out, offset, o);
|
||||
}
|
||||
}
|
||||
|
||||
void fused_mul_add(torch::Tensor &output, torch::Tensor &self,
|
||||
torch::Tensor &bias, const double scale) {
|
||||
TORCH_CHECK(self.sizes() == output.sizes(),
|
||||
"self and output shape don't match");
|
||||
TORCH_CHECK(self.sizes() == bias.sizes(), "self and bias shape don't match");
|
||||
TORCH_CHECK(self.scalar_type() == bias.scalar_type(),
|
||||
"self and bias should be same type");
|
||||
TORCH_CHECK(self.scalar_type() == output.scalar_type(),
|
||||
"self and output should be same type");
|
||||
TORCH_CHECK(self.scalar_type() == at::ScalarType::Float ||
|
||||
self.scalar_type() == at::ScalarType::BFloat16 ||
|
||||
self.scalar_type() == at::ScalarType::Half,
|
||||
"self's dtype should be in float/half/bfloat16");
|
||||
|
||||
// device guard
|
||||
const at::musa::OptionalMUSAGuard device_guard(device_of(self));
|
||||
|
||||
// Suppose the uncontiguous elementwise is much slower
|
||||
if C10_UNLIKELY (!self.is_contiguous()) {
|
||||
self = self.contiguous();
|
||||
}
|
||||
if C10_UNLIKELY (!bias.is_contiguous()) {
|
||||
bias = bias.contiguous();
|
||||
}
|
||||
|
||||
// follow mudnn config for arch==22
|
||||
const int64_t max_load_vec =
|
||||
self.scalar_type() == at::ScalarType::Float ? 4 : 8;
|
||||
const int64_t numel = self.numel();
|
||||
size_t thread_per_block = 512;
|
||||
if (ceil_div(numel, max_load_vec) <= 128) {
|
||||
thread_per_block = 128;
|
||||
} else if (ceil_div(numel, max_load_vec) <= 256) {
|
||||
thread_per_block = 256;
|
||||
}
|
||||
size_t nr_block = ceil_div(numel, max_load_vec * thread_per_block);
|
||||
|
||||
const musaStream_t stream = at::musa::getCurrentMUSAStream();
|
||||
|
||||
switch (self.scalar_type()) {
|
||||
case at::ScalarType::Float:
|
||||
FusedMulAdd<float, 4><<<nr_block, thread_per_block, 0, stream>>>(
|
||||
static_cast<float *>(output.data_ptr()),
|
||||
static_cast<float *>(self.data_ptr()),
|
||||
static_cast<float *>(bias.data_ptr()), scale, numel);
|
||||
break;
|
||||
case at::ScalarType::Half:
|
||||
FusedMulAdd<float16_t, 8>
|
||||
<<<nr_block, thread_per_block, 0, stream>>>(
|
||||
static_cast<float16_t *>(output.data_ptr()),
|
||||
static_cast<float16_t *>(self.data_ptr()),
|
||||
static_cast<float16_t *>(bias.data_ptr()),
|
||||
static_cast<float16_t>(scale), numel);
|
||||
break;
|
||||
case at::ScalarType::BFloat16:
|
||||
FusedMulAdd<bfloat16_t, 8>
|
||||
<<<nr_block, thread_per_block, 0, stream>>>(
|
||||
static_cast<bfloat16_t *>(output.data_ptr()),
|
||||
static_cast<bfloat16_t *>(self.data_ptr()),
|
||||
static_cast<bfloat16_t *>(bias.data_ptr()),
|
||||
static_cast<bfloat16_t>(scale), numel);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <ATen/Utils.h>
|
||||
#include <ATen/core/Generator.h>
|
||||
#include "torch_musa/csrc/aten/musa/MUSAGeneratorImpl.h"
|
||||
#include <torch/all.h>
|
||||
|
||||
#include "torch_musa/csrc/aten/musa/UnpackRaw.muh"
|
||||
#include <flashinfer/sampling.muh>
|
||||
#include <mutex>
|
||||
|
||||
#include "musa.h"
|
||||
#include "musa/dispatch_utils.h"
|
||||
#include "pytorch_extension_utils.h"
|
||||
#include "torch_musa/csrc/core/MUSAGuard.h"
|
||||
#include "torch_musa/csrc/core/MUSAStream.h"
|
||||
|
||||
namespace musa {
|
||||
namespace sampling {
|
||||
|
||||
#define kRmemEles 16 // should not too large, otherwise it will cause register spilling
|
||||
// reuse code of flashinfer
|
||||
using namespace flashinfer;
|
||||
using namespace flashinfer::sampling;
|
||||
|
||||
template <uint32_t VEC_SIZE, uint32_t BLOCK_THREADS, BlockScanAlgorithm SCAN_ALGORITHM,
|
||||
BlockReduceAlgorithm REDUCE_ALGORITHM, bool DETERMINISTIC, typename Predicate>
|
||||
__device__ __forceinline__ void DeviceSamplingFromProbWithOffset(
|
||||
uint32_t i, uint32_t d, Predicate pred, float u, vec_t<float, VEC_SIZE> prob_vec,
|
||||
float& aggregate,
|
||||
SamplingTempStorage<BLOCK_THREADS, SCAN_ALGORITHM, REDUCE_ALGORITHM>* temp_storage,
|
||||
int offset = 0) {
|
||||
const uint32_t tx = threadIdx.x;
|
||||
float prob_greater_than_threshold[VEC_SIZE];
|
||||
float inclusive_cdf[VEC_SIZE];
|
||||
bool greater_than_u[VEC_SIZE], valid[VEC_SIZE];
|
||||
#pragma unroll
|
||||
for (uint32_t j = 0; j < VEC_SIZE; ++j) {
|
||||
prob_greater_than_threshold[j] = pred(prob_vec[j]) ? prob_vec[j] : 0;
|
||||
valid[j] = pred(prob_vec[j]) && (offset + (i * BLOCK_THREADS + tx) * VEC_SIZE + j < d);
|
||||
}
|
||||
float aggregate_local =
|
||||
BlockReduce<float, BLOCK_THREADS, REDUCE_ALGORITHM>(temp_storage->block_prim.reduce)
|
||||
.template Sum<VEC_SIZE>(prob_greater_than_threshold);
|
||||
if (tx == 0) {
|
||||
temp_storage->block_aggregate.value = aggregate_local;
|
||||
}
|
||||
__syncthreads();
|
||||
aggregate_local = temp_storage->block_aggregate.value;
|
||||
|
||||
if (aggregate + aggregate_local > u) {
|
||||
if constexpr (DETERMINISTIC) {
|
||||
DeterministicInclusiveSum<VEC_SIZE, BLOCK_THREADS, SCAN_ALGORITHM, REDUCE_ALGORITHM>(
|
||||
prob_greater_than_threshold, inclusive_cdf, temp_storage);
|
||||
} else {
|
||||
BlockScan<float, BLOCK_THREADS, SCAN_ALGORITHM>(temp_storage->block_prim.scan)
|
||||
.template InclusiveSum<VEC_SIZE>(prob_greater_than_threshold, inclusive_cdf);
|
||||
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (uint32_t j = 0; j < VEC_SIZE; ++j) {
|
||||
greater_than_u[j] = (inclusive_cdf[j] + aggregate > u) && valid[j];
|
||||
}
|
||||
|
||||
bool greater_than_u_diff[VEC_SIZE];
|
||||
#ifdef FLASHINFER_CUB_SUBTRACTLEFT_DEFINED
|
||||
BlockAdjacentDifference<bool, BLOCK_THREADS>(temp_storage->block_prim.adj_diff)
|
||||
.SubtractLeft<VEC_SIZE>(greater_than_u, greater_than_u_diff, BoolDiffOp());
|
||||
#else
|
||||
BlockAdjacentDifference<bool, BLOCK_THREADS>(temp_storage->block_prim.adj_diff)
|
||||
.template FlagHeads<VEC_SIZE>(greater_than_u_diff, greater_than_u, BoolDiffOp(), 0);
|
||||
#endif
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (uint32_t j = 0; j < VEC_SIZE; ++j) {
|
||||
if (greater_than_u_diff[j]) {
|
||||
atomicMin(&(temp_storage->sampled_id), offset + (i * BLOCK_THREADS + tx) * VEC_SIZE + j);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
// update the last valid index
|
||||
int valid_index[VEC_SIZE];
|
||||
#pragma unroll
|
||||
for (uint32_t j = 0; j < VEC_SIZE; ++j) {
|
||||
if (valid[j]) {
|
||||
valid_index[j] = offset + (i * BLOCK_THREADS + tx) * VEC_SIZE + j;
|
||||
} else {
|
||||
valid_index[j] = -1;
|
||||
}
|
||||
}
|
||||
int max_valid_index =
|
||||
BlockReduce<int, BLOCK_THREADS, REDUCE_ALGORITHM>(temp_storage->block_prim.reduce_int)
|
||||
.Reduce(valid_index, MaxReduceOp{});
|
||||
if (tx == 0 && max_valid_index != -1) {
|
||||
temp_storage->last_valid_id = max_valid_index;
|
||||
}
|
||||
__syncthreads();
|
||||
aggregate += aggregate_local;
|
||||
}
|
||||
|
||||
template <uint32_t BLOCK_THREADS, BlockScanAlgorithm SCAN_ALGORITHM,
|
||||
BlockReduceAlgorithm REDUCE_ALGORITHM, uint32_t VEC_SIZE, bool DETERMINISTIC,
|
||||
typename DType, typename IdType>
|
||||
__global__ void TopKTopPSamplingFromProbKernel(DType* probs, IdType* top_k_arr, float* top_p_arr,
|
||||
IdType* output, IdType* indices, IdType top_k_val,
|
||||
float top_p_val, uint32_t d, uint64_t philox_seed,
|
||||
uint64_t philox_offset) {
|
||||
const uint32_t batch_size = gridDim.x;
|
||||
const uint32_t bx = blockIdx.x, tx = threadIdx.x;
|
||||
murandStatePhilox4_32_10_t state;
|
||||
murand_init(philox_seed, bx, philox_offset, &state);
|
||||
const uint32_t row_idx = indices == nullptr ? bx : indices[bx];
|
||||
const uint32_t k = top_k_arr == nullptr ? top_k_val : top_k_arr[row_idx];
|
||||
const float p = top_p_arr == nullptr ? top_p_val : top_p_arr[row_idx];
|
||||
extern __shared__ __align__(alignof(SamplingTempStorage<BLOCK_THREADS, SCAN_ALGORITHM, REDUCE_ALGORITHM>))
|
||||
uint8_t smem_sampling[];
|
||||
auto& temp_storage =
|
||||
reinterpret_cast<SamplingTempStorage<BLOCK_THREADS, SCAN_ALGORITHM, REDUCE_ALGORITHM>&>(smem_sampling);
|
||||
|
||||
vec_t<float, kRmemEles> rprobs; // persistent
|
||||
vec_t<float, VEC_SIZE> rprobs_d;
|
||||
// elements of one row will be split into three stages by the address it will be stored
|
||||
// [rprobs, probs_vec]
|
||||
// rprobs will be persistent to reuse those elements instead of reload
|
||||
int real_r_size = d > (BLOCK_THREADS * kRmemEles) ? BLOCK_THREADS * kRmemEles : d;
|
||||
int real_rd_size = d > (BLOCK_THREADS * kRmemEles) ? d - BLOCK_THREADS * kRmemEles : 0;
|
||||
|
||||
probs = probs + row_idx * d;
|
||||
DType* global_rprobs = probs;
|
||||
DType* global_rprobs_d = probs + real_r_size;
|
||||
|
||||
// fisrtly, we load some elements to register
|
||||
rprobs.fill(0);
|
||||
if (tx * kRmemEles < real_r_size) {
|
||||
rprobs.cast_load(global_rprobs + tx * kRmemEles);
|
||||
int valid_size = real_r_size - tx * kRmemEles;
|
||||
// mask invalid data
|
||||
for (int i = valid_size; i < kRmemEles; ++i) {
|
||||
rprobs[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
float aggregate;
|
||||
float q = 1;
|
||||
double low = 0, high = 1.f;
|
||||
int sampled_id;
|
||||
|
||||
// sample and check
|
||||
do {
|
||||
temp_storage.sampled_id = d;
|
||||
__syncthreads();
|
||||
float u = murand_uniform(&state) * q;
|
||||
aggregate = 0;
|
||||
|
||||
// fisrtly, sample from rprobs
|
||||
DeviceSamplingFromProbWithOffset<kRmemEles, BLOCK_THREADS, SCAN_ALGORITHM, REDUCE_ALGORITHM, DETERMINISTIC>(
|
||||
0, d, [&](float x) { return x > low; }, u, rprobs, aggregate, &temp_storage);
|
||||
|
||||
if (aggregate <= u) {
|
||||
// secondly, sample from rprobs_d
|
||||
for (uint32_t i = 0; i < ceil_div(real_rd_size, BLOCK_THREADS * VEC_SIZE); ++i) {
|
||||
rprobs_d.fill(0);
|
||||
if ((i * BLOCK_THREADS + tx) * VEC_SIZE + real_r_size < d) {
|
||||
rprobs_d.cast_load(global_rprobs_d + (i * BLOCK_THREADS + tx) * VEC_SIZE);
|
||||
}
|
||||
|
||||
DeviceSamplingFromProbWithOffset<VEC_SIZE, BLOCK_THREADS, SCAN_ALGORITHM, REDUCE_ALGORITHM, DETERMINISTIC>(
|
||||
i, d, [&](float x) { return x > low; }, u, rprobs_d, aggregate, &temp_storage, real_r_size);
|
||||
if (aggregate > u) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// id is sampled
|
||||
__syncthreads();
|
||||
sampled_id = temp_storage.sampled_id;
|
||||
if (sampled_id == d) {
|
||||
// NOTE(Zihao): this would happen when u is very close to 1
|
||||
// and the sum of probabilities is smaller than u
|
||||
// In this case, we use the last valid index as the sampled id
|
||||
sampled_id = temp_storage.last_valid_id;
|
||||
}
|
||||
double pivot_0 = probs[sampled_id];
|
||||
double pivot_1 = (pivot_0 + high) / 2;
|
||||
|
||||
ValueCount<float> aggregate_gt_pivot_0{0, 0}, aggregate_gt_pivot_1{0, 0};
|
||||
// check in rprobs
|
||||
ValueCount<float> probs_gt_pivot_0[VEC_SIZE], probs_gt_pivot_1[VEC_SIZE];
|
||||
#pragma unroll
|
||||
for (int i = 0; i < VEC_SIZE; ++i) {
|
||||
probs_gt_pivot_0[i] = {0, 0};
|
||||
probs_gt_pivot_1[i] = {0, 0};
|
||||
}
|
||||
|
||||
// init to 0
|
||||
for (uint32_t j = 0; j < kRmemEles; j += VEC_SIZE) {
|
||||
#pragma unroll
|
||||
for (uint32_t k = 0; k < VEC_SIZE; ++k) {
|
||||
probs_gt_pivot_0[k] +=
|
||||
{(rprobs[j + k] > pivot_0) ? rprobs[j + k] : 0, (rprobs[j + k] > pivot_0 && (tx)*kRmemEles + j + k < d)};
|
||||
probs_gt_pivot_1[k] +=
|
||||
{(rprobs[j + k] > pivot_1) ? rprobs[j + k] : 0, (rprobs[j + k] > pivot_1 && (tx)*kRmemEles + j + k < d)};
|
||||
}
|
||||
}
|
||||
|
||||
// check in rprobs_d
|
||||
for (uint32_t i = 0; i < ceil_div(real_rd_size, BLOCK_THREADS * VEC_SIZE); ++i) {
|
||||
rprobs_d.fill(0);
|
||||
if ((i * BLOCK_THREADS + tx) * VEC_SIZE + real_r_size < d) {
|
||||
rprobs_d.cast_load(global_rprobs_d + (i * BLOCK_THREADS + tx) * VEC_SIZE);
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (uint32_t j = 0; j < VEC_SIZE; ++j) {
|
||||
probs_gt_pivot_0[j] +=
|
||||
{(rprobs_d[j] > pivot_0) ? rprobs_d[j] : 0,
|
||||
(rprobs_d[j] > pivot_0 && (i * BLOCK_THREADS + tx) * VEC_SIZE + j + real_r_size < d)};
|
||||
probs_gt_pivot_1[j] +=
|
||||
{(rprobs_d[j] > pivot_1) ? rprobs_d[j] : 0,
|
||||
(rprobs_d[j] > pivot_1 && (i * BLOCK_THREADS + tx) * VEC_SIZE + j + real_r_size < d)};
|
||||
}
|
||||
}
|
||||
|
||||
aggregate_gt_pivot_0 = BlockReduce<ValueCount<float>, BLOCK_THREADS>(temp_storage.block_prim.reduce_value_count)
|
||||
.template Sum<VEC_SIZE>(probs_gt_pivot_0);
|
||||
if (tx == 0) {
|
||||
temp_storage.block_aggregate.pair = aggregate_gt_pivot_0;
|
||||
}
|
||||
__syncthreads();
|
||||
aggregate_gt_pivot_0 = temp_storage.block_aggregate.pair;
|
||||
|
||||
aggregate_gt_pivot_1 = BlockReduce<ValueCount<float>, BLOCK_THREADS>(temp_storage.block_prim.reduce_value_count)
|
||||
.template Sum<VEC_SIZE>(probs_gt_pivot_1);
|
||||
if (tx == 0) {
|
||||
temp_storage.block_aggregate.pair = aggregate_gt_pivot_1;
|
||||
}
|
||||
__syncthreads();
|
||||
aggregate_gt_pivot_1 = temp_storage.block_aggregate.pair;
|
||||
|
||||
if (aggregate_gt_pivot_0.count < k && aggregate_gt_pivot_0.value < p) {
|
||||
// case 1: pivot_0 accepted
|
||||
break;
|
||||
}
|
||||
if (aggregate_gt_pivot_1.count < k && aggregate_gt_pivot_1.value < p) {
|
||||
// case 2: pivot_0 rejected, pivot_1 accepted
|
||||
low = pivot_0;
|
||||
high = pivot_1;
|
||||
q = aggregate_gt_pivot_0.value;
|
||||
} else {
|
||||
// case 3: pivot_0 rejected, pivot_1 rejected
|
||||
low = pivot_1;
|
||||
q = aggregate_gt_pivot_1.value;
|
||||
}
|
||||
} while (low < high);
|
||||
__syncthreads();
|
||||
if (tx == 0) {
|
||||
output[bx] = sampled_id;
|
||||
}
|
||||
}
|
||||
} // namespace sampling
|
||||
} // namespace musa
|
||||
|
||||
template <typename T, typename IdType>
|
||||
musaError_t MusaTopKTopPSamplingFromProb(T* probs, IdType* top_k_arr, T* top_p_arr, IdType* output,
|
||||
IdType* indices, uint32_t batch_size, IdType top_k_val,
|
||||
T top_p_val, uint32_t d, bool deterministic,
|
||||
uint64_t philox_seed, uint64_t philox_offset,
|
||||
musaStream_t stream = 0) {
|
||||
const uint32_t vec_size = std::gcd(16 / sizeof(T), d);
|
||||
using namespace flashinfer;
|
||||
using namespace flashinfer::sampling;
|
||||
auto compute_capacity = GetCudaComputeCapability();
|
||||
|
||||
DISPATCH_COMPUTE_CAP_NUM_THREADS(compute_capacity, BLOCK_THREADS, {
|
||||
const uint32_t smem_size = sizeof(SamplingTempStorage<BLOCK_THREADS, SCAN_ALGO, REDUCE_ALGO>);
|
||||
dim3 nblks(batch_size);
|
||||
dim3 nthrs(BLOCK_THREADS);
|
||||
void* args[] = {
|
||||
&probs, &top_k_arr, &top_p_arr, &output, &indices, &top_k_val, &top_p_val, &d, &philox_seed, &philox_offset};
|
||||
// fall back to flashinfer implementation
|
||||
if (d < BLOCK_THREADS * kRmemEles) {
|
||||
DISPATCH_ALIGNED_VEC_SIZE(
|
||||
vec_size, VEC_SIZE, {DISPATCH_DETERMINISTIC(deterministic, DETERMINISTIC, {
|
||||
auto kernel = TopKTopPSamplingFromProbKernel<
|
||||
BLOCK_THREADS,
|
||||
SCAN_ALGO,
|
||||
REDUCE_ALGO,
|
||||
VEC_SIZE,
|
||||
DETERMINISTIC,
|
||||
T,
|
||||
IdType>;
|
||||
FLASHINFER_CUDA_CALL(musaFuncSetAttribute(kernel, musaFuncAttributeMaxDynamicSharedMemorySize, smem_size));
|
||||
FLASHINFER_CUDA_CALL(musaLaunchKernel((void*)kernel, nblks, nthrs, args, smem_size, stream));
|
||||
})});
|
||||
} else {
|
||||
DISPATCH_ALIGNED_VEC_SIZE(
|
||||
vec_size, VEC_SIZE, {DISPATCH_DETERMINISTIC(deterministic, DETERMINISTIC, {
|
||||
auto kernel = musa::sampling::TopKTopPSamplingFromProbKernel<
|
||||
BLOCK_THREADS,
|
||||
SCAN_ALGO,
|
||||
REDUCE_ALGO,
|
||||
VEC_SIZE,
|
||||
DETERMINISTIC,
|
||||
T,
|
||||
IdType>;
|
||||
FLASHINFER_CUDA_CALL(musaFuncSetAttribute(kernel, musaFuncAttributeMaxDynamicSharedMemorySize, smem_size));
|
||||
FLASHINFER_CUDA_CALL(musaLaunchKernel((void*)kernel, nblks, nthrs, args, smem_size, stream));
|
||||
})});
|
||||
}
|
||||
return musaSuccess;
|
||||
});
|
||||
}
|
||||
|
||||
void musa_top_k_top_p_sampling_from_probs(
|
||||
at::Tensor probs,
|
||||
at::Tensor output,
|
||||
std::optional<at::Tensor> maybe_indices,
|
||||
std::optional<at::Tensor> maybe_top_k_arr,
|
||||
double top_k_val,
|
||||
std::optional<at::Tensor> maybe_top_p_arr,
|
||||
double top_p_val,
|
||||
bool deterministic,
|
||||
std::optional<at::Generator> gen_) {
|
||||
CHECK_INPUT(probs);
|
||||
CHECK_INPUT(output);
|
||||
auto device = probs.device();
|
||||
CHECK_EQ(output.device(), device);
|
||||
CHECK_EQ(probs.dtype(), torch::kFloat);
|
||||
CHECK_DIM(2, probs); // probs: (batch_size, vocab_size)
|
||||
CHECK_DIM(1, output); // output: (batch_size)
|
||||
unsigned int batch_size = output.size(0);
|
||||
unsigned int vocab_size = probs.size(1);
|
||||
bool has_top_k_arr = maybe_top_k_arr.has_value();
|
||||
bool has_top_p_arr = maybe_top_p_arr.has_value();
|
||||
uint64_t philox_seed, philox_offset;
|
||||
auto gen = at::get_generator_or_default<at::MUSAGeneratorImpl>(gen_, at::musa::detail::getDefaultMUSAGenerator());
|
||||
std::lock_guard<std::mutex> lock(gen->mutex_);
|
||||
at::PhiloxMusaState rng_engine_inputs = gen->philox_musa_state(32 * batch_size);
|
||||
philox_seed = rng_engine_inputs.seed_.val;
|
||||
philox_offset = rng_engine_inputs.offset_.val;
|
||||
|
||||
const c10::musa::OptionalMUSAGuard device_guard(device);
|
||||
auto stream = at::musa::getCurrentMUSAStream();
|
||||
musaError_t status = MusaTopKTopPSamplingFromProb<float, int>(
|
||||
static_cast<float*>(probs.data_ptr()),
|
||||
has_top_k_arr ? static_cast<int*>(maybe_top_k_arr->data_ptr()) : nullptr,
|
||||
has_top_p_arr ? static_cast<float*>(maybe_top_p_arr->data_ptr()) : nullptr,
|
||||
static_cast<int*>(output.data_ptr()),
|
||||
maybe_indices.has_value() ? static_cast<int*>(maybe_indices->data_ptr()) : nullptr,
|
||||
batch_size,
|
||||
top_k_val,
|
||||
top_p_val,
|
||||
vocab_size,
|
||||
deterministic,
|
||||
philox_seed,
|
||||
philox_offset,
|
||||
stream);
|
||||
TORCH_CHECK(
|
||||
status == musaSuccess,
|
||||
"MusaTopKTopPSamplingFromProb failed with error code " + std::string(musaGetErrorString(status)));
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Adapted from
|
||||
* https://github.com/pytorch/pytorch/blob/v2.0.1/aten/src/ATen/Dispatch.h
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <torch/all.h>
|
||||
|
||||
#define MUSA_LDG(arg) __ldg(arg)
|
||||
|
||||
#define MUSA_DISPATCH_CASE_FLOATING_TYPES(...) \
|
||||
AT_DISPATCH_CASE(at::ScalarType::Float, __VA_ARGS__) \
|
||||
AT_DISPATCH_CASE(at::ScalarType::Half, __VA_ARGS__) \
|
||||
AT_DISPATCH_CASE(at::ScalarType::BFloat16, __VA_ARGS__)
|
||||
|
||||
#define MUSA_DISPATCH_FLOATING_TYPES(TYPE, NAME, ...) \
|
||||
AT_DISPATCH_SWITCH(TYPE, NAME, MUSA_DISPATCH_CASE_FLOATING_TYPES(__VA_ARGS__))
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace musa::dnn {
|
||||
|
||||
// cutlass integer_subbyte class
|
||||
template <int Bits, bool Signed = true>
|
||||
struct integer_subbyte {
|
||||
using Storage = uint8_t;
|
||||
|
||||
static_assert(Bits <= 8 * sizeof(Storage), "Require a subbyte of bits in integer_subbyte");
|
||||
|
||||
using xint_t = typename std::conditional<Signed, int, unsigned>::type;
|
||||
|
||||
static constexpr Storage bits_mask_ = Storage((1 << Bits) - 1);
|
||||
|
||||
static constexpr Storage sign_mask_ = Storage((Signed ? 1 : 0) << (Bits - 1));
|
||||
|
||||
Storage storage;
|
||||
|
||||
__host__ __device__ constexpr integer_subbyte() {}
|
||||
|
||||
__host__ __device__ constexpr integer_subbyte(int value)
|
||||
: storage(reinterpret_cast<Storage const&>(value) & bits_mask_) {}
|
||||
|
||||
__host__ __device__ constexpr integer_subbyte(unsigned value)
|
||||
: storage(reinterpret_cast<Storage const&>(value) & bits_mask_) {}
|
||||
};
|
||||
|
||||
} // namespace musa::dnn
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2026, Moore Threads Technology Co., Ltd("Moore Threads").
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ATen/ATen.h>
|
||||
#include <ATen/Tensor.h>
|
||||
#include <torch/torch.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
void batched_rotary_embedding_contiguous(
|
||||
torch::Tensor& positions,
|
||||
torch::Tensor& query,
|
||||
torch::Tensor& key,
|
||||
int64_t head_size,
|
||||
torch::Tensor& cos_sin_cache,
|
||||
bool is_neox,
|
||||
int64_t rot_dim,
|
||||
torch::Tensor& cos_sin_cache_offsets);
|
||||
|
||||
void rotary_embedding_contiguous(
|
||||
torch::Tensor& positions,
|
||||
torch::Tensor& query,
|
||||
torch::Tensor& key,
|
||||
int64_t head_size,
|
||||
torch::Tensor& cos_sin_cache,
|
||||
bool is_neox);
|
||||
|
||||
void fused_moe_gemv(
|
||||
torch::Tensor& A,
|
||||
torch::Tensor& B,
|
||||
torch::Tensor& C,
|
||||
const c10::optional<torch::Tensor>& A_scale,
|
||||
const c10::optional<torch::Tensor>& B_scale,
|
||||
torch::Tensor& topk_weights,
|
||||
torch::Tensor& topk_ids,
|
||||
bool mul_routed_weight,
|
||||
int64_t topk,
|
||||
bool use_int4_w4a16,
|
||||
bool use_swigelu);
|
||||
|
||||
void musa_fused_gemv(
|
||||
torch::Tensor& A,
|
||||
torch::Tensor& B,
|
||||
torch::Tensor& C,
|
||||
const c10::optional<torch::Tensor>& A_scale,
|
||||
const c10::optional<torch::Tensor>& B_scale,
|
||||
bool use_int4_w4a16,
|
||||
bool use_swigelu,
|
||||
bool use_rms_norm,
|
||||
const c10::optional<torch::Tensor>& gamma,
|
||||
double eps);
|
||||
|
||||
void fused_mul_add(torch::Tensor& output, torch::Tensor& self, torch::Tensor& bias, double scale);
|
||||
|
||||
void musa_top_k_top_p_sampling_from_probs(
|
||||
at::Tensor probs,
|
||||
at::Tensor output,
|
||||
std::optional<at::Tensor> maybe_indices,
|
||||
std::optional<at::Tensor> maybe_top_k_arr,
|
||||
double top_k_val,
|
||||
std::optional<at::Tensor> maybe_top_p_arr,
|
||||
double top_p_val,
|
||||
bool deterministic,
|
||||
std::optional<at::Generator> gen);
|
||||
@@ -27,6 +27,10 @@ limitations under the License.
|
||||
|
||||
#include "scalar_type.hpp"
|
||||
|
||||
#ifdef USE_MUSA
|
||||
#include "sgl_kernel_musa_ops.h"
|
||||
#endif
|
||||
|
||||
#define _CONCAT(A, B) A##B
|
||||
#define CONCAT(A, B) _CONCAT(A, B)
|
||||
|
||||
|
||||
@@ -111,6 +111,15 @@ from sgl_kernel.version import __version__
|
||||
if torch.version.hip is not None:
|
||||
from sgl_kernel.elementwise import gelu_quick
|
||||
|
||||
if hasattr(torch.version, "musa") and torch.version.musa is not None:
|
||||
from sgl_kernel.musa import (
|
||||
musa_batched_rotary_embedding_contiguous,
|
||||
musa_fused_gemv,
|
||||
musa_fused_moe_gemv,
|
||||
musa_fused_mul_add,
|
||||
musa_rotary_embedding_contiguous,
|
||||
)
|
||||
|
||||
|
||||
_DEBUG_EXPORT_NAMES = [
|
||||
"apply_shuffle_mul_sum",
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
from typing import Optional
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
def musa_batched_rotary_embedding_contiguous(
|
||||
positions: torch.Tensor,
|
||||
query: torch.Tensor,
|
||||
key: torch.Tensor,
|
||||
head_size: int,
|
||||
cos_sin_cache: torch.Tensor,
|
||||
is_neox: bool,
|
||||
rot_dim: int,
|
||||
cos_sin_cache_offsets: torch.Tensor,
|
||||
) -> None:
|
||||
return torch.ops.sgl_kernel.musa_batched_rotary_embedding_contiguous(
|
||||
positions,
|
||||
query,
|
||||
key,
|
||||
head_size,
|
||||
cos_sin_cache,
|
||||
is_neox,
|
||||
rot_dim,
|
||||
cos_sin_cache_offsets,
|
||||
)
|
||||
|
||||
|
||||
def musa_rotary_embedding_contiguous(
|
||||
positions: torch.Tensor,
|
||||
query: torch.Tensor,
|
||||
key: torch.Tensor,
|
||||
head_size: int,
|
||||
cos_sin_cache: torch.Tensor,
|
||||
is_neox: bool,
|
||||
) -> None:
|
||||
return torch.ops.sgl_kernel.musa_rotary_embedding_contiguous(
|
||||
positions,
|
||||
query,
|
||||
key,
|
||||
head_size,
|
||||
cos_sin_cache,
|
||||
is_neox,
|
||||
)
|
||||
|
||||
|
||||
def musa_fused_moe_gemv(
|
||||
A: torch.Tensor,
|
||||
B: torch.Tensor,
|
||||
C: torch.Tensor,
|
||||
A_scale,
|
||||
B_scale,
|
||||
topk_weights: torch.Tensor,
|
||||
topk_ids: torch.Tensor,
|
||||
mul_routed_weight: bool,
|
||||
topk: int,
|
||||
use_int4_w4a16: bool,
|
||||
use_swigelu: bool,
|
||||
) -> None:
|
||||
return torch.ops.sgl_kernel.musa_fused_moe_gemv(
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
A_scale,
|
||||
B_scale,
|
||||
topk_weights,
|
||||
topk_ids,
|
||||
mul_routed_weight,
|
||||
topk,
|
||||
use_int4_w4a16,
|
||||
use_swigelu,
|
||||
)
|
||||
|
||||
|
||||
def musa_fused_gemv(
|
||||
x: torch.Tensor,
|
||||
qweight: torch.Tensor,
|
||||
x_scales: Optional[torch.Tensor] = None,
|
||||
qweight_scales: Optional[torch.Tensor] = None,
|
||||
use_swigelu: bool = False,
|
||||
use_rms_norm: bool = False,
|
||||
gamma: Optional[torch.Tensor] = None,
|
||||
eps: float = 1e-6,
|
||||
):
|
||||
use_int4_w4a16 = False
|
||||
out_shape = x.shape[:-1] + (
|
||||
qweight.shape[0] if not use_swigelu else qweight.shape[0] // 2,
|
||||
)
|
||||
assert not (
|
||||
use_swigelu and use_rms_norm
|
||||
), "gemv only fused one activation (swigelu or rms_norm)!"
|
||||
|
||||
if use_rms_norm:
|
||||
if gamma is None:
|
||||
assert False, "rms_norm gamma is None!"
|
||||
|
||||
# fp8 grouped matmul
|
||||
if qweight.dtype == torch.float8_e4m3fn:
|
||||
assert qweight_scales is not None, "FP8 grouped matmul weight scales is None!"
|
||||
output = torch.empty(out_shape, device=x.device, dtype=torch.bfloat16)
|
||||
torch.ops.sgl_kernel.musa_fused_gemv(
|
||||
x,
|
||||
qweight,
|
||||
output,
|
||||
x_scales,
|
||||
qweight_scales,
|
||||
use_int4_w4a16,
|
||||
use_swigelu,
|
||||
use_rms_norm,
|
||||
gamma,
|
||||
eps,
|
||||
)
|
||||
return output
|
||||
# w4a16 gemv
|
||||
elif qweight_scales is not None:
|
||||
assert (
|
||||
x.dtype == torch.bfloat16 or x.dtype == torch.float16
|
||||
), "W4A16 gemv only support bfloat16 or float16!"
|
||||
use_int4_w4a16 = True
|
||||
out_shape = x.shape[:-1] + (
|
||||
qweight.shape[0] if not use_swigelu else qweight.shape[0] // 2,
|
||||
)
|
||||
output = torch.empty(out_shape, device=x.device, dtype=x.dtype)
|
||||
torch.ops.sgl_kernel.musa_fused_gemv(
|
||||
x,
|
||||
qweight,
|
||||
output,
|
||||
None,
|
||||
qweight_scales,
|
||||
use_int4_w4a16,
|
||||
use_swigelu,
|
||||
use_rms_norm,
|
||||
gamma,
|
||||
eps,
|
||||
)
|
||||
return output
|
||||
# general gemv
|
||||
else:
|
||||
output = torch.empty(out_shape, device=x.device, dtype=x.dtype)
|
||||
torch.ops.sgl_kernel.musa_fused_gemv(
|
||||
x,
|
||||
qweight,
|
||||
output,
|
||||
None,
|
||||
None,
|
||||
use_int4_w4a16,
|
||||
use_swigelu,
|
||||
use_rms_norm,
|
||||
gamma,
|
||||
eps,
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
def musa_fused_mul_add(
|
||||
self: torch.Tensor,
|
||||
bias: Optional[torch.Tensor],
|
||||
scale: Optional[float],
|
||||
accurate: bool = True,
|
||||
):
|
||||
# if accurate == False, then we call inplace op: bias += (self * scale)
|
||||
if not accurate:
|
||||
bias.add_(self, alpha=scale)
|
||||
return bias
|
||||
|
||||
# otherwise, we call custom outplace op, act: output = self * scale + bias
|
||||
output = torch.empty_like(self)
|
||||
torch.ops.sgl_kernel.musa_fused_mul_add(output, self, bias, scale)
|
||||
|
||||
return output
|
||||
@@ -81,6 +81,7 @@ sources = [
|
||||
"csrc/common_extension_musa.cc",
|
||||
"csrc/elementwise/activation.cu",
|
||||
"csrc/elementwise/concat_mla.cu",
|
||||
"csrc/elementwise/pos_enc.cu",
|
||||
"csrc/elementwise/fused_add_rms_norm_kernel.mu",
|
||||
"csrc/grammar/apply_token_bitmask_inplace_cuda.cu",
|
||||
"csrc/moe/moe_align_kernel.cu",
|
||||
@@ -107,6 +108,11 @@ sources = [
|
||||
"csrc/memory/weak_ref_tensor.cpp",
|
||||
str(_FLASHINFER_REPO.source_dir / "csrc/norm.cu"),
|
||||
str(_FLASHINFER_REPO.source_dir / "csrc/renorm.cu"),
|
||||
# XXX (MUSA): The following files contain MUSA-specific implementations.
|
||||
"csrc/musa/pos_encoding_contiguous.mu",
|
||||
"csrc/musa/moe_gemv_swiglu.mu",
|
||||
"csrc/musa/ternary.mu",
|
||||
"csrc/musa/top_k_top_p_sampling.mu",
|
||||
]
|
||||
|
||||
cxx_flags = ["force_mcc"]
|
||||
|
||||
Reference in New Issue
Block a user