mirror of
https://github.com/linkedin/Liger-Kernel.git
synced 2026-10-12 02:17:47 +00:00
## Summary Adds a fused Modulated RMSNorm kernel for the common pattern: ```python y = rms_norm(x, weight, eps) * (1 + scale) + shift ``` This is intended for FiLM/AdaRMSNorm-style modulation where RMSNorm output is scaled and optionally shifted by per-hidden-dimension modulation tensors. Related issue: #1224 ## Changes - Add `LigerModulatedRMSNormFunction` with fused forward/backward Triton kernels. - Add `LigerModulatedRMSNorm` module wrapper and `liger_modulated_rms_norm` functional API. - Support optional RMSNorm weight, optional shift, and existing RMSNorm casting modes: `llama`, `gemma`, `none`. - Support modulation shapes that flatten to `(hidden,)`, `(batch, hidden)`, or row-wise `(batch, seq, hidden)` when rows divide the hidden-state rows. - Add correctness tests across fp32/bf16, casting modes, modulation broadcast modes, optional shift, and optional affine weight. - Add benchmark script for speed and memory comparison. ## Benchmark Environment: NVIDIA GeForce RTX 3090, bf16, hidden size 4096, row-wise `scale` and `shift`. ### Speed, p50 ms | BT | Mode | Fused | LigerRMSNorm + torch mod | Speedup | |---:|---|---:|---:|---:| | 1024 | forward | 0.0430 | 0.1091 | 2.5x | | 1024 | backward | 0.0763 | 0.1403 | 1.8x | | 1024 | full | 0.1403 | 0.2642 | 1.9x | | 4096 | forward | 0.1546 | 0.3999 | 2.6x | | 4096 | backward | 0.2540 | 0.4895 | 1.9x | | 4096 | full | 0.4787 | 1.3793 | 2.9x | ### Memory, p50 MB | BT | Mode | Fused | LigerRMSNorm + torch mod | Savings | |---:|---|---:|---:|---:| | 1024 | forward | 32.02 | 56.02 | 42.8% | | 1024 | backward | 89.33 | 97.33 | 8.2% | | 1024 | full | 89.33 | 97.33 | 8.2% | | 4096 | forward | 128.03 | 224.03 | 42.9% | | 4096 | backward | 353.34 | 385.34 | 8.3% | | 4096 | full | 353.34 | 385.34 | 8.3% |
219 lines
7.7 KiB
Python
219 lines
7.7 KiB
Python
import math
|
|
import os
|
|
import sys
|
|
|
|
import torch
|
|
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
|
|
|
from benchmark_model_configs import MODEL_REGISTRY
|
|
from benchmark_model_configs import compute_model_config_sweep_config
|
|
from benchmark_model_configs import compute_seq_len_sweep_config
|
|
from benchmark_model_configs import estimate_kernel_peak_memory
|
|
from benchmark_model_configs import get_benchmark_model_config
|
|
from test.transformers.test_modulated_rms_norm import ModulatedRMSNormReference
|
|
from utils import SingleBenchmarkRunInput
|
|
from utils import SingleBenchmarkRunOutput
|
|
from utils import parse_benchmark_script_args
|
|
from utils import run_benchmarks
|
|
from utils import run_memory_benchmark
|
|
from utils import run_speed_benchmark
|
|
|
|
from liger_kernel.transformers.modulated_rms_norm import LigerModulatedRMSNorm
|
|
from liger_kernel.utils import infer_device
|
|
|
|
device = infer_device()
|
|
|
|
|
|
def _setup_modulated_rms_norm(input: SingleBenchmarkRunInput):
|
|
"""Create input tensors and ModulatedRMSNorm layer from benchmark config."""
|
|
cfg = input.extra_benchmark_config
|
|
hidden_size = cfg["hidden_size"]
|
|
eps = cfg["eps"]
|
|
has_shift = cfg["has_shift"]
|
|
x = torch.randn(
|
|
input.x,
|
|
hidden_size,
|
|
device=device,
|
|
dtype=cfg["dtype"],
|
|
requires_grad=True,
|
|
)
|
|
scale = (torch.randn_like(x) * 0.1).requires_grad_(True)
|
|
shift = (torch.randn_like(x) * 0.1).requires_grad_(True) if has_shift else None
|
|
|
|
if input.kernel_provider == "liger":
|
|
layer = LigerModulatedRMSNorm(hidden_size=hidden_size, eps=eps, in_place=False).to(device)
|
|
elif input.kernel_provider == "huggingface":
|
|
layer = ModulatedRMSNormReference(hidden_size=hidden_size, eps=eps).to(device)
|
|
else:
|
|
raise ValueError(f"Invalid provider: {input.kernel_provider} for ModulatedRMSNorm")
|
|
return x, scale, shift, layer
|
|
|
|
|
|
def _input_tensors(x, scale, shift):
|
|
tensors = [x, scale]
|
|
if shift is not None:
|
|
tensors.append(shift)
|
|
return tensors
|
|
|
|
|
|
def bench_speed_modulated_rms_norm(input: SingleBenchmarkRunInput) -> SingleBenchmarkRunOutput:
|
|
x, scale, shift, layer = _setup_modulated_rms_norm(input)
|
|
return run_speed_benchmark(
|
|
lambda: layer(x, scale, shift), input.kernel_operation_mode, _input_tensors(x, scale, shift)
|
|
)
|
|
|
|
|
|
def bench_memory_modulated_rms_norm(input: SingleBenchmarkRunInput) -> SingleBenchmarkRunOutput:
|
|
x, scale, shift, layer = _setup_modulated_rms_norm(input)
|
|
return run_memory_benchmark(lambda: layer(x, scale, shift), input.kernel_operation_mode)
|
|
|
|
|
|
def _resolve_model_config_modulated_rms_norm(input: SingleBenchmarkRunInput):
|
|
cfg = input.extra_benchmark_config
|
|
model_info = cfg["model_configs"][input.x]
|
|
return _setup_modulated_rms_norm(
|
|
SingleBenchmarkRunInput(
|
|
x=cfg["BT"],
|
|
kernel_provider=input.kernel_provider,
|
|
extra_benchmark_config={
|
|
"hidden_size": model_info["hidden_size"],
|
|
"dtype": model_info["dtype"],
|
|
"eps": cfg["eps"],
|
|
"has_shift": cfg["has_shift"],
|
|
},
|
|
)
|
|
)
|
|
|
|
|
|
def bench_speed_modulated_rms_norm_model_config(input: SingleBenchmarkRunInput) -> SingleBenchmarkRunOutput:
|
|
x, scale, shift, layer = _resolve_model_config_modulated_rms_norm(input)
|
|
return run_speed_benchmark(
|
|
lambda: layer(x, scale, shift), input.kernel_operation_mode, _input_tensors(x, scale, shift)
|
|
)
|
|
|
|
|
|
def bench_memory_modulated_rms_norm_model_config(input: SingleBenchmarkRunInput) -> SingleBenchmarkRunOutput:
|
|
x, scale, shift, layer = _resolve_model_config_modulated_rms_norm(input)
|
|
return run_memory_benchmark(lambda: layer(x, scale, shift), input.kernel_operation_mode)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = parse_benchmark_script_args()
|
|
|
|
if args.sweep_mode == "model_config":
|
|
all_model_configs = list(MODEL_REGISTRY.values())
|
|
|
|
def _probe_factory(model_cfg, probe_bt):
|
|
def _probe():
|
|
probe_input = SingleBenchmarkRunInput(
|
|
x=probe_bt,
|
|
kernel_provider="huggingface",
|
|
extra_benchmark_config={
|
|
"hidden_size": model_cfg.hidden_size,
|
|
"dtype": model_cfg.dtype,
|
|
"eps": 1e-6,
|
|
"has_shift": True,
|
|
},
|
|
)
|
|
x, scale, shift, layer = _setup_modulated_rms_norm(probe_input)
|
|
return layer(x, scale, shift)
|
|
|
|
return _probe
|
|
|
|
sweep = compute_model_config_sweep_config(all_model_configs, probe_fn_factory=_probe_factory, bt=args.bt)
|
|
|
|
model_configs_info = {
|
|
cfg.name: {
|
|
"hidden_size": cfg.hidden_size,
|
|
"dtype": cfg.dtype,
|
|
}
|
|
for cfg in sweep.model_configs
|
|
}
|
|
|
|
common_configs = {
|
|
"kernel_name": "modulated_rms_norm",
|
|
"x_name": "model_config",
|
|
"x_label": "model configuration",
|
|
"x_values": [cfg.name for cfg in sweep.model_configs],
|
|
"kernel_providers": ["liger", "huggingface"],
|
|
"extra_benchmark_configs": [
|
|
{
|
|
"model_configs": model_configs_info,
|
|
"BT": sweep.bt,
|
|
"eps": 1e-6,
|
|
"has_shift": True,
|
|
}
|
|
],
|
|
"overwrite": args.overwrite,
|
|
}
|
|
|
|
run_benchmarks(
|
|
bench_test_fn=bench_speed_modulated_rms_norm_model_config,
|
|
kernel_operation_modes=["full", "forward", "backward"],
|
|
metric_name="speed",
|
|
metric_unit="ms",
|
|
**common_configs,
|
|
)
|
|
run_benchmarks(
|
|
bench_test_fn=bench_memory_modulated_rms_norm_model_config,
|
|
kernel_operation_modes=["full"],
|
|
metric_name="memory",
|
|
metric_unit="MB",
|
|
**common_configs,
|
|
)
|
|
else:
|
|
model = get_benchmark_model_config(args.model)
|
|
probe_bt = 1024
|
|
|
|
def _probe():
|
|
probe_input = SingleBenchmarkRunInput(
|
|
x=probe_bt,
|
|
kernel_provider="huggingface",
|
|
extra_benchmark_config={
|
|
"hidden_size": model.hidden_size,
|
|
"dtype": model.dtype,
|
|
"eps": 1e-6,
|
|
"has_shift": True,
|
|
},
|
|
)
|
|
x, scale, shift, layer = _setup_modulated_rms_norm(probe_input)
|
|
return layer(x, scale, shift)
|
|
|
|
peak_bytes = estimate_kernel_peak_memory(probe_fn=_probe)
|
|
kernel_bpt = peak_bytes // probe_bt
|
|
|
|
config = compute_seq_len_sweep_config(model, kernel_bytes_per_token=kernel_bpt)
|
|
|
|
common_configs = {
|
|
"kernel_name": "modulated_rms_norm",
|
|
"x_name": "BT",
|
|
"x_label": "B * T",
|
|
"x_values": [2**i for i in range(10, int(math.log2(config.batch_size * config.seq_len)) + 1)],
|
|
"kernel_providers": ["liger", "huggingface"],
|
|
"extra_benchmark_configs": [
|
|
{
|
|
"hidden_size": model.hidden_size,
|
|
"dtype": model.dtype,
|
|
"eps": 1e-6,
|
|
"has_shift": True,
|
|
}
|
|
],
|
|
"overwrite": args.overwrite,
|
|
}
|
|
|
|
run_benchmarks(
|
|
bench_test_fn=bench_speed_modulated_rms_norm,
|
|
kernel_operation_modes=["full", "forward", "backward"],
|
|
metric_name="speed",
|
|
metric_unit="ms",
|
|
**common_configs,
|
|
)
|
|
run_benchmarks(
|
|
bench_test_fn=bench_memory_modulated_rms_norm,
|
|
kernel_operation_modes=["full"],
|
|
metric_name="memory",
|
|
metric_unit="MB",
|
|
**common_configs,
|
|
)
|