| 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::{HuggingFaceTokenizer, Tokenizer}; |
| 8 | |
| 9 | const MODEL_ID: &str = "Qwen/Qwen3.5-0.8B"; |
| 10 | const SAMPLE_TEXT: &str = "\ |
| 11 | <|im_start|>system |
| 12 | You are Qwen3.5, a helpful assistant. |
| 13 | <|im_end|> |
| 14 | <|im_start|>user |
| 15 | 请用中英混合总结以下需求,并给出一个简短的 JSON 示例。 |
| 16 | The service should stop cleanly at EOS, avoid leaking the next template turn, and keep decode latency low. |
| 17 | Input: 4 concurrent requests, 10240 prompt tokens, 16 generated tokens. |
| 18 | <|im_end|> |
| 19 | <|im_start|>assistant |
| 20 | "; |
| 21 | |
| 22 | struct BenchFixture { |
| 23 | fastokens: HuggingFaceTokenizer, |
| 24 | hf: HuggingFaceTokenizer, |
| 25 | text: String, |
| 26 | token_ids: Vec<u32>, |
| 27 | } |
| 28 | |
| 29 | impl BenchFixture { |
| 30 | fn load() -> Self { |
| 31 | let path = tokenizer_json(); |
| 32 | let fastokens = |
| 33 | HuggingFaceTokenizer::new_fastokens(&path).expect("load fastokens tokenizer"); |
| 34 | let hf = HuggingFaceTokenizer::new_hf(&path).expect("load huggingface tokenizer"); |
| 35 | |
| 36 | let text = SAMPLE_TEXT.repeat(32); |
| 37 | let hf_token_ids = |
| 38 | hf.encode(text.as_str(), false).expect("encode sample text with hf tokenizer"); |
| 39 | let fastokens_token_ids = fastokens |
| 40 | .encode(text.as_str(), false) |
| 41 | .expect("encode sample text with fastokens"); |
| 42 | assert_eq!(fastokens_token_ids, hf_token_ids); |
| 43 | |
| 44 | let hf_decoded = hf |
| 45 | .decode(hf_token_ids.as_slice(), false) |
| 46 | .expect("decode sample token ids with hf tokenizer"); |
| 47 | let fastokens_decoded = fastokens |
| 48 | .decode(hf_token_ids.as_slice(), false) |
| 49 | .expect("decode sample token ids with fastokens"); |
| 50 | assert_eq!(fastokens_decoded, hf_decoded); |
| 51 | |
| 52 | Self { |
| 53 | fastokens, |
| 54 | hf, |
| 55 | text, |
| 56 | token_ids: hf_token_ids, |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | fn tokenizer_json() -> std::path::PathBuf { |
| 62 | Runtime::new().expect("build tokio runtime").block_on(async { |
| 63 | ApiBuilder::from_env() |
| 64 | .with_progress(false) |
| 65 | .build() |
| 66 | .expect("build hf-hub api") |
| 67 | .model(MODEL_ID.to_string()) |
| 68 | .get("tokenizer.json") |
| 69 | .await |
| 70 | .expect("fetch tokenizer.json from hf-hub") |
| 71 | }) |
| 72 | } |
| 73 | |
| 74 | fn bench_encode(c: &mut Criterion) { |
| 75 | let fixture = BenchFixture::load(); |
| 76 | let mut group = c.benchmark_group("tokenizer_encode"); |
| 77 | group.throughput(Throughput::Bytes(fixture.text.len() as u64)); |
| 78 | |
| 79 | group.bench_function("fastokens", |b| { |
| 80 | b.iter(|| { |
| 81 | fixture |
| 82 | .fastokens |
| 83 | .encode(black_box(fixture.text.as_str()), black_box(false)) |
| 84 | .expect("encode sample text with fastokens") |
| 85 | }) |
| 86 | }); |
| 87 | group.bench_function("hf_tokenizers", |b| { |
| 88 | b.iter(|| { |
| 89 | fixture |
| 90 | .hf |
| 91 | .encode(black_box(fixture.text.as_str()), black_box(false)) |
| 92 | .expect("encode sample text with hf tokenizer") |
| 93 | }) |
| 94 | }); |
| 95 | |
| 96 | group.finish(); |
| 97 | } |
| 98 | |
| 99 | fn bench_decode(c: &mut Criterion) { |
| 100 | let fixture = BenchFixture::load(); |
| 101 | let mut group = c.benchmark_group("tokenizer_decode"); |
| 102 | group.throughput(Throughput::Elements(fixture.token_ids.len() as u64)); |
| 103 | |
| 104 | group.bench_function("fastokens", |b| { |
| 105 | b.iter(|| { |
| 106 | fixture |
| 107 | .fastokens |
| 108 | .decode(black_box(fixture.token_ids.as_slice()), black_box(false)) |
| 109 | .expect("decode sample token ids with fastokens") |
| 110 | }) |
| 111 | }); |
| 112 | group.bench_function("hf_tokenizers", |b| { |
| 113 | b.iter(|| { |
| 114 | fixture |
| 115 | .hf |
| 116 | .decode(black_box(fixture.token_ids.as_slice()), black_box(false)) |
| 117 | .expect("decode sample token ids with hf tokenizer") |
| 118 | }) |
| 119 | }); |
| 120 | |
| 121 | group.finish(); |
| 122 | } |
| 123 | |
| 124 | criterion_group!(benches, bench_encode, bench_decode); |
| 125 | criterion_main!(benches); |