Skip to content

Commit 0e1b2e5

Browse files
committed
Benchmark memoizer
1 parent 5dda843 commit 0e1b2e5

File tree

3 files changed

+185
-114
lines changed

3 files changed

+185
-114
lines changed

intl-memoizer/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ icu_datetime = {version = "1.4", features = ["serde"]}
4141
icu_calendar = "1.4"
4242
icu_decimal = "1.4"
4343
icu_provider_blob = "1.4"
44+
icu_collator = "1.4"
45+
fixed_decimal = "0.5"
46+
icu_list = { version = "1.4", features = ["serde"]}
4447

4548
[features]
4649
default = []

intl-memoizer/benches/single.rs

+181-113
Original file line numberDiff line numberDiff line change
@@ -3,138 +3,206 @@ use criterion::criterion_main;
33
use criterion::Criterion;
44
use criterion::{Bencher, BenchmarkId};
55
use icu_calendar::DateTime;
6-
use icu_datetime::{options::length::Time, TimeFormatter};
6+
use icu_datetime::{
7+
options::length::{Date, Time},
8+
// DateTimeFormatterOptions,
9+
DateFormatter,
10+
// DateTimeFormatter,
11+
TimeFormatter,
12+
};
13+
// use icu_collator::{Collator, CollatorOptions};
14+
// use icu_decimal::{FixedDecimalFormatter, options::FixedDecimalFormatterOptions};
15+
// use fixed_decimal::FixedDecimal;
16+
use icu_list::{ListFormatter, ListLength};
717
use icu_locid::LanguageIdentifier;
18+
use icu_plurals::{PluralRuleType, PluralRules};
819
use intl_memoizer::{IntlLangMemoizer, Memoizable};
9-
10-
struct TF(pub TimeFormatter);
20+
use std::hint::black_box;
1121

1222
use icu_provider_blob::BlobDataProvider;
1323
const ICU4X_DATA: &[u8] = include_bytes!(concat!(
14-
"/Users/zibi/projects/icu-perf/data/icu4x-1.4-datetime.postcard"
24+
"/Users/zibi/projects/icu-perf/data/icu4x-1.4.postcard"
1525
));
1626

