161 lines
5.1 KiB
Python
161 lines
5.1 KiB
Python
import os
|
|
|
|
import numpy as np
|
|
import pytest
|
|
import torch
|
|
|
|
try:
|
|
import tabulate
|
|
except Exception:
|
|
tabulate = None
|
|
|
|
from sglang.jit_kernel.timestep_embedding import (
|
|
timestep_embedding as timestep_embedding_cuda,
|
|
)
|
|
|
|
|
|
def get_timestep_embedding_reference(
|
|
timesteps: torch.Tensor,
|
|
dim: int,
|
|
*,
|
|
flip_sin_to_cos: bool = False,
|
|
downscale_freq_shift: float = 1,
|
|
scale: float = 1,
|
|
max_period: int = 10000,
|
|
):
|
|
assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array"
|
|
|
|
timesteps = timesteps.to(torch.float32)
|
|
half_dim = dim // 2
|
|
exponent = -torch.log(
|
|
torch.tensor(max_period, dtype=torch.float32, device=timesteps.device)
|
|
) * torch.arange(
|
|
start=0, end=half_dim, dtype=torch.float32, device=timesteps.device
|
|
)
|
|
exponent = exponent / (half_dim - downscale_freq_shift)
|
|
|
|
emb = torch.exp(exponent)
|
|
emb = timesteps[:, None].float() * emb[None, :]
|
|
|
|
emb = scale * emb
|
|
|
|
emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
|
|
if flip_sin_to_cos:
|
|
emb = torch.cat([emb[:, half_dim:], emb[:, :half_dim]], dim=-1)
|
|
if dim % 2 == 1:
|
|
emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
|
|
return emb
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"batch_size", [1, 2, 8, 128, 256, 512, 1536, 2048, 4096, 11008, 16384]
|
|
)
|
|
@pytest.mark.parametrize("dim", [32, 128, 256, 512, 1536, 2048, 4096, 8192])
|
|
@pytest.mark.parametrize("dtype", [torch.float16, torch.bfloat16, torch.float32])
|
|
def test_timestep_embedding_correctness_with_sgld(batch_size, dim, dtype):
|
|
device = "cuda"
|
|
t = torch.randint(low=0, high=1000, size=(batch_size,), device=device).to(dtype)
|
|
torch_output = get_timestep_embedding_reference(
|
|
t, dim, flip_sin_to_cos=True, downscale_freq_shift=0
|
|
)
|
|
cuda_output = timestep_embedding_cuda(
|
|
t, dim, flip_sin_to_cos=True, downscale_freq_shift=0
|
|
)
|
|
torch.testing.assert_close(torch_output, cuda_output, atol=1e-3, rtol=1e-3)
|
|
|
|
|
|
@pytest.mark.parametrize("batch_size", [1, 2, 8, 128, 256, 512, 1536, 2048, 16384])
|
|
@pytest.mark.parametrize("dim", [32, 256, 512, 1536, 8192])
|
|
@pytest.mark.parametrize("dtype", [torch.float16, torch.bfloat16, torch.float32])
|
|
@pytest.mark.parametrize("flip_sin_to_cos", [False, True])
|
|
@pytest.mark.parametrize("downscale_freq_shift", [0, 1])
|
|
@pytest.mark.parametrize("scale", [1, 0.01])
|
|
def test_timestep_embedding_correctness_with_diffusers(
|
|
batch_size, dim, flip_sin_to_cos, downscale_freq_shift, scale, dtype
|
|
):
|
|
device = "cuda"
|
|
t = torch.randint(low=0, high=1000, size=(batch_size,), device=device).to(dtype)
|
|
torch_output = get_timestep_embedding_reference(
|
|
t,
|
|
dim,
|
|
flip_sin_to_cos=flip_sin_to_cos,
|
|
downscale_freq_shift=downscale_freq_shift,
|
|
scale=scale,
|
|
max_period=10000,
|
|
)
|
|
cuda_output = timestep_embedding_cuda(
|
|
t,
|
|
dim,
|
|
flip_sin_to_cos=flip_sin_to_cos,
|
|
downscale_freq_shift=downscale_freq_shift,
|
|
scale=scale,
|
|
max_period=10000,
|
|
)
|
|
torch.testing.assert_close(torch_output, cuda_output, atol=1e-3, rtol=1e-3)
|
|
|
|
|
|
def test_timestep_embedding_perf():
|
|
if os.environ.get("SGLANG_RUN_JIT_KERNEL_PERF_TESTS") != "1":
|
|
pytest.skip("Perf test disabled by default")
|
|
if tabulate is None:
|
|
pytest.skip("Optional dependency 'tabulate' is not installed")
|
|
|
|
NUM_BATCH = [1, 2, 8, 63, 256, 512, 613, 1024, 1536]
|
|
NUM_DIM = [32, 64, 128, 256, 512, 1024, 2048, 4096]
|
|
|
|
def perf_kernel_fn(kernel_fn: callable, *args, **kwargs):
|
|
warmup_times = 4
|
|
repeat_times = 20
|
|
start = torch.cuda.Event(enable_timing=True)
|
|
end = torch.cuda.Event(enable_timing=True)
|
|
|
|
for _ in range(warmup_times):
|
|
output_fn = kernel_fn(*args, **kwargs)
|
|
torch.cuda.synchronize()
|
|
|
|
start.record()
|
|
for _ in range(repeat_times):
|
|
output_fn = kernel_fn(*args, **kwargs)
|
|
end.record()
|
|
end.synchronize()
|
|
return start.elapsed_time(end) / repeat_times
|
|
|
|
device = "cuda"
|
|
results = []
|
|
|
|
cuda_speedups = []
|
|
for B in NUM_BATCH:
|
|
for dim in NUM_DIM:
|
|
t = torch.linspace(0, max(100000, B), steps=B, device=device).to(
|
|
torch.float32
|
|
)
|
|
time_torch = perf_kernel_fn(get_timestep_embedding_reference, t, dim)
|
|
time_cuda = perf_kernel_fn(timestep_embedding_cuda, t, dim)
|
|
speedup_cuda = time_torch / time_cuda
|
|
|
|
results.append(
|
|
{
|
|
"Batch Size": B,
|
|
"Dimension": dim,
|
|
"Torch Time (ms)": time_torch,
|
|
"CUDA Time (ms)": time_cuda,
|
|
"Speedup (CUDA)": speedup_cuda,
|
|
}
|
|
)
|
|
cuda_speedups.append(speedup_cuda)
|
|
|
|
print("=== Timestep Embedding Benchmark Results ===")
|
|
print(
|
|
tabulate.tabulate(
|
|
results,
|
|
headers="keys",
|
|
tablefmt="fancy_grid",
|
|
floatfmt=(".0f", ".0f", ".6f", ".6f", ".5f"),
|
|
)
|
|
)
|
|
print(f"Average Speedup(cuda): {np.mean(cuda_speedups):.4f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__])
|