-
Notifications
You must be signed in to change notification settings - Fork 1.3k
KV Split Oversubscription for Mixed Sequence Lengths #3379
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
18ea66e
82a38d1
d612247
bb416d7
1207140
ec03b0b
531fff7
92eae0a
c5baeb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ def bench_trtllm_mla( | |
| dtype, | ||
| backend="auto", | ||
| with_sinks=False, | ||
| seq_lens_list=None, | ||
| ): | ||
| """Benchmark a single (config, backend, sinks?) cell. | ||
|
|
||
|
|
@@ -38,16 +39,20 @@ def bench_trtllm_mla( | |
| device=device, | ||
| ).to(dtype) | ||
|
|
||
| num_tokens = seq_len * batch_size | ||
| num_blocks = (num_tokens + page_size - 1) // page_size | ||
| if seq_lens_list is not None: | ||
| assert len(seq_lens_list) == batch_size, ( | ||
| f"seq_lens_list length {len(seq_lens_list)} != batch_size {batch_size}" | ||
| ) | ||
| seq_lens = list(seq_lens_list) | ||
| max_seq_len = max(seq_lens) | ||
| else: | ||
| seq_lens = [torch.randint(1, seq_len, (1,)).item() for _ in range(batch_size)] | ||
| seq_lens[-1] = seq_len | ||
| max_seq_len = max(seq_lens) | ||
|
|
||
| # Sequence lengths and block tables | ||
| seq_lens = [torch.randint(1, seq_len, (1,)).item() for _ in range(batch_size)] | ||
| seq_lens[-1] = seq_len | ||
| max_seq_len = max(seq_lens) | ||
| seq_lens_tensor = torch.tensor(seq_lens, dtype=torch.int, device=device) | ||
|
|
||
| blocks_per_seq = (seq_lens_tensor + page_size - 1) // page_size | ||
| num_blocks = int(blocks_per_seq.sum().item()) | ||
| max_num_blocks_per_seq = blocks_per_seq.max().item() | ||
|
|
||
| # Generate random but unique block IDs for all sequences | ||
|
|
@@ -80,7 +85,7 @@ def bench_trtllm_mla( | |
|
|
||
| # Allocate workspace buffer | ||
| # todo(Yingyi): calculate the actual size of workspace buffer | ||
| workspace_buffer = torch.empty(128 * 1024 * 1024, dtype=torch.int8, device=device) | ||
| workspace_buffer = torch.empty(1024 * 1024 * 1024, dtype=torch.int8, device=device) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zero-initialize decode workspace before first kernel use. Line 88 allocates Proposed fix- workspace_buffer = torch.empty(1024 * 1024 * 1024, dtype=torch.int8, device=device)
+ workspace_buffer = torch.zeros(1024 * 1024 * 1024, dtype=torch.int8, device=device)🤖 Prompt for AI Agents |
||
|
|
||
| sinks = ( | ||
| torch.randn(num_q_heads, dtype=torch.float32, device=device) | ||
|
|
@@ -155,6 +160,16 @@ def bench_trtllm_mla( | |
| print(f"FLOPs: {flops / ms / 1e9:.2f} TFLOPs/s") | ||
|
|
||
|
|
||
| def sample_prod_distribution(batch_size: int, seed: int = 42) -> list[int]: | ||
| """Sample sequence lengths from a production-like distribution.""" | ||
| import random | ||
|
|
||
| rng = random.Random(seed) | ||
| seq_lens = [4096, 8192, 16384, 32768, 65536, 131072] | ||
| weights = [35, 20, 15, 12, 10, 8] | ||
| return rng.choices(seq_lens, weights=weights, k=batch_size) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| import argparse | ||
|
|
||
|
|
@@ -238,3 +253,27 @@ def bench_trtllm_mla( | |
| f"{type(e).__name__}: {e}" | ||
| ) | ||
| print() | ||
|
|
||
| # Mixed sequence length benchmark (production-like distribution) | ||
| for batch_size in [16, 32, 64, 128, 256]: | ||
| sl = sample_prod_distribution(batch_size, seed=batch_size) | ||
| try: | ||
| bench_trtllm_mla( | ||
| batch_size, | ||
| 1, | ||
| max(sl), | ||
| 32, | ||
| torch.bfloat16, | ||
| backend=args.backend, | ||
| seq_lens_list=sl, | ||
| ) | ||
| except ValueError as e: | ||
| print(f"SKIPPED: {e}") | ||
| print() | ||
| except Exception as e: | ||
| print( | ||
| f"ERROR: batch_size={batch_size}, q_len=1, " | ||
| f"seq_len=mixed, page_size=32, dtype=torch.bfloat16, " | ||
| f"backend={args.backend}: {type(e).__name__}: {e}" | ||
| ) | ||
| print() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -197,10 +197,34 @@ inline static bool getBoolEnv(char const* name) { | |
| return env && env[0] == '1' && env[1] == '\0'; | ||
| } | ||
|
|
||
| inline static int getIntEnv(char const* name, int defaultVal) { | ||
| char const* env = std::getenv(name); | ||
| return env ? std::atoi(env) : defaultVal; | ||
| } | ||
|
Comment on lines
+200
to
+203
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If Consider adding validation in 🛡️ Proposed fix: add minimum bound in getter inline int getEnvKvOversubMinTokensPerCta() {
- static int const val = getIntEnv("FLASHINFER_KV_OVERSUB_MIN_TOKENS_PER_CTA", 2048);
+ static int const val = std::max(1, getIntEnv("FLASHINFER_KV_OVERSUB_MIN_TOKENS_PER_CTA", 2048));
return val;
}🤖 Prompt for AI Agents |
||
|
|
||
| inline bool getEnvUseTileSizeKv64ForTrtllmGen() { | ||
| static bool const useTileSizeKv64 = getBoolEnv("TRTLLM_GEN_ENABLE_TILE_SIZE_KV64"); | ||
| return useTileSizeKv64; | ||
| } | ||
|
|
||
| // KV split oversubscription tuning (for mixed-length batches). | ||
| // Set FLASHINFER_KV_OVERSUB_MAX_WAVES=1 to disable oversubscription. | ||
| inline int getEnvKvOversubMinTokensPerCta() { | ||
| static int const val = getIntEnv("FLASHINFER_KV_OVERSUB_MIN_TOKENS_PER_CTA", 2048); | ||
| return val; | ||
| } | ||
| inline int getEnvKvOversubMaxWaves() { | ||
| static int const val = getIntEnv("FLASHINFER_KV_OVERSUB_MAX_WAVES", 16); | ||
| return val; | ||
| } | ||
| inline int getEnvKvOversubMaxSplits() { | ||
| static int const val = getIntEnv("FLASHINFER_KV_OVERSUB_MAX_SPLITS", 32); | ||
| return val; | ||
| } | ||
| inline int getEnvKvOversubMinDesiredSplits() { | ||
| static int const val = getIntEnv("FLASHINFER_KV_OVERSUB_MIN_DESIRED_SPLITS", 4); | ||
| return val; | ||
| } | ||
| template <typename T> | ||
| inline __device__ __host__ T divUp(T m, T n) { | ||
| return (m + n - 1) / n; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.