derive: add benchmark

This PR adds the crate "rinja_derive_standalone", which is just like
"rinja_derive", though not a "proc_macro". This way we can easily expose
it's internals for testing and benchmarking.

Right now, the PR is more or less a prove of concept, and it probably
needs a handful more useful benchmark use cases to be worth the hassle.
This commit is contained in:
René Kijewski 2024-06-18 18:28:30 +02:00
parent a6e98d5ef6
commit 1ce549d260
13 changed files with 126 additions and 30 deletions

View File

@ -32,7 +32,7 @@ jobs:
strategy:
matrix:
package: [
rinja, rinja_actix, rinja_axum, rinja_derive, rinja_escape,
rinja, rinja_actix, rinja_axum, rinja_derive, rinja_derive_standalone, rinja_escape,
rinja_parser, rinja_rocket, rinja_warp, testing,
]
runs-on: ubuntu-latest

View File

@ -4,6 +4,7 @@ members = [
"rinja_actix",
"rinja_axum",
"rinja_derive",
"rinja_derive_standalone",
"rinja_escape",
"rinja_parser",
"rinja_rocket",

View File

@ -2,11 +2,12 @@ use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc;
use crate::config::Config;
use crate::{CompileError, FileInfo};
use parser::node::{BlockDef, Macro};
use parser::{Node, Parsed, WithSpan};
use crate::config::Config;
use crate::{CompileError, FileInfo};
pub(crate) struct Heritage<'a> {
pub(crate) root: &'a Context<'a>,
pub(crate) blocks: BlockAncestry<'a>,

View File

@ -4,12 +4,12 @@ use std::rc::Rc;
use std::str::FromStr;
use mime::Mime;
use parser::{Node, Parsed, Syntax};
use quote::ToTokens;
use syn::punctuated::Punctuated;
use crate::config::{get_template_source, Config};
use crate::CompileError;
use parser::{Node, Parsed, Syntax};
pub(crate) struct TemplateInput<'a> {
pub(crate) ast: &'a syn::DeriveInput,

View File

@ -1,38 +1,57 @@
#![deny(elided_lifetimes_in_paths)]
#![deny(unreachable_pub)]
mod config;
mod generator;
mod heritage;
mod input;
#[cfg(test)]
mod tests;
use std::collections::HashMap;
use std::fmt;
use std::path::Path;
use proc_macro::TokenStream;
use proc_macro2::Span;
use parser::{generate_error_info, strip_common, ErrorInfo, ParseError};
mod config;
use config::{read_config_file, Config};
mod generator;
use generator::{Generator, MapChain};
mod heritage;
use heritage::{Context, Heritage};
mod input;
use input::{Print, TemplateArgs, TemplateInput};
#[cfg(test)]
mod tests;
use parser::{generate_error_info, strip_common, ErrorInfo, ParseError};
use proc_macro2::{Span, TokenStream};
#[cfg(not(feature = "__standalone"))]
macro_rules! pub_if_standalone {
(pub $($tt:tt)*) => {
$($tt)*
}
}
#[cfg(feature = "__standalone")]
macro_rules! pub_if_standalone {
($($tt:tt)*) => {
$($tt)*
}
}
#[cfg(not(feature = "__standalone"))]
#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
match build_template(&ast) {
Ok(source) => source.parse().unwrap(),
Err(e) => {
let mut e = e.into_compile_error();
if let Ok(source) = build_skeleton(&ast) {
let source: TokenStream = source.parse().unwrap();
e.extend(source);
pub fn derive_template(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
derive_template2(input.into()).into()
}
pub_if_standalone! {
pub fn derive_template2(input: TokenStream) -> TokenStream {
let ast = syn::parse2(input).unwrap();
match build_template(&ast) {
Ok(source) => source.parse().unwrap(),
Err(e) => {
let mut e = e.into_compile_error();
if let Ok(source) = build_skeleton(&ast) {
let source: TokenStream = source.parse().unwrap();
e.extend(source);
}
e
}
e
}
}
}
@ -138,9 +157,7 @@ impl CompileError {
}
fn into_compile_error(self) -> TokenStream {
syn::Error::new(self.span, self.msg)
.to_compile_error()
.into()
syn::Error::new(self.span, self.msg).to_compile_error()
}
}

View File

@ -1,7 +1,8 @@
// Files containing tests for generated code.
//! Files containing tests for generated code.
use std::fmt::Write;
use crate::build_template;
use std::fmt::Write;
#[test]
fn check_if_let() {

View File

@ -0,0 +1,42 @@
[package]
name = "rinja_derive_standalone"
version = "0.13.0"
description = "Procedural macro package for Rinja"
homepage = "https://github.com/rinja-rs/rinja"
repository = "https://github.com/rinja-rs/rinja"
license = "MIT/Apache-2.0"
workspace = ".."
readme = "README.md"
edition = "2021"
rust-version = "1.65"
[features]
default = ["__standalone"]
__standalone = []
config = ["dep:serde", "dep:basic-toml"]
humansize = []
urlencode = []
serde_json = []
num-traits = []
with-actix-web = []
with-axum = []
with-rocket = []
with-warp = []
[dependencies]
parser = { package = "rinja_parser", version = "0.3", path = "../rinja_parser" }
mime = "0.3"
mime_guess = "2"
proc-macro2 = "1"
quote = "1"
serde = { version = "1.0", optional = true, features = ["derive"] }
syn = "2"
basic-toml = { version = "0.1.1", optional = true }
[dev-dependencies]
criterion = "0.5"
[[bench]]
name = "derive-template"
harness = false
required-features = ["__standalone"]

View File

@ -0,0 +1 @@
../LICENSE-APACHE

View File

@ -0,0 +1 @@
../LICENSE-MIT

View File

@ -0,0 +1,5 @@
This crate embeds the source of `rinja_derive`, but is not a `proc_macro`.
This way we can more easily access the internals of the crate.
To run the benchmark, execute `cargo bench` in this folder, or
`cargo bench -p rinja_derive_standalone` in the project root.

View File

@ -0,0 +1,25 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use quote::quote;
criterion_main!(benches);
criterion_group!(benches, functions);
fn functions(c: &mut Criterion) {
c.bench_function("hello_world", hello_world);
}
fn hello_world(b: &mut criterion::Bencher<'_>) {
let ts = quote! {
#[derive(Template)]
#[template(
source = "<html><body><h1>Hello, {{user}}!</h1></body></html>",
ext = "html"
)]
struct Hello<'a> {
user: &'a str,
}
};
b.iter(|| {
rinja_derive_standalone::derive_template2(black_box(&ts).clone());
})
}

1
rinja_derive_standalone/src Symbolic link
View File

@ -0,0 +1 @@
../rinja_derive/src/

View File

@ -0,0 +1 @@
../rinja_derive/templates/