Fix leon::ValuesFn: impl for Fn(&str) -> Option<Cow<'static, str>> (#998)

If we impl it for `<'f> Fn(&str) -> Option<Cow<'f, str>> + 'f`, then it would somehow imply it to `'static` when using it in `benches/values.rs` and `benches/others.rs`, thus I decided to simplify it back to only implement it for `'static` string.

Users who wants more flexibility should implement `Values` themselves.

This commit also extracts `benches` as a separate crate in an independent workspace to avoid building criterion and tinytemplate in CI, which makes it much slower as more crates need to be built and criterion actually pulls in clap, and a whole lots of other crates.

In additional to that, it:
 - Impl `leon::Values` for `Arc<T>` & `Rc<T>` where T: `Values`
 - Enable lto, abort on panic, stripping, set `codege-units` to 1 for `leon/benches`
 - Move into closure of criterion benchmark loop to reduce indirections
    which also reduce sizes of the closure since the Fns used in `ValuesFn`
    are zero-size.
    This also means that the compiler can now assumes `no-alias`.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2023-04-26 13:17:41 +10:00 committed by GitHub
parent c65e1269a0
commit 02fe3d4f27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 525 additions and 230 deletions

View file

@ -8,7 +8,7 @@ rust-version = "1.61.0"
authors = ["Félix Saparelli <felix@passcod.name>"]
edition = "2021"
license = "Apache-2.0 OR MIT"
exclude = ["fuzz"]
exclude = ["fuzz", "benches"]
[dependencies]
clap = { version = "4.2.4", features = ["derive"], optional = true }
@ -19,16 +19,3 @@ thiserror = "1.0.38"
default = ["miette"]
cli = ["dep:clap", "miette?/fancy-no-backtrace"]
miette = ["dep:miette"]
[dev-dependencies]
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
serde = { version = "1.0.160", features = ["derive"] }
tinytemplate = "1.2.1"
[[bench]]
name = "values"
harness = false
[[bench]]
name = "others"
harness = false

1
crates/leon/benches/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target

431
crates/leon/benches/Cargo.lock generated Normal file
View file

@ -0,0 +1,431 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"bitflags",
"clap_lex",
"indexmap",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "leon"
version = "1.0.0"
dependencies = [
"miette",
"thiserror",
]
[[package]]
name = "leon-benches"
version = "0.0.0"
dependencies = [
"criterion",
"leon",
"serde",
"tinytemplate",
]
[[package]]
name = "libc"
version = "0.2.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
[[package]]
name = "miette"
version = "5.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92a992891d5579caa9efd8e601f82e30a1caa79a27a5db075dde30ecb9eab357"
dependencies = [
"miette-derive",
"once_cell",
"thiserror",
"unicode-width",
]
[[package]]
name = "miette-derive"
version = "5.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c65c625186a9bcce6699394bee511e1b1aec689aa7e3be1bf4e996e75834153"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
[[package]]
name = "proc-macro2"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "walkdir"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View file

@ -0,0 +1,30 @@
[package]
name = "leon-benches"
version = "0.0.0"
publish = false
edition = "2021"
[dependencies]
leon = { path = ".." }
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
serde = { version = "1.0.160", features = ["derive"] }
tinytemplate = "1.2.1"
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bench]]
name = "values"
harness = false
[[bench]]
name = "others"
harness = false
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"
strip = "symbols"

View file

