model: paged KV cache with CPU swap pool, decode graph, qwen3 updates
- paged_kv_cache: new block-paged KV cache; adds a pinned-host swap pool with
a second BlockAllocator, per-sequence Location {Gpu,Cpu}, and lossless
swap_out/swap_in (block-granular D2H/H2D) for vLLM-style preemption.
bytes_per_block helper exposes per-block cost for VRAM-based sizing.
- decode_graph: CUDA-graph decode path.
- qwen3/gpt2/kv_cache: paged prefill/decode forward + related updates.
- tokenizer/bins: BPE updates, new xserv-chat CLI, bench-qwen3 tweaks.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
use xserv_model::qwen3::sample_greedy;
|
||||
use xserv_model::{loader, GpuKVCache, ModelConfig, Qwen3};
|
||||
use xserv_model::{loader, DecodeGraphState, GpuKVCache, ModelConfig, Qwen3};
|
||||
use xserv_tensor::{DType, Device};
|
||||
use xserv_tokenizer::Tokenizer;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: bench-qwen3 <model-dir> [--gen-tokens N]");
|
||||
eprintln!("Usage: bench-qwen3 <model-dir> [--gen-tokens N] [--cuda-graph]");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let model_dir = PathBuf::from(&args[1]);
|
||||
@@ -18,6 +18,7 @@ fn main() {
|
||||
.and_then(|i| args.get(i + 1))
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(20);
|
||||
let use_cuda_graph = args.iter().any(|a| a == "--cuda-graph");
|
||||
|
||||
xserv_cuda::device::set_device(0).unwrap();
|
||||
|
||||
@@ -34,6 +35,18 @@ fn main() {
|
||||
let mut cache = GpuKVCache::new(&config, 256, DType::BF16, 0);
|
||||
let _ = model.forward_gpu_cache(&ids, &mut cache);
|
||||
}
|
||||
|
||||
// CUDA Graph setup
|
||||
let layer_ptrs = model.layer_weight_ptrs();
|
||||
let (norm_w, lm_head, embed, cos, sin) = model.graph_capture_ptrs();
|
||||
let mut decode_graph = if use_cuda_graph {
|
||||
eprintln!("CUDA Graph mode enabled");
|
||||
Some(DecodeGraphState::new(&config))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut graph_captured = false;
|
||||
|
||||
eprintln!("Warmup done. Running benchmark...");
|
||||
|
||||
let prompts: Vec<&str> = vec![
|
||||
@@ -96,6 +109,12 @@ fn main() {
|
||||
|
||||
let mut cache = GpuKVCache::new(&config, 256, DType::BF16, 0);
|
||||
|
||||
// Reset graph state for new prompt
|
||||
graph_captured = false;
|
||||
if let Some(ref mut g) = decode_graph {
|
||||
g.invalidate();
|
||||
}
|
||||
|
||||
// Prefill
|
||||
let t0 = Instant::now();
|
||||
let logits = model.forward_gpu_cache(&input_ids, &mut cache);
|
||||
@@ -109,8 +128,35 @@ fn main() {
|
||||
for _ in 1..gen_tokens {
|
||||
let last = *generated.last().unwrap();
|
||||
let t_start = Instant::now();
|
||||
let logits = model.forward_gpu_cache(&[last], &mut cache);
|
||||
let next = sample_greedy(&logits);
|
||||
|
||||
let next = if let Some(ref mut graph) = decode_graph {
|
||||
if !graph_captured {
|
||||
// First decode token: run ungraphed, then capture
|
||||
let logits = model.forward_gpu_cache(&[last], &mut cache);
|
||||
graph_captured = true;
|
||||
graph.capture(&layer_ptrs, norm_w, lm_head, embed, cos, sin);
|
||||
sample_greedy(&logits)
|
||||
} else {
|
||||
// Replay captured graphs
|
||||
let pos = cache.seq_len() as u32;
|
||||
graph.execute(last, pos, &mut cache, &layer_ptrs, embed, config.vocab_size as i32, config.hidden() as i32);
|
||||
cache.advance_seq_len(1);
|
||||
// Read logits from graph buffer
|
||||
let vocab_size = config.vocab_size;
|
||||
let mut logits_bytes = vec![0u8; vocab_size * 2];
|
||||
graph.logits_buffer().copy_to_host(&mut logits_bytes).unwrap();
|
||||
let logits_data: &[half::bf16] = unsafe {
|
||||
std::slice::from_raw_parts(logits_bytes.as_ptr() as *const half::bf16, vocab_size)
|
||||
};
|
||||
logits_data.iter().enumerate()
|
||||
.max_by(|a, b| a.1.to_f32().partial_cmp(&b.1.to_f32()).unwrap())
|
||||
.map(|(idx, _)| idx as u32).unwrap()
|
||||
}
|
||||
} else {
|
||||
let logits = model.forward_gpu_cache(&[last], &mut cache);
|
||||
sample_greedy(&logits)
|
||||
};
|
||||
|
||||
token_times.push(t_start.elapsed().as_micros());
|
||||
generated.push(next);
|
||||
if tokenizer.eos_token_id() == Some(next) { break; }
|
||||
|
||||
Reference in New Issue
Block a user