Nemotron perf changes (#26733)
Co-authored-by: Brayden Zhong <brayden@radixark.ai>
This commit is contained in:
co-authored by
Brayden Zhong
parent
393d0e169e
commit
38ae22e08c
@@ -42,11 +42,16 @@ def _jit_activation_module(dtype: torch.dtype) -> Module:
|
|||||||
"run_activation_filtered",
|
"run_activation_filtered",
|
||||||
f"ActivationKernel<{args}>::run_activation_filtered",
|
f"ActivationKernel<{args}>::run_activation_filtered",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"run_unary_activation",
|
||||||
|
f"ActivationKernel<{args}>::run_unary_activation",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
SUPPORTED_ACTIVATIONS = {"silu", "gelu", "gelu_tanh"}
|
SUPPORTED_ACTIVATIONS = {"silu", "gelu", "gelu_tanh"}
|
||||||
|
SUPPORTED_UNARY_ACTIVATIONS = {"relu2"}
|
||||||
|
|
||||||
|
|
||||||
@register_custom_op(mutates_args=["out"])
|
@register_custom_op(mutates_args=["out"])
|
||||||
@@ -100,6 +105,42 @@ def run_activation(
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
@register_custom_op(mutates_args=["out"])
|
||||||
|
def _run_unary_activation_inplace(
|
||||||
|
op_name: str, input: torch.Tensor, out: torch.Tensor
|
||||||
|
) -> None:
|
||||||
|
last = input.shape[-1]
|
||||||
|
module = _jit_activation_module(input.dtype)
|
||||||
|
module.run_unary_activation(input.view(-1, last), out.view(-1, last), op_name)
|
||||||
|
|
||||||
|
|
||||||
|
def run_unary_activation(
|
||||||
|
op_name: str,
|
||||||
|
input: torch.Tensor,
|
||||||
|
out: Optional[torch.Tensor] = None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
"""Apply a standalone (non-gated) element-wise activation: ``out = act(input)``.
|
||||||
|
|
||||||
|
Unlike :func:`run_activation`, there is no gate/up split — ``input`` and
|
||||||
|
``out`` share the same shape.
|
||||||
|
"""
|
||||||
|
assert (
|
||||||
|
op_name in SUPPORTED_UNARY_ACTIVATIONS
|
||||||
|
), f"Unsupported unary activation: {op_name}"
|
||||||
|
if out is None:
|
||||||
|
out = torch.empty_like(input)
|
||||||
|
_run_unary_activation_inplace(op_name, input, out)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def relu2(
|
||||||
|
input: torch.Tensor,
|
||||||
|
out: Optional[torch.Tensor] = None,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
"""Squared ReLU: ``out = max(0, input) ** 2`` (element-wise)."""
|
||||||
|
return run_unary_activation("relu2", input, out)
|
||||||
|
|
||||||
|
|
||||||
def silu_and_mul(
|
def silu_and_mul(
|
||||||
input: torch.Tensor,
|
input: torch.Tensor,
|
||||||
out: Optional[torch.Tensor] = None,
|
out: Optional[torch.Tensor] = None,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from sgl_kernel import silu_and_mul as silu_and_mul_aot
|
|||||||
|
|
||||||
from sglang.jit_kernel.activation import gelu_and_mul as gelu_and_mul_jit
|
from sglang.jit_kernel.activation import gelu_and_mul as gelu_and_mul_jit
|
||||||
from sglang.jit_kernel.activation import gelu_tanh_and_mul as gelu_tanh_and_mul_jit
|
from sglang.jit_kernel.activation import gelu_tanh_and_mul as gelu_tanh_and_mul_jit
|
||||||
|
from sglang.jit_kernel.activation import relu2 as relu2_jit
|
||||||
from sglang.jit_kernel.activation import silu_and_mul as silu_and_mul_jit
|
from sglang.jit_kernel.activation import silu_and_mul as silu_and_mul_jit
|
||||||
from sglang.jit_kernel.benchmark import marker
|
from sglang.jit_kernel.benchmark import marker
|
||||||
from sglang.jit_kernel.benchmark.utils import create_random
|
from sglang.jit_kernel.benchmark.utils import create_random
|
||||||
@@ -87,6 +88,21 @@ def benchmark_filter(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@torch.compile
|
||||||
|
def relu2_torch(input: torch.Tensor) -> torch.Tensor:
|
||||||
|
return F.relu(input).pow(2)
|
||||||
|
|
||||||
|
|
||||||
|
@marker.parametrize("dim", [1024, 4096, 6144, 8192], [4096])
|
||||||
|
@marker.parametrize("batch_size", [2**x for x in range(0, 15)], [8, 512])
|
||||||
|
@marker.benchmark("impl", ["jit", "torch"])
|
||||||
|
def benchmark_unary(dim: int, batch_size: int, impl: str):
|
||||||
|
x = create_random(batch_size, dim)
|
||||||
|
fn = {"jit": relu2_jit, "torch": relu2_torch}[impl]
|
||||||
|
return marker.do_bench(fn, input_args=(x,))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
benchmark.run()
|
benchmark.run()
|
||||||
benchmark_filter.run()
|
benchmark_filter.run()
|
||||||
|
benchmark_unary.run()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ enum class ActivationKind : uint32_t {
|
|||||||
kSiLU,
|
kSiLU,
|
||||||
kGELU,
|
kGELU,
|
||||||
kGELUTanh,
|
kGELUTanh,
|
||||||
|
kReLU2,
|
||||||
};
|
};
|
||||||
|
|
||||||
template <ActivationKind kAct>
|
template <ActivationKind kAct>
|
||||||
@@ -33,6 +34,9 @@ SGL_DEVICE float apply_activation_f32(float x_f32) {
|
|||||||
constexpr auto kGeluTanhBeta = 0.7978845608028654f;
|
constexpr auto kGeluTanhBeta = 0.7978845608028654f;
|
||||||
const float cdf = 0.5f * (1.0f + tanhf(kGeluTanhBeta * (x_f32 + kGeluTanhAlpha * x_f32 * x_f32 * x_f32)));
|
const float cdf = 0.5f * (1.0f + tanhf(kGeluTanhBeta * (x_f32 + kGeluTanhAlpha * x_f32 * x_f32 * x_f32)));
|
||||||
return x_f32 * cdf;
|
return x_f32 * cdf;
|
||||||
|
} else if constexpr (kAct == ActivationKind::kReLU2) {
|
||||||
|
const float relu = x_f32 > 0.0f ? x_f32 : 0.0f;
|
||||||
|
return relu * relu;
|
||||||
} else {
|
} else {
|
||||||
static_assert(host::dependent_false_v<decltype(kAct)>, "unsupported activation kind");
|
static_assert(host::dependent_false_v<decltype(kAct)>, "unsupported activation kind");
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
@@ -81,6 +85,30 @@ __global__ void act_and_mul_kernel(const __grid_constant__ ActivationParams para
|
|||||||
PDLTriggerSecondary<kUsePDL>();
|
PDLTriggerSecondary<kUsePDL>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UnaryActivationParams {
|
||||||
|
const void* __restrict__ input;
|
||||||
|
void* __restrict__ out;
|
||||||
|
uint32_t num_vecs;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, ActivationKind kAct, bool kUsePDL>
|
||||||
|
__global__ void act_kernel(const __grid_constant__ UnaryActivationParams params) {
|
||||||
|
using namespace device;
|
||||||
|
constexpr auto kVecSize = kMaxVecBytes / sizeof(T);
|
||||||
|
using vec_t = AlignedVector<T, kMaxVecBytes / sizeof(T)>;
|
||||||
|
const auto vec_id = blockIdx.x * blockDim.x + threadIdx.x;
|
||||||
|
if (vec_id >= params.num_vecs) return;
|
||||||
|
PDLWaitPrimary<kUsePDL>();
|
||||||
|
const auto in = device::load_as<vec_t>(params.input, vec_id);
|
||||||
|
vec_t out;
|
||||||
|
#pragma unroll
|
||||||
|
for (int i = 0; i < kVecSize; ++i) {
|
||||||
|
out[i] = device::cast<T>(apply_activation_f32<kAct>(device::cast<fp32_t>(in[i])));
|
||||||
|
}
|
||||||
|
device::store_as<vec_t>(params.out, out, vec_id);
|
||||||
|
PDLTriggerSecondary<kUsePDL>();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, bool kUsePDL>
|
template <typename T, bool kUsePDL>
|
||||||
struct ActivationKernel {
|
struct ActivationKernel {
|
||||||
static constexpr auto kVecSize = device::kMaxVecBytes / sizeof(T);
|
static constexpr auto kVecSize = device::kMaxVecBytes / sizeof(T);
|
||||||
@@ -174,6 +202,50 @@ struct ActivationKernel {
|
|||||||
RuntimeCheck(expert_step >= 1, "expert_step must be positive");
|
RuntimeCheck(expert_step >= 1, "expert_step must be positive");
|
||||||
launch(input, out, type, static_cast<const int32_t*>(expert_ids.data_ptr()), static_cast<uint32_t>(expert_step));
|
launch(input, out, type, static_cast<const int32_t*>(expert_ids.data_ptr()), static_cast<uint32_t>(expert_step));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <ActivationKind kAct>
|
||||||
|
static constexpr auto unary_kernel = act_kernel<T, kAct, kUsePDL>;
|
||||||
|
|
||||||
|
static auto select_unary_kernel(const std::string& type)
|
||||||
|
-> decltype(ActivationKernel::template unary_kernel<ActivationKind::kReLU2>) {
|
||||||
|
using namespace host;
|
||||||
|
if (type == "relu2") {
|
||||||
|
return ActivationKernel::template unary_kernel<ActivationKind::kReLU2>;
|
||||||
|
} else {
|
||||||
|
Panic("unsupported unary activation type: ", type);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_unary_activation(const tvm::ffi::TensorView input, const tvm::ffi::TensorView out, std::string type) {
|
||||||
|
using namespace host;
|
||||||
|
|
||||||
|
auto N = SymbolicSize{"num_tokens"};
|
||||||
|
auto D = SymbolicSize{"hidden"};
|
||||||
|
auto device_ = SymbolicDevice{};
|
||||||
|
device_.set_options<kDLCUDA>();
|
||||||
|
|
||||||
|
TensorMatcher({N, D}) //
|
||||||
|
.with_dtype<T>()
|
||||||
|
.with_device(device_)
|
||||||
|
.verify(out)
|
||||||
|
.verify(input);
|
||||||
|
|
||||||
|
const auto num_elems = static_cast<int64_t>(N.unwrap()) * D.unwrap();
|
||||||
|
const auto device = device_.unwrap();
|
||||||
|
if (num_elems == 0) return;
|
||||||
|
RuntimeCheck(num_elems % kVecSize == 0, "num elements must be divisible by vector size");
|
||||||
|
const auto num_vecs = num_elems / kVecSize;
|
||||||
|
RuntimeCheck(num_vecs <= std::numeric_limits<uint32_t>::max(), "too many items for 32-bit indexing");
|
||||||
|
const auto num_blocks = div_ceil(static_cast<uint32_t>(num_vecs), kBlockSize);
|
||||||
|
const auto params = UnaryActivationParams{
|
||||||
|
.input = input.data_ptr(),
|
||||||
|
.out = out.data_ptr(),
|
||||||
|
.num_vecs = static_cast<uint32_t>(num_vecs),
|
||||||
|
};
|
||||||
|
const auto kernel = select_unary_kernel(type);
|
||||||
|
LaunchKernel(num_blocks, kBlockSize, device).enable_pdl(kUsePDL)(kernel, params);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import pytest
|
|||||||
import torch
|
import torch
|
||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
|
|
||||||
from sglang.jit_kernel.activation import SUPPORTED_ACTIVATIONS, run_activation
|
from sglang.jit_kernel.activation import (
|
||||||
|
SUPPORTED_ACTIVATIONS,
|
||||||
|
relu2,
|
||||||
|
run_activation,
|
||||||
|
)
|
||||||
from sglang.jit_kernel.utils import get_ci_test_range
|
from sglang.jit_kernel.utils import get_ci_test_range
|
||||||
from sglang.test.ci.ci_register import register_cuda_ci
|
from sglang.test.ci.ci_register import register_cuda_ci
|
||||||
|
|
||||||
@@ -155,5 +159,47 @@ def test_activation_filter_expert_none_skipped(op_name: str) -> None:
|
|||||||
torch.testing.assert_close(out_filtered, out_unfiltered, atol=0.0, rtol=0.0)
|
torch.testing.assert_close(out_filtered, out_unfiltered, atol=0.0, rtol=0.0)
|
||||||
|
|
||||||
|
|
||||||
|
UNARY_SHAPES = get_ci_test_range(
|
||||||
|
full_range=[
|
||||||
|
(7, 16),
|
||||||
|
(83, 1024),
|
||||||
|
(3, 5, 16),
|
||||||
|
(2, 3, 512),
|
||||||
|
(1, 17, 4096),
|
||||||
|
*[(2**x, 2048) for x in range(0, 15, 2)],
|
||||||
|
],
|
||||||
|
ci_range=[(7, 16), (2, 3, 512)],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("dtype", DTYPES)
|
||||||
|
@pytest.mark.parametrize("shape", UNARY_SHAPES)
|
||||||
|
def test_relu2_correctness(dtype: torch.dtype, shape: tuple[int, ...]) -> None:
|
||||||
|
x = torch.randn(shape, dtype=dtype, device="cuda")
|
||||||
|
out = relu2(x)
|
||||||
|
expected = F.relu(x.float()).pow(2).to(dtype=dtype)
|
||||||
|
atol, rtol = _tolerances(dtype)
|
||||||
|
torch.testing.assert_close(out, expected, atol=atol, rtol=rtol)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("dtype", DTYPES)
|
||||||
|
@pytest.mark.parametrize("shape", UNARY_SHAPES)
|
||||||
|
def test_relu2_out_param(dtype: torch.dtype, shape: tuple[int, ...]) -> None:
|
||||||
|
x = torch.randn(shape, dtype=dtype, device="cuda")
|
||||||
|
out = torch.empty(shape, dtype=dtype, device="cuda")
|
||||||
|
result = relu2(x, out)
|
||||||
|
assert result is out
|
||||||
|
expected = F.relu(x.float()).pow(2).to(dtype=dtype)
|
||||||
|
atol, rtol = _tolerances(dtype)
|
||||||
|
torch.testing.assert_close(out, expected, atol=atol, rtol=rtol)
|
||||||
|
|
||||||
|
|
||||||
|
def test_relu2_negative_inputs_zeroed() -> None:
|
||||||
|
"""All-negative input must produce an all-zero output."""
|
||||||
|
x = -torch.rand((64, 512), dtype=torch.bfloat16, device="cuda") - 1e-3
|
||||||
|
out = relu2(x)
|
||||||
|
assert torch.count_nonzero(out) == 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(pytest.main([__file__, "-v", "-s"]))
|
sys.exit(pytest.main([__file__, "-v", "-s"]))
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ logger = logging.getLogger(__name__)
|
|||||||
def apply_nemotron_h_defaults(server_args: "ServerArgs", model_arch: str) -> None:
|
def apply_nemotron_h_defaults(server_args: "ServerArgs", model_arch: str) -> None:
|
||||||
"""Apply NemotronH model-specific server arg defaults and constraints."""
|
"""Apply NemotronH model-specific server arg defaults and constraints."""
|
||||||
model_config = server_args.get_model_config()
|
model_config = server_args.get_model_config()
|
||||||
if model_config.quantization in [
|
is_modelopt = model_config.quantization in [
|
||||||
"modelopt",
|
"modelopt",
|
||||||
"modelopt_fp8",
|
"modelopt_fp8",
|
||||||
"modelopt_fp4",
|
"modelopt_fp4",
|
||||||
"modelopt_mixed",
|
"modelopt_mixed",
|
||||||
]:
|
]
|
||||||
|
if is_modelopt:
|
||||||
assert model_config.hf_config.mlp_hidden_act == "relu2"
|
assert model_config.hf_config.mlp_hidden_act == "relu2"
|
||||||
if model_config.quantization == "modelopt":
|
if model_config.quantization == "modelopt":
|
||||||
quant_algo = model_config.hf_config.quantization_config["quant_algo"]
|
quant_algo = model_config.hf_config.quantization_config["quant_algo"]
|
||||||
@@ -29,28 +30,30 @@ def apply_nemotron_h_defaults(server_args: "ServerArgs", model_arch: str) -> Non
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
server_args.quantization = model_config.quantization
|
server_args.quantization = model_config.quantization
|
||||||
if server_args.moe_runner_backend == "auto":
|
|
||||||
if is_sm100_supported() and server_args.moe_a2a_backend == "none":
|
if (is_modelopt or model_config.quantization is None) and (
|
||||||
server_args.moe_runner_backend = "flashinfer_trtllm"
|
server_args.moe_runner_backend == "auto"
|
||||||
logger.info(
|
):
|
||||||
"Use flashinfer_trtllm as MoE runner backend on sm100 for "
|
if is_sm100_supported() and server_args.moe_a2a_backend == "none":
|
||||||
f"{model_arch}"
|
server_args.moe_runner_backend = "flashinfer_trtllm"
|
||||||
)
|
logger.info(
|
||||||
elif (
|
f"Use flashinfer_trtllm as MoE runner backend on sm100 for {model_arch}"
|
||||||
(
|
)
|
||||||
model_config.quantization in ("modelopt_fp4", "modelopt_mixed")
|
elif (
|
||||||
or server_args.quantization == "modelopt_fp4"
|
(
|
||||||
)
|
model_config.quantization in ("modelopt_fp4", "modelopt_mixed")
|
||||||
and is_cuda()
|
or server_args.quantization == "modelopt_fp4"
|
||||||
and (8, 0) <= get_device_capability() < (10, 0)
|
)
|
||||||
):
|
and is_cuda()
|
||||||
server_args.moe_runner_backend = "marlin"
|
and (8, 0) <= get_device_capability() < (10, 0)
|
||||||
logger.info(
|
):
|
||||||
"Use marlin as MoE runner backend on SM80-SM90 for "
|
server_args.moe_runner_backend = "marlin"
|
||||||
f"{model_arch} {model_config.quantization}"
|
logger.info(
|
||||||
)
|
"Use marlin as MoE runner backend on SM80-SM90 for "
|
||||||
else:
|
f"{model_arch} {model_config.quantization}"
|
||||||
server_args.moe_runner_backend = "flashinfer_cutlass"
|
)
|
||||||
|
else:
|
||||||
|
server_args.moe_runner_backend = "flashinfer_cutlass"
|
||||||
|
|
||||||
server_args._handle_mamba_radix_cache(
|
server_args._handle_mamba_radix_cache(
|
||||||
model_arch=model_arch,
|
model_arch=model_arch,
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ if _is_cuda:
|
|||||||
from sglang.jit_kernel.activation import (
|
from sglang.jit_kernel.activation import (
|
||||||
gelu_and_mul,
|
gelu_and_mul,
|
||||||
gelu_tanh_and_mul,
|
gelu_tanh_and_mul,
|
||||||
|
relu2,
|
||||||
silu_and_mul,
|
silu_and_mul,
|
||||||
)
|
)
|
||||||
elif _is_xpu:
|
elif _is_xpu:
|
||||||
@@ -190,16 +191,19 @@ class NewGELU(MultiPlatformOp):
|
|||||||
return self.forward_native(x)
|
return self.forward_native(x)
|
||||||
|
|
||||||
|
|
||||||
class ReLU2(nn.Module):
|
class ReLU2(MultiPlatformOp):
|
||||||
"""
|
"""
|
||||||
Applies the squared Rectified Linear Unit function.
|
Applies the squared Rectified Linear Unit function.
|
||||||
y = max(0, x)^2
|
y = max(0, x)^2
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
def forward_native(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
x = F.relu(x)
|
x = F.relu(x)
|
||||||
return x * x
|
return x * x
|
||||||
|
|
||||||
|
def forward_cuda(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
|
return relu2(x)
|
||||||
|
|
||||||
|
|
||||||
class QuickGELU(MultiPlatformOp):
|
class QuickGELU(MultiPlatformOp):
|
||||||
def forward_native(self, x: torch.Tensor) -> torch.Tensor:
|
def forward_native(self, x: torch.Tensor) -> torch.Tensor:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import triton
|
|||||||
import triton.language as tl
|
import triton.language as tl
|
||||||
from einops import rearrange
|
from einops import rearrange
|
||||||
|
|
||||||
|
from sglang.jit_kernel.utils import is_arch_support_pdl
|
||||||
from sglang.srt.server_args import get_global_server_args
|
from sglang.srt.server_args import get_global_server_args
|
||||||
from sglang.srt.utils import (
|
from sglang.srt.utils import (
|
||||||
cdiv,
|
cdiv,
|
||||||
@@ -86,7 +87,11 @@ def _layer_norm_fwd_1pass_kernel(
|
|||||||
NORM_BEFORE_GATE: tl.constexpr,
|
NORM_BEFORE_GATE: tl.constexpr,
|
||||||
IS_RMS_NORM: tl.constexpr,
|
IS_RMS_NORM: tl.constexpr,
|
||||||
ACTIVATION: tl.constexpr,
|
ACTIVATION: tl.constexpr,
|
||||||
|
USE_GDC: tl.constexpr = False,
|
||||||
):
|
):
|
||||||
|
if USE_GDC:
|
||||||
|
tl.extra.cuda.gdc_wait()
|
||||||
|
|
||||||
# Map the program id to the starting row of X and Y it should compute.
|
# Map the program id to the starting row of X and Y it should compute.
|
||||||
row_start = tl.program_id(0) * ROWS_PER_BLOCK
|
row_start = tl.program_id(0) * ROWS_PER_BLOCK
|
||||||
group = tl.program_id(1)
|
group = tl.program_id(1)
|
||||||
@@ -168,6 +173,9 @@ def _layer_norm_fwd_1pass_kernel(
|
|||||||
# Write output
|
# Write output
|
||||||
tl.store(Y_base, y, mask=mask)
|
tl.store(Y_base, y, mask=mask)
|
||||||
|
|
||||||
|
if USE_GDC:
|
||||||
|
tl.extra.cuda.gdc_launch_dependents()
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
def _get_sm_count(device: torch.device) -> int:
|
def _get_sm_count(device: torch.device) -> int:
|
||||||
@@ -243,6 +251,7 @@ def _layer_norm_fwd(
|
|||||||
rows_per_block = calc_rows_per_block(M, x.device)
|
rows_per_block = calc_rows_per_block(M, x.device)
|
||||||
# Update grid to use rows_per_block
|
# Update grid to use rows_per_block
|
||||||
grid = (cdiv(M, rows_per_block), ngroups)
|
grid = (cdiv(M, rows_per_block), ngroups)
|
||||||
|
pdl_kwargs = {"USE_GDC": True, "launch_pdl": True} if is_arch_support_pdl() else {}
|
||||||
with device_context(x.device):
|
with device_context(x.device):
|
||||||
_layer_norm_fwd_1pass_kernel[grid](
|
_layer_norm_fwd_1pass_kernel[grid](
|
||||||
x,
|
x,
|
||||||
@@ -266,6 +275,7 @@ def _layer_norm_fwd(
|
|||||||
IS_RMS_NORM=is_rms_norm,
|
IS_RMS_NORM=is_rms_norm,
|
||||||
num_warps=num_warps,
|
num_warps=num_warps,
|
||||||
ACTIVATION=activation,
|
ACTIVATION=activation,
|
||||||
|
**pdl_kwargs,
|
||||||
)
|
)
|
||||||
return out, mean, rstd
|
return out, mean, rstd
|
||||||
|
|
||||||
|
|||||||
@@ -710,7 +710,7 @@ class Mamba2AttnBackend(MambaAttnBackendBase):
|
|||||||
self,
|
self,
|
||||||
mixer: MambaMixer2,
|
mixer: MambaMixer2,
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
output: torch.Tensor,
|
output: Optional[torch.Tensor],
|
||||||
layer_id: int,
|
layer_id: int,
|
||||||
forward_batch: ForwardBatch,
|
forward_batch: ForwardBatch,
|
||||||
mup_vector: Optional[torch.Tensor] = None,
|
mup_vector: Optional[torch.Tensor] = None,
|
||||||
@@ -718,7 +718,7 @@ class Mamba2AttnBackend(MambaAttnBackendBase):
|
|||||||
):
|
):
|
||||||
assert isinstance(self.forward_metadata, Mamba2Metadata)
|
assert isinstance(self.forward_metadata, Mamba2Metadata)
|
||||||
layer_cache = self.req_to_token_pool.mamba2_layer_cache(layer_id)
|
layer_cache = self.req_to_token_pool.mamba2_layer_cache(layer_id)
|
||||||
intermediate_states = mixer.forward(
|
mixer_out, intermediate_states = mixer.forward(
|
||||||
hidden_states=hidden_states,
|
hidden_states=hidden_states,
|
||||||
output=output,
|
output=output,
|
||||||
layer_cache=layer_cache,
|
layer_cache=layer_cache,
|
||||||
@@ -752,6 +752,8 @@ class Mamba2AttnBackend(MambaAttnBackendBase):
|
|||||||
num_decodes,
|
num_decodes,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return mixer_out
|
||||||
|
|
||||||
def forward_decode(self, *args, **kwargs):
|
def forward_decode(self, *args, **kwargs):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Mamba2AttnBackend's forward is called directly instead of through HybridLinearAttnBackend, as it supports mixed prefill and decode"
|
"Mamba2AttnBackend's forward is called directly instead of through HybridLinearAttnBackend, as it supports mixed prefill and decode"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import torch
|
|||||||
import triton
|
import triton
|
||||||
import triton.language as tl
|
import triton.language as tl
|
||||||
|
|
||||||
|
from sglang.jit_kernel.utils import is_arch_support_pdl
|
||||||
|
|
||||||
PAD_SLOT_ID = -1
|
PAD_SLOT_ID = -1
|
||||||
|
|
||||||
|
|
||||||
@@ -629,8 +631,12 @@ def _causal_conv1d_update_kernel(
|
|||||||
BLOCK_N: tl.constexpr,
|
BLOCK_N: tl.constexpr,
|
||||||
SAVE_INTERMEDIATE: tl.constexpr,
|
SAVE_INTERMEDIATE: tl.constexpr,
|
||||||
HAS_EAGLE_TREE_CUSTOM_ATTN_MASK: tl.constexpr,
|
HAS_EAGLE_TREE_CUSTOM_ATTN_MASK: tl.constexpr,
|
||||||
|
USE_GDC: tl.constexpr = False,
|
||||||
):
|
):
|
||||||
# ruff: noqa: E501
|
# ruff: noqa: E501
|
||||||
|
if USE_GDC:
|
||||||
|
tl.extra.cuda.gdc_wait()
|
||||||
|
|
||||||
idx_seq = tl.program_id(0)
|
idx_seq = tl.program_id(0)
|
||||||
if idx_seq >= batch:
|
if idx_seq >= batch:
|
||||||
return
|
return
|
||||||
@@ -978,6 +984,9 @@ def _causal_conv1d_update_kernel(
|
|||||||
mask=mask_retrieve,
|
mask=mask_retrieve,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if USE_GDC:
|
||||||
|
tl.extra.cuda.gdc_launch_dependents()
|
||||||
|
|
||||||
|
|
||||||
def causal_conv1d_update(
|
def causal_conv1d_update(
|
||||||
x: torch.Tensor,
|
x: torch.Tensor,
|
||||||
@@ -1124,6 +1133,8 @@ def causal_conv1d_update(
|
|||||||
else:
|
else:
|
||||||
stride_retrieve_parent_token_seq = stride_retrieve_parent_token_token = 0
|
stride_retrieve_parent_token_seq = stride_retrieve_parent_token_token = 0
|
||||||
|
|
||||||
|
pdl_kwargs = {"USE_GDC": True, "launch_pdl": True} if is_arch_support_pdl() else {}
|
||||||
|
|
||||||
_causal_conv1d_update_kernel[grid](
|
_causal_conv1d_update_kernel[grid](
|
||||||
# Pointers to matrices
|
# Pointers to matrices
|
||||||
x,
|
x,
|
||||||
@@ -1183,6 +1194,7 @@ def causal_conv1d_update(
|
|||||||
BLOCK_N=256,
|
BLOCK_N=256,
|
||||||
SAVE_INTERMEDIATE=intermediate_conv_window is not None,
|
SAVE_INTERMEDIATE=intermediate_conv_window is not None,
|
||||||
HAS_EAGLE_TREE_CUSTOM_ATTN_MASK=retrieve_next_token is not None,
|
HAS_EAGLE_TREE_CUSTOM_ATTN_MASK=retrieve_next_token is not None,
|
||||||
|
**pdl_kwargs,
|
||||||
)
|
)
|
||||||
if unsqueeze:
|
if unsqueeze:
|
||||||
out = out.squeeze(-1)
|
out = out.squeeze(-1)
|
||||||
|
|||||||
@@ -408,13 +408,17 @@ class MambaMixer2(torch.nn.Module):
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
output: torch.Tensor,
|
output: Optional[torch.Tensor] = None,
|
||||||
layer_cache: MambaPool.State,
|
layer_cache: MambaPool.State,
|
||||||
metadata: Mamba2Metadata,
|
metadata: Mamba2Metadata,
|
||||||
forward_batch: ForwardBatch,
|
forward_batch: ForwardBatch,
|
||||||
mup_vector: Optional[torch.Tensor] = None,
|
mup_vector: Optional[torch.Tensor] = None,
|
||||||
use_triton_causal_conv: bool = False,
|
use_triton_causal_conv: bool = False,
|
||||||
):
|
):
|
||||||
|
# Returns the projected result. When `output` is given it is also
|
||||||
|
# written into that buffer (required by the cuda-graph split ops, which
|
||||||
|
# need a stable buffer); otherwise the caller uses the return value and
|
||||||
|
# avoids a copy.
|
||||||
# metadata contains metadata necessary for the mamba2 triton
|
# metadata contains metadata necessary for the mamba2 triton
|
||||||
# kernels to operate in continuous batching and in chunked prefill
|
# kernels to operate in continuous batching and in chunked prefill
|
||||||
# modes; they are computed at top-level model forward since they
|
# modes; they are computed at top-level model forward since they
|
||||||
@@ -718,9 +722,11 @@ class MambaMixer2(torch.nn.Module):
|
|||||||
hidden_states = self.norm(preallocated_ssm_out, gate[:num_actual_tokens])
|
hidden_states = self.norm(preallocated_ssm_out, gate[:num_actual_tokens])
|
||||||
|
|
||||||
# 5. Final linear projection
|
# 5. Final linear projection
|
||||||
output[:num_actual_tokens], _ = self.out_proj(hidden_states)
|
mixer_out, _ = self.out_proj(hidden_states)
|
||||||
|
if output is not None:
|
||||||
|
output[:num_actual_tokens].copy_(mixer_out)
|
||||||
|
|
||||||
return intermediate_states
|
return mixer_out, intermediate_states
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mamba_type(self) -> str:
|
def mamba_type(self) -> str:
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class Mixer2RMSNormGated(MultiPlatformOp):
|
|||||||
# Keep gate in float32 for numerical stability during silu
|
# Keep gate in float32 for numerical stability during silu
|
||||||
return x * torch.nn.functional.silu(gate.to(torch.float32)).to(input_dtype)
|
return x * torch.nn.functional.silu(gate.to(torch.float32)).to(input_dtype)
|
||||||
|
|
||||||
if ((self.n_groups % self.tp_size) != 0) or self.n_groups != 1:
|
if (self.n_groups % self.tp_size) != 0:
|
||||||
return self.forward_native(x, gate)
|
return self.forward_native(x, gate)
|
||||||
|
|
||||||
return rms_norm_gated(
|
return rms_norm_gated(
|
||||||
@@ -115,6 +115,7 @@ class Mixer2RMSNormGated(MultiPlatformOp):
|
|||||||
bias=None,
|
bias=None,
|
||||||
z=gate,
|
z=gate,
|
||||||
eps=self.variance_epsilon,
|
eps=self.variance_epsilon,
|
||||||
|
group_size=self.group_size,
|
||||||
norm_before_gate=False,
|
norm_before_gate=False,
|
||||||
is_rms_norm=True,
|
is_rms_norm=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import triton
|
|||||||
import triton.language as tl
|
import triton.language as tl
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
|
from sglang.jit_kernel.utils import is_arch_support_pdl
|
||||||
|
|
||||||
PAD_SLOT_ID = -1
|
PAD_SLOT_ID = -1
|
||||||
|
|
||||||
TRITON3 = version.parse(triton.__version__) >= version.parse("3.0.0")
|
TRITON3 = version.parse(triton.__version__) >= version.parse("3.0.0")
|
||||||
@@ -141,7 +143,11 @@ def _selective_scan_update_kernel(
|
|||||||
HAS_EAGLE_TREE_CUSTOM_ATTN_MASK: tl.constexpr,
|
HAS_EAGLE_TREE_CUSTOM_ATTN_MASK: tl.constexpr,
|
||||||
HAS_INTERMEDIATE_STATE_INDICES: tl.constexpr,
|
HAS_INTERMEDIATE_STATE_INDICES: tl.constexpr,
|
||||||
BLOCK_SIZE_DSTATE: tl.constexpr,
|
BLOCK_SIZE_DSTATE: tl.constexpr,
|
||||||
|
USE_GDC: tl.constexpr = False,
|
||||||
):
|
):
|
||||||
|
if USE_GDC:
|
||||||
|
tl.extra.cuda.gdc_wait()
|
||||||
|
|
||||||
pid_m = tl.program_id(axis=0)
|
pid_m = tl.program_id(axis=0)
|
||||||
pid_b = tl.program_id(axis=1)
|
pid_b = tl.program_id(axis=1)
|
||||||
pid_h = tl.program_id(axis=2)
|
pid_h = tl.program_id(axis=2)
|
||||||
@@ -296,6 +302,9 @@ def _selective_scan_update_kernel(
|
|||||||
if not DISABLE_STATE_UPDATE:
|
if not DISABLE_STATE_UPDATE:
|
||||||
tl.store(state_ptrs, state.to(state_ptrs.dtype.element_ty), mask=mask)
|
tl.store(state_ptrs, state.to(state_ptrs.dtype.element_ty), mask=mask)
|
||||||
|
|
||||||
|
if USE_GDC:
|
||||||
|
tl.extra.cuda.gdc_launch_dependents()
|
||||||
|
|
||||||
|
|
||||||
def selective_state_update(
|
def selective_state_update(
|
||||||
state,
|
state,
|
||||||
@@ -427,6 +436,8 @@ def selective_state_update(
|
|||||||
else (0, 0)
|
else (0, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
pdl_kwargs = {"USE_GDC": True, "launch_pdl": True} if is_arch_support_pdl() else {}
|
||||||
|
|
||||||
with torch.get_device_module(x.device).device(x.device.index):
|
with torch.get_device_module(x.device).device(x.device.index):
|
||||||
_selective_scan_update_kernel[grid](
|
_selective_scan_update_kernel[grid](
|
||||||
state,
|
state,
|
||||||
@@ -491,4 +502,5 @@ def selective_state_update(
|
|||||||
BLOCK_SIZE_M,
|
BLOCK_SIZE_M,
|
||||||
DISABLE_STATE_UPDATE=disable_state_update,
|
DISABLE_STATE_UPDATE=disable_state_update,
|
||||||
num_warps=num_warps,
|
num_warps=num_warps,
|
||||||
|
**pdl_kwargs,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1145,6 +1145,12 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
|
|||||||
activation_type=activation,
|
activation_type=activation,
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
if (
|
||||||
|
not layer.should_fuse_routed_scaling_factor_in_topk
|
||||||
|
and self.moe_runner_config.routed_scaling_factor is not None
|
||||||
|
):
|
||||||
|
output.mul_(self.moe_runner_config.routed_scaling_factor)
|
||||||
|
|
||||||
return StandardCombineInput(hidden_states=output)
|
return StandardCombineInput(hidden_states=output)
|
||||||
|
|
||||||
quant_info = TritonMoeQuantInfo(
|
quant_info = TritonMoeQuantInfo(
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, MultiPlatformOp):
|
|||||||
self._cache_permute_indices,
|
self._cache_permute_indices,
|
||||||
layer.w13_weight.data[i].view(torch.uint8),
|
layer.w13_weight.data[i].view(torch.uint8),
|
||||||
epilogue_tile_m,
|
epilogue_tile_m,
|
||||||
|
is_gated_act_gemm=layer.moe_runner_config.is_gated,
|
||||||
)
|
)
|
||||||
tmp_weights1 = (
|
tmp_weights1 = (
|
||||||
layer.w13_weight.data[i]
|
layer.w13_weight.data[i]
|
||||||
@@ -509,6 +510,13 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, MultiPlatformOp):
|
|||||||
else ActivationType.Swiglu
|
else ActivationType.Swiglu
|
||||||
),
|
),
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
|
if (
|
||||||
|
not layer.should_fuse_routed_scaling_factor_in_topk
|
||||||
|
and moe_runner_config.routed_scaling_factor is not None
|
||||||
|
):
|
||||||
|
output.mul_(moe_runner_config.routed_scaling_factor)
|
||||||
|
|
||||||
return StandardCombineInput(hidden_states=output)
|
return StandardCombineInput(hidden_states=output)
|
||||||
elif self.use_flashinfer_trtllm_moe:
|
elif self.use_flashinfer_trtllm_moe:
|
||||||
from sglang.srt.layers.moe.moe_runner.flashinfer_trtllm import (
|
from sglang.srt.layers.moe.moe_runner.flashinfer_trtllm import (
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ class NemotronHMoE(nn.Module):
|
|||||||
config.hidden_size,
|
config.hidden_size,
|
||||||
config.n_routed_experts,
|
config.n_routed_experts,
|
||||||
bias=False,
|
bias=False,
|
||||||
params_dtype=torch.float32,
|
|
||||||
quant_config=None,
|
quant_config=None,
|
||||||
prefix=f"{prefix}.gate",
|
prefix=f"{prefix}.gate",
|
||||||
)
|
)
|
||||||
@@ -172,16 +171,6 @@ class NemotronHMoE(nn.Module):
|
|||||||
torch.empty(config.n_routed_experts, dtype=torch.float32)
|
torch.empty(config.n_routed_experts, dtype=torch.float32)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.topk = TopK(
|
|
||||||
top_k=config.num_experts_per_tok,
|
|
||||||
use_grouped_topk=True,
|
|
||||||
topk_group=config.topk_group,
|
|
||||||
num_expert_group=config.n_group,
|
|
||||||
renormalize=config.norm_topk_prob,
|
|
||||||
scoring_func="sigmoid",
|
|
||||||
correction_bias=self.gate.e_score_correction_bias,
|
|
||||||
routed_scaling_factor=1.0,
|
|
||||||
)
|
|
||||||
self.experts = get_moe_impl_class(quant_config)(
|
self.experts = get_moe_impl_class(quant_config)(
|
||||||
num_experts=config.n_routed_experts
|
num_experts=config.n_routed_experts
|
||||||
+ get_global_server_args().ep_num_redundant_experts,
|
+ get_global_server_args().ep_num_redundant_experts,
|
||||||
@@ -195,6 +184,18 @@ class NemotronHMoE(nn.Module):
|
|||||||
layer_id=layer_idx,
|
layer_id=layer_idx,
|
||||||
is_gated=False,
|
is_gated=False,
|
||||||
routing_method_type=RoutingMethodType.DeepSeekV3,
|
routing_method_type=RoutingMethodType.DeepSeekV3,
|
||||||
|
routed_scaling_factor=self.routed_scaling_factor,
|
||||||
|
)
|
||||||
|
self.topk = TopK(
|
||||||
|
top_k=config.num_experts_per_tok,
|
||||||
|
use_grouped_topk=True,
|
||||||
|
topk_group=config.topk_group,
|
||||||
|
num_expert_group=config.n_group,
|
||||||
|
renormalize=config.norm_topk_prob,
|
||||||
|
scoring_func="sigmoid",
|
||||||
|
correction_bias=self.gate.e_score_correction_bias,
|
||||||
|
routed_scaling_factor=self.routed_scaling_factor,
|
||||||
|
apply_routed_scaling_factor_on_output=self.experts.should_fuse_routed_scaling_factor_in_topk,
|
||||||
)
|
)
|
||||||
if config.n_shared_experts:
|
if config.n_shared_experts:
|
||||||
self.shared_experts = NemotronHMLP(
|
self.shared_experts = NemotronHMLP(
|
||||||
@@ -243,7 +244,10 @@ class NemotronHMoE(nn.Module):
|
|||||||
hidden_states: torch.Tensor,
|
hidden_states: torch.Tensor,
|
||||||
) -> tuple[torch.Tensor, torch.Tensor | None]:
|
) -> tuple[torch.Tensor, torch.Tensor | None]:
|
||||||
# router_scores: [num_tokens, num_experts]
|
# router_scores: [num_tokens, num_experts]
|
||||||
router_logits, _ = self.gate(hidden_states.to(dtype=torch.float32))
|
# bf16 gemm on tensor cores with fp32 accumulation/output for sigmoid/topk.
|
||||||
|
router_logits = torch.mm(
|
||||||
|
hidden_states, self.gate.weight.t(), out_dtype=torch.float32
|
||||||
|
)
|
||||||
if self.shared_experts is not None:
|
if self.shared_experts is not None:
|
||||||
shared_output = self.shared_experts(hidden_states)
|
shared_output = self.shared_experts(hidden_states)
|
||||||
else:
|
else:
|
||||||
@@ -269,7 +273,10 @@ class NemotronHMoE(nn.Module):
|
|||||||
|
|
||||||
with self.device_module.stream(alt_stream):
|
with self.device_module.stream(alt_stream):
|
||||||
# router_scores: [num_tokens, num_experts]
|
# router_scores: [num_tokens, num_experts]
|
||||||
router_logits, _ = self.gate(hidden_states.to(dtype=torch.float32))
|
# bf16 gemm on tensor cores with fp32 accumulation/output for sigmoid/topk.
|
||||||
|
router_logits = torch.mm(
|
||||||
|
hidden_states, self.gate.weight.t(), out_dtype=torch.float32
|
||||||
|
)
|
||||||
topk_output = self.topk(hidden_states, router_logits)
|
topk_output = self.topk(hidden_states, router_logits)
|
||||||
if self.use_latent_moe:
|
if self.use_latent_moe:
|
||||||
hidden_states, _ = self.fc1_latent_proj(hidden_states)
|
hidden_states, _ = self.fc1_latent_proj(hidden_states)
|
||||||
@@ -280,15 +287,10 @@ class NemotronHMoE(nn.Module):
|
|||||||
|
|
||||||
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
||||||
num_tokens, hidden_dim = hidden_states.shape
|
num_tokens, hidden_dim = hidden_states.shape
|
||||||
|
# routed_scaling_factor is fused into the experts call (applied by the
|
||||||
|
# MoE runner / topk), so final_hidden_states is already scaled.
|
||||||
final_hidden_states, shared_output = self._forward_core(hidden_states)
|
final_hidden_states, shared_output = self._forward_core(hidden_states)
|
||||||
|
|
||||||
# Fix FP16 overflow
|
|
||||||
if hidden_states.dtype != torch.float16:
|
|
||||||
final_hidden_states *= self.routed_scaling_factor
|
|
||||||
elif self.shared_experts is not None:
|
|
||||||
assert shared_output is not None
|
|
||||||
shared_output *= 1.0 / self.routed_scaling_factor
|
|
||||||
|
|
||||||
if self.use_latent_moe:
|
if self.use_latent_moe:
|
||||||
final_hidden_states, _ = self.fc2_latent_proj(final_hidden_states)
|
final_hidden_states, _ = self.fc2_latent_proj(final_hidden_states)
|
||||||
|
|
||||||
@@ -414,20 +416,18 @@ class NemotronHMambaDecoderLayer(nn.Module):
|
|||||||
def _forward_mamba(
|
def _forward_mamba(
|
||||||
self, hidden_states: torch.Tensor, forward_batch: ForwardBatch
|
self, hidden_states: torch.Tensor, forward_batch: ForwardBatch
|
||||||
) -> torch.Tensor:
|
) -> torch.Tensor:
|
||||||
"""Core Mamba forward logic, called directly or via split op."""
|
"""Core Mamba forward logic for the eager path; returns the result."""
|
||||||
output = torch.empty_like(hidden_states)
|
|
||||||
attn_backend = get_attn_backend()
|
attn_backend = get_attn_backend()
|
||||||
assert isinstance(attn_backend, HybridLinearAttnBackend)
|
assert isinstance(attn_backend, HybridLinearAttnBackend)
|
||||||
assert isinstance(attn_backend.linear_attn_backend, Mamba2AttnBackend)
|
assert isinstance(attn_backend.linear_attn_backend, Mamba2AttnBackend)
|
||||||
attn_backend.linear_attn_backend.forward(
|
return attn_backend.linear_attn_backend.forward(
|
||||||
mixer=self.mixer,
|
mixer=self.mixer,
|
||||||
layer_id=self.layer_id,
|
layer_id=self.layer_id,
|
||||||
hidden_states=hidden_states,
|
hidden_states=hidden_states,
|
||||||
output=output,
|
output=None,
|
||||||
forward_batch=forward_batch,
|
forward_batch=forward_batch,
|
||||||
use_triton_causal_conv=True,
|
use_triton_causal_conv=True,
|
||||||
)
|
)
|
||||||
return output
|
|
||||||
|
|
||||||
def forward(
|
def forward(
|
||||||
self,
|
self,
|
||||||
|
|||||||
Reference in New Issue
Block a user