-
Notifications
You must be signed in to change notification settings - Fork 62
docs(bench): add H100 SXM5 benchmark results alongside A100 baseline #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Billy1900
wants to merge
6
commits into
RL-Align:main
Choose a base branch
from
Billy1900:bench/h100-sxm5-results
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
946edee
docs(bench): add H100 SXM5 benchmark results alongside A100 baseline
Billy1900 55a2b4e
docs(bench): commit raw artifacts backing the H100 sampling/linear_lo…
Billy1900 3927f92
Merge branch 'main' into bench/h100-sxm5-results
Billy1900 8b69ca4
fix(lint): resolve pre-commit failures in benchmark script and reports
Billy1900 023c7a1
Merge branch 'main' into bench/h100-sxm5-results
Billy1900 f1433f9
Merge branch 'main' into bench/h100-sxm5-results
Billy1900 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (c) 2026 RL-Kernel Contributors | ||
|
|
||
| """Real-model logprob memory/latency benchmark against Qwen3-30B-A3B. | ||
|
|
||
| Downloads real weights from the HF Hub (default: Qwen/Qwen3-30B-A3B) and runs a real | ||
| forward pass to obtain real hidden states and the model's real lm_head weight -- unlike | ||
| benchmarks/benchmark_linear_logp.py, which uses synthetic random tensors. Compares native | ||
| (materializing log_softmax + gather) against the dispatched rl_engine linear_logp kernel | ||
| as token count scales into the model's remaining VRAM headroom. | ||
|
|
||
| Usage: | ||
| python benchmarks/benchmark_qwen3_moe_real_model.py | ||
| python benchmarks/benchmark_qwen3_moe_real_model.py --model Qwen/Qwen3-30B-A3B \ | ||
| --n-configs 2048,4096,8192,12288,16384 | ||
| """ | ||
|
|
||
| import argparse | ||
| import time | ||
|
|
||
| import torch | ||
| from tabulate import tabulate | ||
| from transformers import AutoModelForCausalLM, AutoTokenizer | ||
|
|
||
| from rl_engine.kernels.registry import kernel_registry | ||
| from rl_engine.utils.logger import logger | ||
|
|
||
| DEFAULT_N_CONFIGS = [2048, 4096, 8192, 12288, 16384, 20480, 24576] | ||
| DEFAULT_PROMPT = ( | ||
| "Explain the tradeoffs between memory usage and compute latency when " | ||
| "computing log probabilities over a large vocabulary in reinforcement " | ||
| "learning post-training for large language models." | ||
| ) | ||
|
|
||
|
|
||
| def gb(x): | ||
| return x / (1024**3) | ||
|
|
||
|
|
||
| def native_logprob(hidden, weight, target): | ||
| """Standard full log_softmax + gather -- O(N*V) extra memory.""" | ||
| logits = torch.nn.functional.linear(hidden, weight) | ||
| log_probs = torch.log_softmax(logits.float(), dim=-1) | ||
| return torch.gather(log_probs, dim=-1, index=target.unsqueeze(-1)).squeeze(-1) | ||
|
|
||
|
|
||
| def measure(fn, warmup=2, iters=5): | ||
| torch.cuda.synchronize() | ||
| torch.cuda.empty_cache() | ||
| base = torch.cuda.memory_allocated() | ||
| for _ in range(warmup): | ||
| fn() | ||
| torch.cuda.synchronize() | ||
| torch.cuda.reset_peak_memory_stats() | ||
| t0 = time.perf_counter() | ||
| for _ in range(iters): | ||
| fn() | ||
| torch.cuda.synchronize() | ||
| t1 = time.perf_counter() | ||
| peak = torch.cuda.max_memory_allocated() | ||
| return gb(peak - base), (t1 - t0) / iters * 1000 | ||
|
|
||
|
|
||
| def run_benchmark(args): | ||
| if not torch.cuda.is_available(): | ||
| raise RuntimeError("this benchmark requires a CUDA GPU.") | ||
| dtype = getattr(torch, args.dtype) | ||
|
|
||
| torch.cuda.reset_peak_memory_stats() | ||
| baseline_before_load = torch.cuda.memory_allocated() | ||
|
|
||
| t0 = time.time() | ||
| logger.info(f"Loading {args.model} in {dtype} ...") | ||
| tokenizer = AutoTokenizer.from_pretrained(args.model) | ||
| model = AutoModelForCausalLM.from_pretrained(args.model, torch_dtype=dtype, device_map={"": 0}) | ||
| model.eval() | ||
| torch.cuda.synchronize() | ||
| load_s = time.time() - t0 | ||
|
|
||
| weight_gb = gb(torch.cuda.memory_allocated() - baseline_before_load) | ||
| total_gb = gb(torch.cuda.get_device_properties(0).total_memory) | ||
| headroom_gb = total_gb - weight_gb | ||
| logger.info( | ||
| f"Loaded in {load_s:.1f}s. Weight VRAM: {weight_gb:.2f} GB / {total_gb:.2f} GB " | ||
| f"total -> headroom {headroom_gb:.2f} GB" | ||
| ) | ||
|
|
||
| hidden_size = model.config.hidden_size | ||
| vocab_size = model.config.vocab_size | ||
| lm_head_weight = model.lm_head.weight.detach() | ||
| logger.info( | ||
| f"hidden_size={hidden_size} vocab_size={vocab_size} " | ||
| f"lm_head shape={tuple(lm_head_weight.shape)}" | ||
| ) | ||
|
|
||
| enc = tokenizer(args.prompt, return_tensors="pt").to("cuda") | ||
|
|
||
| logger.info("Running a real forward pass to obtain real hidden states...") | ||
| with torch.no_grad(): | ||
| out = model.model(**enc, output_hidden_states=False, use_cache=False) | ||
| real_hidden = out.last_hidden_state.detach() # [1, seq, H] | ||
| real_hidden = real_hidden.reshape(-1, hidden_size).to(dtype).contiguous() | ||
| logger.info(f"Real hidden states shape: {tuple(real_hidden.shape)}") | ||
|
|
||
| linear_logp_op = kernel_registry.get_op("linear_logp") | ||
| logger.info(f"Dispatched linear_logp backend: {type(linear_logp_op).__name__}") | ||
|
|
||
| def make_batch(n): | ||
| reps = (n + real_hidden.shape[0] - 1) // real_hidden.shape[0] | ||
| hidden = real_hidden.repeat(reps, 1)[:n].clone() | ||
| target = torch.randint(0, vocab_size, (n,), device="cuda") | ||
| return hidden, target | ||
|
|
||
| rows = [] | ||
| for n in args.n_configs: | ||
| try: | ||
| hidden, target = make_batch(n) | ||
| except torch.cuda.OutOfMemoryError: | ||
| rows.append([n, "OOM (input alloc)", "OOM (input alloc)", "N/A", "N/A", "N/A"]) | ||
| torch.cuda.empty_cache() | ||
| continue | ||
|
|
||
| try: | ||
| native_extra, native_ms = measure( | ||
| lambda: native_logprob(hidden, lm_head_weight, target) | ||
| ) | ||
| native_str, native_ms_str = f"{native_extra:.2f} GB", f"{native_ms:.2f} ms" | ||
| except torch.cuda.OutOfMemoryError: | ||
| native_str, native_ms_str = "OOM", "N/A" | ||
| torch.cuda.empty_cache() | ||
|
|
||
| try: | ||
| kernel_extra, kernel_ms = measure( | ||
| lambda: linear_logp_op(hidden, lm_head_weight, target) | ||
| ) | ||
| kernel_str, kernel_ms_str = f"{kernel_extra:.2f} GB", f"{kernel_ms:.2f} ms" | ||
| except torch.cuda.OutOfMemoryError: | ||
| kernel_str, kernel_ms_str = "OOM", "N/A" | ||
| torch.cuda.empty_cache() | ||
|
|
||
| rows.append( | ||
| [ | ||
| n, | ||
| native_str, | ||
| kernel_str, | ||
| native_ms_str, | ||
| kernel_ms_str, | ||
| f"current alloc: {gb(torch.cuda.memory_allocated()):.1f} GB", | ||
| ] | ||
| ) | ||
| torch.cuda.empty_cache() | ||
|
|
||
| print("\n" + "=" * 100) | ||
| print(f"{args.model} REAL MODEL LOGPROB BENCHMARK on {torch.cuda.get_device_name(0)}") | ||
| print( | ||
| f"Weight VRAM: {weight_gb:.2f} GB | Total: {total_gb:.2f} GB | " | ||
| f"Headroom: {headroom_gb:.2f} GB" | ||
| ) | ||
| print(f"linear_logp backend: {type(linear_logp_op).__name__}") | ||
| print("=" * 100) | ||
| print( | ||
| tabulate( | ||
| rows, | ||
| headers=[ | ||
| "N tokens", | ||
| "Native extra VRAM", | ||
| "RL-Kernel extra VRAM", | ||
| "Native ms", | ||
| "RL-Kernel ms", | ||
| "note", | ||
| ], | ||
| tablefmt="github", | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| def parse_args(): | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| parser.add_argument("--model", type=str, default="Qwen/Qwen3-30B-A3B") | ||
| parser.add_argument("--dtype", type=str, default="bfloat16", choices=["bfloat16", "float16"]) | ||
| parser.add_argument("--prompt", type=str, default=DEFAULT_PROMPT) | ||
| parser.add_argument( | ||
| "--n-configs", | ||
| type=str, | ||
| default=None, | ||
| help="Comma-separated token counts, e.g. '2048,4096,8192'.", | ||
| ) | ||
| args = parser.parse_args() | ||
| args.n_configs = ( | ||
| [int(x) for x in args.n_configs.split(",")] if args.n_configs else DEFAULT_N_CONFIGS | ||
| ) | ||
| return args | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| run_benchmark(parse_args()) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
reports/benchmark_linear_logp_sm90_NVIDIA_H100_80GB_HBM3.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # RL-Kernel linear_logp benchmark — raw output (native vs Triton vs SM90 TMA/WGMMA) | ||
| # GPU: NVIDIA H100 80GB HBM3 (SXM5) | Driver 535.309.01 | CUDA 12.4 | PyTorch 2.6.0+cu124 | ||
| # Extension built with KERNEL_ALIGN_FORCE_SM90=1 | ||
| # Command: python benchmarks/benchmark_linear_logp.py | ||
| # Date: 2026-07-19T08:45:44Z | ||
|
|
||
| INFO 07-19 04:45:46 [RL-Kernel]: RL-Engine initialized with NVIDIA CUDA backend (Version: 12.4) | ||
| INFO 07-19 04:45:46 [RL-Kernel]: Successfully linked to precompiled _C.fused_linear_logp_sm90 kernel. | ||
| INFO 07-19 04:45:46 [RL-Kernel]: linear_logp benchmark on cuda (dtype=torch.bfloat16); SM90 TMA+MMA backend enabled | ||
| INFO 07-19 04:45:50 [RL-Kernel]: Using fused_linear_logp_sm90_backward fast path. | ||
| | shape (N x H x V) | native fwd ms | triton fwd ms | fwd speedup | native f+b ms | triton f+b ms | f+b speedup | native fwd MB | triton fwd MB | sm90 fwd ms | sm90 vs native | sm90 vs triton | sm90 f+b ms | sm90 fwd MB | | ||
| |---------------------|-----------------|-----------------|---------------|-----------------|-----------------|---------------|-----------------|-----------------|---------------|------------------|------------------|---------------|---------------| | ||
| | 4096x2048x32768 | 1.798 | 6.288 | 0.29x | 4.347 | 47.181 | 0.09x | 1280 | 0 | 3.653 | 0.49x | 1.72x | 7.818 | 2 | | ||
| | 4096x2048x50257 | 10.268 | 9.878 | 1.04x | 24.005 | 85.23 | 0.28x | 1965 | 0 | 5.398 | 1.90x | 1.83x | 26.307 | 2 | | ||
| | 4096x2048x131072 | 7.199 | 24.755 | 0.29x | 17.097 | 264.012 | 0.06x | 5120 | 0 | 14.107 | 0.51x | 1.75x | 30.434 | 2 | |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Bind loop values in both benchmark callbacks.
Ruff reports B023 because these lambdas capture loop-scoped
hiddenandtarget. Bind them as default arguments (or usefunctools.partial) so the code passes lint and does not depend onmeasure()remaining synchronous.Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 Ruff (0.16.0)
[warning] 125-125: Function definition does not bind loop variable
hidden(B023)
[warning] 125-125: Function definition does not bind loop variable
target(B023)
[warning] 134-134: Function definition does not bind loop variable
hidden(B023)
[warning] 134-134: Function definition does not bind loop variable
target(B023)
🤖 Prompt for AI Agents
Source: Linters/SAST tools