@ -7,7 +7,7 @@ use tinytemplate::TinyTemplate;
fn compare_impls(c: &mut Criterion) {
const TEMPLATE: &str = "hello {name}! i am {age} years old. my goal is to {goal}. i like: {flower}, {music}, {animal}, {color}, {food}. i'm drinking {drink}";
fn replace_fn<'s>(key: &'s str) -> Option<Cow<'s, str>> {
fn replace_fn(key: &str) -> Option<Cow<'static, str>> {
Some(Cow::Borrowed(match key {
"name" => "marcus",
"age" => "42",
@ -22,7 +22,7 @@ fn compare_impls(c: &mut Criterion) {
}))
}
#[derive(Serialize)]
#[derive(Copy, Clone, Serialize)]
struct Context<'c> {
name: &'c str,
age: u8,
@ -47,16 +47,16 @@ fn compare_impls(c: &mut Criterion) {
drink: "coffee",
};
c.bench_function("leon", |b| {
b.iter(|| {
c.bench_function("leon", move |b| {
b.iter(move || {
let template = Template::parse(black_box(TEMPLATE)).unwrap();
let output = template.render(&vals(replace_fn)).unwrap();
black_box(output);
})
});
c.bench_function("std, string replaces", |b| {
b.iter(|| {
c.bench_function("std, string replaces", move |b| {
b.iter(move || {
let mut output = black_box(TEMPLATE).to_string();
for (key, value) in [
("name", "marcus"),
@ -75,8 +75,8 @@ fn compare_impls(c: &mut Criterion) {
})
});
c.bench_function("tiny template", |b| {
b.iter(|| {
c.bench_function("tiny template", move |b| {
b.iter(move || {
let mut tt = TinyTemplate::new();
tt.add_template("tmp", black_box(TEMPLATE)).unwrap();
let output = tt.render("tmp", &tt_context).unwrap();

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, collections::HashMap};
use std::{borrow::Cow, collections::HashMap, sync::Arc};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use leon::{vals, Template, Values, ValuesFn};
@ -270,22 +270,25 @@ fn inner_bench<F>(
hashmap: HashMap<&str, &str>,
slice: &[(&str, &str)],
) where
F: for<'s> Fn(&'s str) -> Option<Cow<'s, str>> + Send + 'static,
F: Fn(&str) -> Option<Cow<'static, str>> + Send + Clone + 'static,
{
c.bench_function(&format!("{name}, fn"), |b| {
b.iter(|| {
c.bench_function(&format!("{name}, fn"), move |b| {
let vals = vals.clone();
b.iter(move || {
let template = Template::parse(black_box(template_str)).unwrap();
black_box(template.render(&vals).unwrap());
})
});
c.bench_function(&format!("{name}, hashmap"), |b| {
b.iter(|| {
let hashmap = Arc::new(hashmap);
c.bench_function(&format!("{name}, hashmap"), move |b| {
let hashmap = Arc::clone(&hashmap);
b.iter(move || {
let template = Template::parse(black_box(template_str)).unwrap();
black_box(template.render(&hashmap).unwrap());
})
});
c.bench_function(&format!("{name}, slice"), |b| {
b.iter(|| {
c.bench_function(&format!("{name}, slice"), move |b| {
b.iter(move || {
let template = Template::parse(black_box(template_str)).unwrap();
black_box(template.render(&slice as &dyn Values).unwrap());
})

View file

View file

@ -2,6 +2,9 @@ use std::{
borrow::{Borrow, Cow},
collections::{BTreeMap, HashMap},
hash::{BuildHasher, Hash},
ops::Deref,
rc::Rc,
sync::Arc,
};
pub trait Values {
@ -17,6 +20,24 @@ where
}
}
impl<T> Values for Arc<T>
where
T: Values,
{
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
T::get_value(self.deref(), key)
}
}
impl<T> Values for Rc<T>
where
T: Values,
{
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
T::get_value(self.deref(), key)
}
}
impl<K, V> Values for [(K, V)]
where
K: AsRef<str>,
@ -87,31 +108,41 @@ where
/// Workaround to allow using functions as [`Values`].
///
/// As this isn't constructible you'll want to use [`vals()`] instead.
#[derive(Copy, Clone, Debug)]
pub struct ValuesFn<F> {
inner: F,
}
impl<'s, F> Values for &'s ValuesFn<F>
impl<F> Values for ValuesFn<F>
where
F: Fn(&str) -> Option<Cow<'s, str>> + 's,
F: Fn(&str) -> Option<Cow<'static, str>>,
{
fn get_value(&self, key: &str) -> Option<Cow<'_, str>> {
(self.inner)(key)
}
}
impl<'f, F> From<F> for ValuesFn<F>
/// See doc of [`vals`]
impl<F> From<F> for ValuesFn<F>
where
F: Fn(&str) -> Option<Cow<'f, str>> + 'f,
F: Fn(&str) -> Option<Cow<'static, str>>,
{
fn from(inner: F) -> Self {
Self { inner }
}
}
/// Workaround to allow using functions as [`Values`].
/// Wraps your function so it implements [`Values`],
/// though it only works if your function returns `Cow<'static, str>`.
///
/// Wraps your function so it implements [`Values`].
/// Since regular function pointers cannot return anything other than
/// `Cow<'static, str>` and closure in Rust currently does not support
/// returning borrows of captured data, supporting anything other than
/// `Cow<'static, str>` for functions is pointless and would only cause
/// more confusion and compile-time errors.
///
/// To return `&str` owned by the values itself, please create a newtype
/// and implement [`Values`] on it manually instead of using this function.
///
/// # Example
///
@ -122,9 +153,9 @@ where
///
/// use_values(&vals(|_| Some("hello".into())));
/// ```
pub const fn vals<'f, F>(func: F) -> ValuesFn<F>
pub const fn vals<F>(func: F) -> ValuesFn<F>
where
F: Fn(&str) -> Option<Cow<'f, str>> + 'f,
F: Fn(&str) -> Option<Cow<'static, str>>,
{
ValuesFn { inner: func }
}