unitoken 0.1.5

Fast BPE tokenizer/trainer with a Rust core and Python bindings
Documentation
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};

use unitoken::{
  bpe::{encoder::BpeBuilder, Idx},
  pretokenizer::PreTokenizer,
  spec::gpt2::Gpt2Spec,
  traits::Encode as _,
};

fn build_gpt2_encoder_from_fixtures(name: &str) -> unitoken::bpe::BpeEncoder<u8> {
  BpeBuilder::new()
    .load_merges_file(format!("fixtures/merges.{name}.txt"), &Gpt2Spec)
    .unwrap()
    .load_vocab_file(format!("fixtures/vocab.{name}.json"), &Gpt2Spec)
    .unwrap()
    .build(&Gpt2Spec)
    .unwrap()
}

fn bench_pretokenizer(c: &mut Criterion) {
  let special_tokens = vec![unitoken::pretokenizer::DEFAULT_EOT.to_string()];
  let pre = PreTokenizer::new(&special_tokens, Some(unitoken::pretokenizer::DEFAULT_EOT));

  let base = "Once upon a time, in a small village, there lived a cat named Mango.";
  let input = base.repeat(200);

  c.bench_function("pretokenizer/get_words", |b| {
    b.iter(|| {
      let words = pre.get_words(black_box(&input)).unwrap();
      black_box(words.len())
    })
  });
}

fn bench_bpe_encode_decode(c: &mut Criterion) {
  const FIXTURE: &str = "tinystories_sample_5M";
  let bpe = build_gpt2_encoder_from_fixtures(FIXTURE);

  let base = "Once upon a time, there was a little robot who loved to read books.";
  let input = base.repeat(200);

  let mut group = c.benchmark_group("bpe");

  group.bench_with_input(BenchmarkId::new("encode_string", FIXTURE), &input, |b, s| {
    b.iter(|| {
      let out = bpe.encode_string(black_box(s)).unwrap();
      black_box(out)
    })
  });

  let encoded: Vec<Idx> = bpe.encode_string(&input).unwrap();
  group.bench_with_input(BenchmarkId::new("decode", FIXTURE), &encoded, |b, ids| {
    b.iter(|| {
      let out = bpe.decode(black_box(ids)).unwrap();
      black_box(out)
    })
  });

  group.finish();
}

criterion_group!(benches, bench_pretokenizer, bench_bpe_encode_decode);
criterion_main!(benches);