| 1 | // SPDX-License-Identifier: Apache-2.0 |
| 2 | // SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | |
| 4 | use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main}; |
| 5 | use hf_hub::api::tokio::ApiBuilder; |
| 6 | use tokio::runtime::Runtime; |
| 7 | use vllm_tokenizer::{TiktokenTokenizer, Tokenizer}; |
| 8 | |
| 9 | const MODEL_ID: &str = "moonshotai/Kimi-K2.5"; |
| 10 | const SAMPLE_TEXT: &str = "\ |
| 11 | <think> |
| 12 | I'm sure it's fine, but I can't say I'd trust that it's what we'd ship. |
| 13 | </think> |
| 14 | 请用中英混合总结以下需求,并保留 tool-call marker: |
| 15 | <|tool_calls_section_begin|>{\"name\":\"summarize\",\"arguments\":{\"style\":\"brief\"}}<|tool_calls_section_end|> |
| 16 | The service should stop cleanly at EOS, avoid leaking the next template turn, and keep decode latency low. |
| 17 | "; |
| 18 | |
| 19 | struct BenchFixture { |
| 20 | riptoken: TiktokenTokenizer, |
| 21 | tiktoken_rs: TiktokenTokenizer, |
| 22 | text: String, |
| 23 | token_ids: Vec<u32>, |
| 24 | } |
| 25 | |
| 26 | impl BenchFixture { |
| 27 | fn load() -> Self { |
| 28 | let path = tiktoken_model(); |
| 29 | let riptoken = TiktokenTokenizer::new_riptoken(&path).expect("load riptoken tokenizer"); |
| 30 | let tiktoken_rs = |
| 31 | TiktokenTokenizer::new_tiktoken_rs(&path).expect("load tiktoken-rs tokenizer"); |
| 32 | |
| 33 | let text = SAMPLE_TEXT.repeat(32); |
| 34 | let riptoken_token_ids = |
| 35 | riptoken.encode(text.as_str(), false).expect("encode sample text with riptoken"); |
| 36 | let tiktoken_rs_token_ids = tiktoken_rs |
| 37 | .encode(text.as_str(), false) |
| 38 | .expect("encode sample text with tiktoken-rs"); |
| 39 | assert_eq!(riptoken_token_ids, tiktoken_rs_token_ids); |
| 40 | |
| 41 | let riptoken_decoded = riptoken |
| 42 | .decode(riptoken_token_ids.as_slice(), false) |
| 43 | .expect("decode sample token ids with riptoken"); |
| 44 | let tiktoken_rs_decoded = tiktoken_rs |
| 45 | .decode(riptoken_token_ids.as_slice(), false) |
| 46 | .expect("decode sample token ids with tiktoken-rs"); |
| 47 | assert_eq!(riptoken_decoded, tiktoken_rs_decoded); |
| 48 | |
| 49 | Self { |
| 50 | riptoken, |
| 51 | tiktoken_rs, |
| 52 | text, |
| 53 | token_ids: riptoken_token_ids, |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | fn tiktoken_model() -> std::path::PathBuf { |
| 59 | Runtime::new().expect("build tokio runtime").block_on(async { |
| 60 | let repo = ApiBuilder::from_env() |
| 61 | .with_progress(false) |
| 62 | .build() |
| 63 | .expect("build hf-hub api") |
| 64 | .model(MODEL_ID.to_string()); |
| 65 | repo.get("config.json").await.expect("fetch config.json from hf-hub"); |
| 66 | repo.get("tokenizer_config.json") |
| 67 | .await |
| 68 | .expect("fetch tokenizer_config.json from hf-hub"); |
| 69 | repo.get("tiktoken.model").await.expect("fetch tiktoken.model from hf-hub") |
| 70 | }) |
| 71 | } |
| 72 | |
| 73 | fn bench_encode(c: &mut Criterion) { |
| 74 | let fixture = BenchFixture::load(); |
| 75 | let mut group = c.benchmark_group("tiktoken_encode"); |
| 76 | group.throughput(Throughput::Bytes(fixture.text.len() as u64)); |
| 77 | |
| 78 | group.bench_function("riptoken", |b| { |
| 79 | b.iter(|| { |
| 80 | fixture |
| 81 | .riptoken |
| 82 | .encode(black_box(fixture.text.as_str()), black_box(false)) |
| 83 | .expect("encode sample text with riptoken") |
| 84 | }) |
| 85 | }); |
| 86 | group.bench_function("tiktoken_rs", |b| { |
| 87 | b.iter(|| { |
| 88 | fixture |
| 89 | .tiktoken_rs |
| 90 | .encode(black_box(fixture.text.as_str()), black_box(false)) |
| 91 | .expect("encode sample text with tiktoken-rs") |
| 92 | }) |
| 93 | }); |
| 94 | |
| 95 | group.finish(); |
| 96 | } |
| 97 | |
| 98 | fn bench_decode(c: &mut Criterion) { |
| 99 | let fixture = BenchFixture::load(); |
| 100 | let mut group = c.benchmark_group("tiktoken_decode"); |
| 101 | group.throughput(Throughput::Elements(fixture.token_ids.len() as u64)); |
| 102 | |
| 103 | group.bench_function("riptoken", |b| { |
| 104 | b.iter(|| { |
| 105 | fixture |
| 106 | .riptoken |
| 107 | .decode(black_box(fixture.token_ids.as_slice()), black_box(false)) |
| 108 | .expect("decode sample token ids with riptoken") |
| 109 | }) |
| 110 | }); |
| 111 | group.bench_function("tiktoken_rs", |b| { |
| 112 | b.iter(|| { |
| 113 | fixture |
| 114 | .tiktoken_rs |
| 115 | .decode(black_box(fixture.token_ids.as_slice()), black_box(false)) |
| 116 | .expect("decode sample token ids with tiktoken-rs") |
| 117 | }) |
| 118 | }); |
| 119 | |
| 120 | group.finish(); |
| 121 | } |
| 122 | |
| 123 | criterion_group!(benches, bench_encode, bench_decode); |
| 124 | criterion_main!(benches); |