Skip to content

Commit

Permalink
Merge branch 'main_n' into peformance
Browse files Browse the repository at this point in the history
This is to separate main lib and performane bench.
  • Loading branch information
aobatact committed Mar 5, 2021
2 parents 6099236 + 9914ebb commit 3898296
Show file tree
Hide file tree
Showing 8 changed files with 1,218 additions and 62 deletions.
1 change: 1 addition & 0 deletions .gitignore
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
.vscode
47 changes: 0 additions & 47 deletions .vscode/launch.json

This file was deleted.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "faster-char-count"
version = "0.1.0"
name = "faster-chars-count"
version = "0.1.1"
authors = ["aobatact <aobatact144@gmail.com>"]
edition = "2018"

Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Library for counting length of chars faster than `Chars::count()`

Idea is from [UTF-8のコードポイントはどうやって高速に数えるか](https://qiita.com/saka1_p/items/ff49d981cfd56f3588cc), and [UTF-8のコードポイントはどうやってもっと高速に数えるか](https://qiita.com/umezawatakeshi/items/ed23935788756c800b86).

## usage
```
//before
"Hello, world!".chars().count();
//after
"Hello, world!".chars_count();
```

## bench
repeated "a" (only 1byte utf8)
![bench 1byte](peformance/lines_1.svg)

repeated "錆" (only 3byte utf8)
![bench 3byte](peformance/lines_3.svg)

## future plan
sse (128bit)

avx512
[UTF-8のコードポイントはどうやってAVX-512で高速に数えるか](https://qiita.com/umezawatakeshi/items/fca066b2fd3dcf9cbec9)
228 changes: 228 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#![feature(test)]
#![feature(log_syntax)]

extern crate test;
use criterion::{
criterion_group, criterion_main, measurement::Measurement, AxisScale, BenchmarkGroup,
BenchmarkId, Criterion, PlotConfiguration,
};
use faster_chars_count::*;
//use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng};
use std::mem::forget;
use std::ptr;

pub fn black_box<T>(dummy: T) -> T {
unsafe {
let ret = ptr::read_volatile(&dummy);
forget(dummy);
ret
}
}

fn group_count_bench<'a, M: Measurement>(
mut group: BenchmarkGroup<'a, M>,
test_strs: impl IntoIterator<Item = &'a (usize, &'a str)>,
) {
for test_str in test_strs {
//group.bench_with_input(BenchmarkId::new("usize",&test_str.0),&test_str.1, |b,i| b.iter(|| chars_count_usize(i)));
group.bench_with_input(BenchmarkId::new("u8", &test_str.0), &test_str.1, |b, i| {
b.iter(|| chars_count_u8(i))
});
group.bench_with_input(BenchmarkId::new("avx", &test_str.0), &test_str.1, |b, i| {
b.iter(|| chars_count_256(i))
});
group.bench_with_input(BenchmarkId::new("u32", &test_str.0), &test_str.1, |b, i| {
b.iter(|| chars_count_u32(i))
});
group.bench_with_input(BenchmarkId::new("u64", &test_str.0), &test_str.1, |b, i| {
b.iter(|| chars_count_u64(i))
});
group.bench_with_input(
BenchmarkId::new("mix1", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix1(i)),
);
//mix 2 is slow
// group.bench_with_input(BenchmarkId::new("mix2", &test_str.0), &test_str.1, |b, i| {
// b.iter(|| chars_count_mix2(i))
// });
// group.bench_with_input(BenchmarkId::new("mix2_suf", &test_str.0), &test_str.1, |b, i| {
// b.iter(|| chars_count_mix2_suf(i))
// });
group.bench_with_input(
BenchmarkId::new("mix3", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix3(i)),
);
group.bench_with_input(
BenchmarkId::new("mix3t", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix3t(i)),
);
group.bench_with_input(
BenchmarkId::new("mix3i", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix3(i)),
);
group.bench_with_input(
BenchmarkId::new("mix3ti", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix3t(i)),
);
group.bench_with_input(BenchmarkId::new("std", &test_str.0), &test_str.1, |b, i| {
b.iter(|| i.chars().count())
});
}
}

fn group_count_bench_avx<'a, M: Measurement>(
mut group: BenchmarkGroup<'a, M>,
test_strs: impl IntoIterator<Item = &'a (usize, &'a str)>,
) {
for test_str in test_strs {
group.bench_with_input(BenchmarkId::new("avx", &test_str.0), &test_str.1, |b, i| {
b.iter(|| chars_count_256(i))
});
group.bench_with_input(
BenchmarkId::new("avx_iter", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_256_iter(i)),
);
}
}

