Skip to content

Commit adef769

Browse files
zbranieckialerque
authored andcommitted
refactor(intr-memoizer): Move intl_memoizer to capture provider
1 parent f2476cb commit adef769

File tree

7 files changed

+277
-436
lines changed

7 files changed

+277
-436
lines changed

intl-memoizer/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,21 @@ icu_locid.workspace = true
2828
icu_plurals.workspace = true
2929
icu_provider.workspace = true
3030
type-map = "0.5"
31+
hashbrown = "0.14"
3132

3233
[dev-dependencies]
3334
fluent-langneg.workspace = true
35+
criterion.workspace = true
36+
icu_datetime = {version = "1.4", features = ["serde"]}
37+
icu_calendar = "1.4"
38+
icu_decimal = "1.4"
39+
icu_provider_blob = "1.4"
3440

3541
[features]
3642
default = []
3743
sync = ["icu_provider/sync"]
44+
45+
[[bench]]
46+
name = "single"
47+
harness = false
48+

intl-memoizer/benches/single.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use criterion::criterion_group;
2+
use criterion::criterion_main;
3+
use criterion::Criterion;
4+
use criterion::{Bencher, BenchmarkId};
5+
use icu_calendar::DateTime;
6+
use icu_datetime::{options::length::Time, TimeFormatter};
7+
use icu_locid::LanguageIdentifier;
8+
use intl_memoizer::{IntlLangMemoizer, Memoizable};
9+
10+
struct TF(pub TimeFormatter);
11+
12+
use icu_provider_blob::BlobDataProvider;
13+
const ICU4X_DATA: &[u8] = include_bytes!(concat!(
14+
"/Users/zibi/projects/icu-perf/data/icu4x-1.4-datetime.postcard"
15+
));
16+
17+
impl Memoizable for TF {
18+
type Args = (Time,);
19+
20+
type Provider = icu_provider_blob::BlobDataProvider;
21+
22+
/// If the construtor is fallible, than errors can be described here.
23+
type Error = ();
24+
25+
/// This function wires together the `Args` and `Error` type to construct
26+
/// the intl API. In our example, there is
27+
fn construct(
28+
lang: LanguageIdentifier,
29+
args: Self::Args,
30+
provider: Option<&Self::Provider>,
31+
) -> Result<Self, Self::Error> {
32+
Ok(Self(
33+
TimeFormatter::try_new_with_length_with_buffer_provider(
34+
provider.unwrap(),
35+
&lang.into(),
36+
args.0,
37+
)
38+
.unwrap(),
39+
))
40+
}
41+
}
42+
43+
const SETS: usize = 10;
44+
const REPS: usize = 10;
45+
46+
fn construct_lang_bench(c: &mut Criterion) {
47+
let lang: LanguageIdentifier = "en-US".parse().unwrap();
48+
let provider =
49+
BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
50+
51+
c.bench_with_input(
52+
BenchmarkId::new("construct_lang", &lang),
53+
&(lang, provider),
54+
|b, (lang, provider)| {
55+
b.iter(|| {
56+
let _ = IntlLangMemoizer::new(lang.clone(), Some(provider));
57+
});
58+
},
59+
);
60+
}
61+
62+
fn populate_lang(c: &mut Criterion) {
63+
let lang: LanguageIdentifier = "en".parse().unwrap();
64+
65+
let input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
66+
let provider =
67+
BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
68+
let construct_args = (Time::Short,);
69+
70+
c.bench_with_input(
71+
BenchmarkId::new("populate_lang", &lang),
72+
&(construct_args, provider),
73+
|b: &mut Bencher, (construct_args, provider)| {
74+
b.iter(|| {
75+
let memoizer = IntlLangMemoizer::new(lang.clone(), Some(provider));
76+
for _ in 0..SETS {
77+
for _ in 0..REPS {
78+
let _ = memoizer.with_try_get::<TF, _, _>(construct_args, |intl_example| {
79+
intl_example.0.format_to_string(&input)
80+
});
81+
}
82+
}
83+
});
84+
},
85+
);
86+
}
87+
88+
fn without_memoizer(c: &mut Criterion) {
89+
let lang: LanguageIdentifier = "en".parse().unwrap();
90+
let provider =
91+
BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
92+
let construct_args = (Time::Short,);
93+
94+
let input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
95+
96+
c.bench_with_input(
97+
BenchmarkId::new("without_memoizer", &lang),
98+
&(construct_args, provider),
99+
|b: &mut Bencher, (construct_args, provider)| {
100+
b.iter(|| {
101+
for _ in 0..SETS {
102+
for _ in 0..REPS {
103+
let formatter = TimeFormatter::try_new_with_length_with_buffer_provider(
104+
provider,
105+
&lang.clone().into(),
106+
construct_args.0,
107+
)
108+
.unwrap();
109+
let _ = formatter.format(&input);
110+
}
111+
}
112+
});
113+
},
114+
);
115+
}
116+
117+
fn without_memoizer_hoisted(c: &mut Criterion) {
118+
let lang: LanguageIdentifier = "en".parse().unwrap();
119+
let provider =
120+
BlobDataProvider::try_new_from_static_blob(ICU4X_DATA).expect("Failed to load data");
121+
let construct_args = (Time::Short,);
122+
123+
let input = DateTime::try_new_gregorian_datetime(2020, 9, 1, 12, 34, 28).unwrap();
124+
125+
c.bench_with_input(
126+
BenchmarkId::new("without_memoizer_hoisted", &lang),
127+
&(construct_args, provider),
128+
|b: &mut Bencher, (construct_args, provider)| {
129+
b.iter(|| {
130+
for _ in 0..SETS {
131+
let formatter = TimeFormatter::try_new_with_length_with_buffer_provider(
132+
provider,
133+
&lang.clone().into(),
134+
construct_args.0,
135+
)
136+
.unwrap();
137+
for _ in 0..REPS {
138+
let _ = formatter.format(&input);
139+
}
140+
}
141+
});
142+
},
143+
);
144+
}
145+
146+
criterion_group!(
147+
benches,
148+
construct_lang_bench,
149+
populate_lang,
150+
without_memoizer,
151+
without_memoizer_hoisted
152+
);
153+
criterion_main!(benches);