27+
trait Testable {
28+
type Input;
1729

18-
impl Memoizable for TF {
19-
type Args = (Time,);
30+
fn execute(&self, input: Self::Input);
31+
}
2032

21-
type Provider = icu_provider_blob::BlobDataProvider;
33+
macro_rules! define_testable_type {
34+
($name:ident, $type:ident, $args:tt, $constructor:ident, $method:ident, $input:ty) => {
35+
define_testable_type!($name, $type, $args, $constructor);
36+
37+
impl Testable for $name {
38+
type Input = $input;
39+
40+
fn execute(&self, input: Self::Input) {
41+
let _ = self.0.$method(input);
42+
}
43+
}
44+
};
45+
46+
($name:ident, $type:ident, $args:tt, $constructor:ident, $method:ident, ref $input:ty) => {
47+
define_testable_type!($name, $type, $args, $constructor);
48+
49+
impl Testable for $name {
50+
type Input = $input;
51+
52+
fn execute(&self, input: Self::Input) {
53+
let _ = self.0.$method(&input);
54+
}
55+
}
56+
};
57+
58+
($name:ident, $type:ident, $args:tt, $constructor:ident) => {
59+
struct $name($type);
60+
61+
impl Memoizable for $name {
62+
type Args = $args;
63+
type Provider = icu_provider_blob::BlobDataProvider;
64+
type Error = ();
65+
66+
fn construct(
67+
lang: LanguageIdentifier,
68+
args: Self::Args,
69+
provider: Option<&Self::Provider>,
70+
) -> Result<Self, Self::Error> {
71+
Ok(Self(
72+
$type::$constructor(provider.unwrap(), &lang.into(), args.0).unwrap(),
73+
))
74+
}
75+
}
76+
};
77+
}
2278

23-
/// If the construtor is fallible, than errors can be described here.
24-
type Error = ();
79+
define_testable_type!(TF, TimeFormatter, (Time, ), try_new_with_length_with_buffer_provider, format_to_string, ref DateTime<icu_calendar::Gregorian>);
80+
define_testable_type!(DF, DateFormatter, (Date, ), try_new_with_length_with_buffer_provider, format_to_string, ref DateTime<icu_calendar::AnyCalendar>);
81+
// define_testable_type!(DTF, DateTimeFormatter, (DateTimeFormatterOptions, ), try_new_with_length_with_buffer_provider, format_to_string, ref DateTime<icu_calendar::AnyCalendar>);
82+
define_testable_type!(
83+
PR,
84+
PluralRules,
85+
(PluralRuleType,),
86+
try_new_with_buffer_provider,
87+
category_for,
88+
usize
89+
);
90+
// define_testable_type!(
91+
// C,
92+
// Collator,
93+
// (CollatorOptions,),
94+
// try_new_with_buffer_provider,
95+
// compare,
96+
// &str,
97+
// &str,
98+
// );
99+
// define_testable_type!(
100+
// D,
101+
// FixedDecimalFormatter,
102+
// (FixedDecimalFormatterOptions,),
103+
// try_new_with_buffer_provider,
104+
// format_to_string,
105+
// ref FixedDecimal
106+
// );
107+
define_testable_type!(
108+
LF,
109+
ListFormatter,
110+
(ListLength,),
111+
try_new_and_with_length_with_buffer_provider,
112+
format_to_string,
113+
std::vec::IntoIter<String>
114+
);
25115

26-
/// This function wires together the `Args` and `Error` type to construct
27-
/// the intl API. In our example, there is
28-
fn construct(
29-
lang: LanguageIdentifier,
30-
args: Self::Args,
31-
provider: Option<&Self::Provider>,
32-
) -> Result<Self, Self::Error> {
33-
Ok(Self(
34-
TimeFormatter::try_new_with_length_with_buffer_provider(
35-
provider.unwrap(), &lang.into(), args.0).unwrap(),
36-
))
37-
}
116+
macro_rules! without_memoizer_hoisted {
117+
($type:ident, $b:ident, $lang:ident, $provider:ident, $args:expr, $count:expr, $input:expr ) => {
118+
$b.iter(|| {
119+
let intl = $type::construct($lang.clone(), black_box($args), Some($provider)).unwrap();
120+
for _ in 0..$count {
121+
let _ = intl.execute($input);
122+
}
123+
})
124+
};
38125
}
39126

40-
const SETS: usize = 10;
41-
const REPS: usize = 10;
42-
43-
fn construct_lang_bench(c: &mut Criterion) {
44-
let lang: LanguageIdentifier = "en-US".parse().unwrap();
45-
let provider = BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
46-
47-
c.bench_with_input(
48-
BenchmarkId::new("construct_lang", &lang),
49-
&(lang, provider),
50-
|b, (lang, provider)| {
51-
b.iter(|| {
52-
let _ = IntlLangMemoizer::new(lang.clone(), Some(provider));
53-
});
54-
},
55-
);
127+
macro_rules! without_memoizer {
128+
($type:ident, $b:ident, $lang:ident, $provider:ident, $args:expr, $count:expr, $input:expr ) => {
129+
$b.iter(|| {
130+
for _ in 0..$count {
131+
let intl =
132+
$type::construct($lang.clone(), black_box($args), Some($provider)).unwrap();
133+
let _ = intl.execute($input);
134+
}
135+
})
136+
};
56137
}
57138

58-
fn populate_lang(c: &mut Criterion) {
59-
let lang: LanguageIdentifier = "en".parse().unwrap();
60-
61-
let input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
62-
let provider = BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
63-
let construct_args = (Time::Short, );
64-
65-
c.bench_with_input(
66-
BenchmarkId::new("populate_lang", &lang),
67-
&(construct_args, provider),
68-
|b: &mut Bencher, (construct_args, provider)| {
69-
b.iter(|| {
70-
let memoizer = IntlLangMemoizer::new(lang.clone(), Some(provider));
71-
for _ in 0..SETS {
72-
for _ in 0..REPS {
73-
let _ = memoizer.with_try_get::<TF, _, _>(construct_args, |intl_example| {
74-
intl_example.0.format_to_string(&input)
75-
});
76-
}
77-
}
78-
});
79-
},
80-
);
139+
macro_rules! with_memoizer {
140+
($type:ident, $b:ident, $lang:ident, $provider:ident, $args:expr, $count:expr, $input:expr ) => {
141+
$b.iter(|| {
142+
let memoizer =
143+
IntlLangMemoizer::new(black_box($lang.clone()), Some(black_box($provider)));
144+
for _ in 0..$count {
145+
let _ =
146+
memoizer.with_try_get(black_box(&$args), |intl: &$type| intl.execute($input));
147+
}
148+
})
149+
};
81150
}
82151

83-
fn without_memoizer(c: &mut Criterion) {
84-
let lang: LanguageIdentifier = "en".parse().unwrap();
85-
let provider = BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
86-
let construct_args = (Time::Short, );
87-
88-
let input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
89-
90-
c.bench_with_input(
91-
BenchmarkId::new("without_memoizer", &lang),
92-
&(construct_args, provider),
93-
|b: &mut Bencher, (construct_args, provider)| {
94-
b.iter(|| {
95-
for _ in 0..SETS {
96-
for _ in 0..REPS {
97-
let formatter =
98-
TimeFormatter::try_new_with_length_with_buffer_provider(provider, &lang.clone().into(), construct_args.0)
99-
.unwrap();
100-
let _ = formatter.format(&input);
101-
}
102-
}
103-
});
104-
},
105-
);
106-
}
152+
fn bench_variants(c: &mut Criterion) {
153+
let lang: LanguageIdentifier = "und".parse().unwrap();
107154

108-
fn without_memoizer_hoisted(c: &mut Criterion) {
109-
let lang: LanguageIdentifier = "en".parse().unwrap();
110-
let provider = BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
111-
let construct_args = (Time::Short, );
112-
113-
let input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
114-
115-
c.bench_with_input(
116-
BenchmarkId::new("without_memoizer_hoisted", &lang),
117-
&(construct_args, provider),
118-
|b: &mut Bencher, (construct_args, provider)| {
119-
b.iter(|| {
120-
for _ in 0..SETS {
121-
let formatter =
122-
TimeFormatter::try_new_with_length_with_buffer_provider(provider, &lang.clone().into(), construct_args.0)
123-
.unwrap();
124-
for _ in 0..REPS {
125-
let _ = formatter.format(&input);
155+
let provider =
156+
BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
157+
158+
let tf_input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
159+
let tf_args = (Time::Short,);
160+
161+
let pr_input = 5;
162+
let pr_args = (PluralRuleType::Cardinal,);
163+
164+
for component in ["time", "plurals"] {
165+
let mut group = c.benchmark_group(component);
166+
let counts: &[usize] = &[0, 1, 10, 100, 1000, 10000];
167+
168+
for count in counts {
169+
group.bench_with_input(
170+
BenchmarkId::new("without_memoizer_hoisted", count),
171+
&(count, &provider),
172+
|b: &mut Bencher, &(count, provider)| match component {
173+
"time" => {
174+
without_memoizer_hoisted!(TF, b, lang, provider, tf_args, *count, tf_input)
126175
}
127-
}
128-
});
129-
},
130-
);
176+
"plurals" => {
177+
without_memoizer_hoisted!(PR, b, lang, provider, pr_args, *count, pr_input)
178+
}
179+
_ => unreachable!(),
180+
},
181+
);
182+
group.bench_with_input(
183+
BenchmarkId::new("without_memoizer", count),
184+
&(count, &provider),
185+
|b: &mut Bencher, &(count, provider)| match component {
186+
"time" => without_memoizer!(TF, b, lang, provider, tf_args, *count, tf_input),
187+
"plurals" => {
188+
without_memoizer!(PR, b, lang, provider, pr_args, *count, pr_input)
189+
}
190+
_ => unreachable!(),
191+
},
192+
);
193+
group.bench_with_input(
194+
BenchmarkId::new("with_memoizer", count),
195+
&(count, &provider),
196+
|b: &mut Bencher, &(count, provider)| match component {
197+
"time" => with_memoizer!(TF, b, lang, provider, tf_args, *count, tf_input),
198+
"plurals" => with_memoizer!(PR, b, lang, provider, pr_args, *count, pr_input),
199+
_ => unreachable!(),
200+
},
201+
);
202+
}
203+
group.finish();
204+
}
131205
}
132206

133-
criterion_group!(
134-
benches,
135-
construct_lang_bench,
136-
populate_lang,
137-
without_memoizer,
138-
without_memoizer_hoisted
139-
);
207+
criterion_group!(benches, bench_variants,);
140208
criterion_main!(benches);

intl-memoizer/src/lang_memoizer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ impl<'dp, DP> IntlLangMemoizer<'dp, DP> {
4343
.expect("FOO"),
4444
)
4545
});
46-
Ok(callback(&e))
46+
Ok(callback(e))
4747
}
4848
}

0 commit comments

Comments
 (0)