use std::{fs::File, io::BufReader, path::Path, time::Duration};
use ahash::AHashMap;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use kbnf::{
engine::{Engine, EngineConfig},
vocabulary::{Token, Vocabulary},
EngineLike,
};
#[derive(Debug, thiserror::Error)]
pub enum ReadRWKVVocabError {
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Serde json error: {0}")]
JsonError(#[from] serde_json::Error),
}
pub fn read_rwkv_world_vocab(path: impl AsRef<Path>) -> Result<Vocabulary, ReadRWKVVocabError> {
let path = path.as_ref();
let file = File::open(path).unwrap();
let reader = BufReader::new(file);
let mut id_to_token: AHashMap<u32, Token> = AHashMap::default();
let mut id_to_token_string: AHashMap<u32, String> = AHashMap::default();
let data: serde_json::Map<String, serde_json::Value> = serde_json::from_reader(reader)?;
for (key, value) in data {
let key = key.parse::<u32>().unwrap();
match value {
serde_json::Value::Array(x) => {
let mut token = Vec::new();
for x in x {
match x {
serde_json::Value::Number(x) => {
token.push(x.as_u64().unwrap() as u8);
}
_ => {
panic!("Unexpected value type")
}
}
}
id_to_token.insert(key, Token(token.clone().into_boxed_slice()));
id_to_token_string.insert(key, format!("{:?}", token));
}
serde_json::Value::String(x) => {
id_to_token.insert(key, Token(x.as_bytes().to_vec().into_boxed_slice()));
id_to_token_string.insert(key, x);
}
_ => {
panic!("Unexpected value type")
}
};
}
Ok(Vocabulary::new(id_to_token, id_to_token_string).unwrap())
}
fn run_an_engine(engine: &mut Engine, iteration: usize, token_id: u32, logits: &mut [f32]) {
for _ in 0..iteration {
let _ = engine.try_accept_new_token(token_id).unwrap();
engine.compute_allowed_token_ids();
}
engine.reset(); }
fn criterion_benchmark(c: &mut Criterion) {
let mut c = c.benchmark_group("Simple");
c.measurement_time(Duration::from_secs(10)).sample_size(100);
let vocab = read_rwkv_world_vocab("tests/rwkv_vocab_v20230424.json").unwrap();
let mut logits = vec![0.0f32; 65536];
let mut engine = Engine::new("start::=('{'start'}')?;", vocab.clone()).unwrap();
c.bench_function("unmarked middle recursion 100 iterations", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 100, 124, &mut logits))
});
let no_cache_config = kbnf::config::Config {
engine_config: EngineConfig {
cache_enabled: false,
compaction_enabled: true,
rejected_token_prefix_cache_enabled: true,
},
..Default::default()
};
let mut engine = Engine::with_config(
"start::=C#'\n';C::=#'( )+';",
vocab.clone(),
no_cache_config.clone(),
)
.unwrap();
c.bench_function("whitespace recursion 10 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 10, 33, &mut logits))
});
let mut engine = Engine::with_config(
"start::=C'\n';C::=#substrs' ';",
vocab.clone(),
no_cache_config.clone(),
)
.unwrap();
c.bench_function("substring recursion 10 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 10, 33, &mut logits))
});
let mut engine = Engine::with_config(
"start::=#ex'd|e|f' '\n';",
vocab.clone(),
no_cache_config.clone(),
)
.unwrap();
c.bench_function(
"regular expression complement recursion 10 iterations(no cache)",
|b| b.iter(|| run_an_engine(black_box(&mut engine), 10, 33, &mut logits)),
);
let mut engine = Engine::with_config(
"start::=C'\n';C::='{'|'{' C;",
vocab.clone(),
no_cache_config.clone(),
)
.unwrap();
c.bench_function("right recursion 100 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 100, 124, &mut logits))
});
c.bench_function("right recursion 50 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 50, 124, &mut logits))
});
c.bench_function("right recursion 25 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 25, 124, &mut logits))
});
c.bench_function("right recursion 10 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 10, 124, &mut logits))
});
c.bench_function("right recursion 5 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 5, 124, &mut logits))
});
let mut engine = Engine::with_config(
"start::=C'\n';C::=C'{'|'{';",
vocab.clone(),
no_cache_config.clone(),
)
.unwrap();
c.bench_function("left recursion 100 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 100, 124, &mut logits))
});
let mut engine = Engine::new("start::=#\"[a-zA-Z0-9_ ]+\"'\n';", vocab.clone()).unwrap();
c.bench_function("alphanumeric 5 iterations", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 5, 76, &mut logits))
});
let mut engine = Engine::new("start::=#\".+\"'\n';", vocab.clone()).unwrap();
c.bench_function("always match regex 3 iterations", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 3, 113, &mut logits))
});
let mut engine = Engine::with_config(
"start::=#\".+\"'\n';",
vocab.clone(),
no_cache_config.clone(),
)
.unwrap();
c.bench_function("always match regex 3 iterations(no cache)", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 3, 113, &mut logits))
});
let mut engine = Engine::new("start::=#e'.+\n\n';", vocab.clone()).unwrap();
c.bench_function("simple except! 3 iterations", |b| {
b.iter(|| run_an_engine(black_box(&mut engine), 3, 113, &mut logits))
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);