intl-memoizer/src/lang_memoizer.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// use std::collections::HashMap;
2+
use crate::memoizable::Memoizable;
3+
use hashbrown::HashMap;
4+
use icu_locid::LanguageIdentifier;
5+
use std::cell::RefCell;
6+
7+
pub struct IntlLangMemoizer<'dp, DP = ()> {
8+
lang: LanguageIdentifier,
9+
provider: Option<&'dp DP>,
10+
map: RefCell<type_map::TypeMap>,
11+
}
12+
13+
impl<'dp, DP> IntlLangMemoizer<'dp, DP> {
14+
pub fn new(lang: LanguageIdentifier, provider: Option<&'dp DP>) -> Self {
15+
Self {
16+
lang,
17+
provider,
18+
map: Default::default(),
19+
}
20+
}
21+
22+
pub fn with_try_get<I, R, U>(
23+
&self,
24+
construct_args: &I::Args,
25+
callback: U,
26+
) -> Result<R, I::Error>
27+
where
28+
Self: Sized,
29+
I: Memoizable<Provider = DP> + 'static,
30+
U: FnOnce(&I) -> R,
31+
{
32+
let mut map = self.map.borrow_mut();
33+
34+
let cache = map.entry().or_insert_with(HashMap::<I::Args, I>::new);
35+
36+
let (_, e) = cache
37+
.raw_entry_mut()
38+
.from_key(construct_args)
39+
.or_insert_with(|| {
40+
(
41+
construct_args.clone(),
42+
I::construct(self.lang.clone(), construct_args.clone(), self.provider)
43+
.expect("FOO"),
44+
)
45+
});
46+
Ok(callback(e))
47+
}
48+
}

0 commit comments

Comments
 (0)