fn group_count_bench_mix1<'a, M: Measurement>(
mut group: BenchmarkGroup<'a, M>,
test_strs: impl IntoIterator<Item = &'a (usize, &'a str)>,
) {
for test_str in test_strs {
//group.bench_with_input(BenchmarkId::new("usize",&test_str.0),&test_str.1, |b,i| b.iter(|| chars_count_usize(i)));
group.bench_with_input(
BenchmarkId::new("mix1", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix1(i)),
);
group.bench_with_input(
BenchmarkId::new("mix1a", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix1a(i)),
);
group.bench_with_input(
BenchmarkId::new("mix1b", &test_str.0),
&test_str.1,
|b, i| b.iter(|| chars_count_mix1b(i)),
);
group.bench_with_input(BenchmarkId::new("std", &test_str.0), &test_str.1, |b, i| {
b.iter(|| i.chars().count())
});
}
}

fn count_bench_1_small_mix1(c: &mut Criterion) {
let mut test_strs_a = vec![];
let a = "a";
let a64 = a.repeat(64);
test_strs_a.push((0, ""));
test_strs_a.push((1, a));
test_strs_a.push((64, &a64));
for i in [2, 3, 4, 7, 8, 12, 15, 16, 17, 24, 31, 32, 33, 48, 49, 63].iter() {
unsafe {
test_strs_a.push((*i, a64.get_unchecked(..a.len() * i)));
}
}
let group = c.benchmark_group("count_bench_1byte_small_mix1");
group_count_bench_mix1(group, test_strs_a.iter());
}

fn count_bench_1_small(c: &mut Criterion) {
let mut test_strs_a = vec![];
let a = "a";
let a64 = a.repeat(64);
test_strs_a.push((0, ""));
test_strs_a.push((1, a));
test_strs_a.push((64, &a64));
for i in [2, 4, 8, 12, 16, 24, 32, 48, 63].iter() {
unsafe {
test_strs_a.push((*i, a64.get_unchecked(..a.len() * i)));
}
}
let group = c.benchmark_group("count_bench_1byte_small");
group_count_bench(group, test_strs_a.iter());
}

fn count_bench_1_s1_small(c: &mut Criterion) {
let mut test_strs_a = vec![];
let a = "a";
let a64 = a.repeat(67);
for i in (2..67).into_iter()
/*
for i in [
2, 3, 5, 7, 9, 13, 16, 17, 19, 25, 32, 33, 35, 36, 49, 64, 65, 67,
]
.iter()
*/
{
unsafe {
test_strs_a.push((i - 1, a64.get_unchecked(1..a.len() * i)));
}
}
let group = c.benchmark_group("count_bench_1byte_s1_small");
group_count_bench(group, test_strs_a.iter());
}

fn count_bench_1_large(c: &mut Criterion) {
let mut test_strs_a = vec![];
let a = "a";
let a10000 = a.repeat(10000);
test_strs_a.push((1, a));
test_strs_a.push((10000, &a10000));
for i in [10, 100, 1000].iter() {
unsafe {
test_strs_a.push((*i, a10000.get_unchecked(..a.len() * i)));
}
}
let mut group = c.benchmark_group("count_bench_1byte");
let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic);
group.plot_config(plot_config);
group_count_bench(group, test_strs_a.iter());
}

fn count_bench_1_large_avx_iter(c: &mut Criterion) {
let mut test_strs_a = vec![];
let a = "a";
let a10000 = a.repeat(10000);
test_strs_a.push((1, a));
test_strs_a.push((10000, &a10000));
for i in [10, 100, 1000].iter() {
unsafe {
test_strs_a.push((*i, a10000.get_unchecked(..a.len() * i)));
}
}
let mut group = c.benchmark_group("count_bench_1byte_avx_iter");
let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic);
group.plot_config(plot_config);
group_count_bench_avx(group, test_strs_a.iter());
}

fn count_bench_3_large(c: &mut Criterion) {
let mut test_strs_s = vec![];
let s = "錆";
let s10000 = s.repeat(10000);
test_strs_s.push((1, s));
test_strs_s.push((10000, &s10000));
for i in [10, 100, 1000].iter() {
unsafe {
test_strs_s.push((*i, s10000.get_unchecked(..s.len() * i)));
}
}
let mut group = c.benchmark_group("count_bench_3byte");
let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic);
group.plot_config(plot_config);
group_count_bench(group, test_strs_s.iter());
}

criterion_group!(benches_large, count_bench_1_large, count_bench_3_large);
criterion_group!(benches_avx, count_bench_1_large_avx_iter);
criterion_group!(benches_small, count_bench_1_small, count_bench_1_s1_small);
criterion_group!(benches_small_mix1, count_bench_1_small_mix1);
criterion_main!(benches_avx);
Loading

0 comments on commit 3898296

Please sign in to comment.