Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add headless benchmarking #44

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .cargo/config

This file was deleted.

30 changes: 30 additions & 0 deletions .github/workflows/everything.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,36 @@ jobs:
- name: Run Browser Tests
run: wasm-pack test --headless --chrome --firefox examples/yew-integration

- name: Run Browser Benchmarks
run: wasm-pack test --headless --chrome --firefox ci/benchmarks --release --no-default-features

- name: "Run Codesize Benchmark: Macros"
working-directory: ci/codesize
env:
TRUNK_BUILD_TARGET: index.macros.html
run: |
trunk build --release
gzip -r --best dist
gzip -r --list dist

- name: "Run Codesize Benchmark: Parser"
working-directory: ci/codesize
env:
TRUNK_BUILD_TARGET: index.parser.html
run: |
trunk build --release
gzip -r --best dist
gzip -r --list dist

- name: "Run Codesize Benchmark: Yew Integration"
working-directory: ci/codesize
env:
TRUNK_BUILD_TARGET: index.yew_integration.html
run: |
trunk build --release
gzip -r --best dist
gzip -r --list dist

publish:
name: Publish to crates.io
runs-on: ubuntu-latest
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ members = [
"packages/stylist-core",
"packages/stylist-macros",

"examples/benchmarks",
"examples/yew-proc-macros",
"examples/yew-integration",
"examples/yew-shadow",
"examples/yew-theme-context",
"examples/yew-theme-hooks",
"examples/use-media-query",

"ci/benchmarks",
"ci/codesize",
]
resolver = "2"

Expand Down
4 changes: 3 additions & 1 deletion Trunk.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ ignore = [
"packages/stylist/target/",
"packages/stylist-core/target/",

"examples/benchmarks/target/",
"examples/yew-integration/target/",
"examples/yew-theme-context/target/",

"ci/benchmarks/target/",
"ci/codesize/target/",
]

[tools]
Expand Down
26 changes: 26 additions & 0 deletions ci/benchmarks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "stylist-benchmarks"
version = "0.1.0"
authors = ["Kaede Hoshikawa <[email protected]>"]
edition = "2018"

[dependencies]
stylist = { path = "../../packages/stylist" }

log = "0.4.14"
console_log = { version = "0.2.0", features = ["color"] }
web-sys = { version = "0.3.54", features = [
"Window",
"Performance",
] }

yew = { git = "https://github.com/yewstack/yew", optional = true }
gloo = { version = "0.3.0", optional = true }

[features]
default = ["with_interface"]
with_interface = ["stylist/yew_integration", "yew", "gloo"]

[dev-dependencies]
wasm-bindgen-test = "0.3.26"
wasm-bindgen = "0.2"
File renamed without changes.
File renamed without changes.
265 changes: 265 additions & 0 deletions ci/benchmarks/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
use gloo::timers::callback::Timeout;
use stylist::yew::Global;
use stylist::{StyleSource, YieldStyle};
use yew::prelude::*;

use crate::runner::BenchmarkResults;

static GLOBAL_STYLE: &str = r#"
html, body {
margin: 0;
padding: 0;
font-family: sans-serif;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
"#;

pub enum BenchMsg {
Step(BenchmarkResults),
}

pub struct Benchmarks {
finished: bool,
results: BenchmarkResults,
}

impl Benchmarks {
fn step(&mut self, ctx: &Context<Benchmarks>) {
let link = ctx.link();
let mut results = self.results.clone();
let cb = link.batch_callback_once(move |_| results.step().then(|| BenchMsg::Step(results)));
Timeout::new(0, move || cb.emit(())).forget();
}
}

impl Component for Benchmarks {
type Message = BenchMsg;
type Properties = ();

fn create(_: &Context<Benchmarks>) -> Self {
Self {
finished: false,
results: Default::default(),
}
}

fn rendered(&mut self, ctx: &Context<Benchmarks>, first_render: bool) {
if first_render {
self.step(ctx);
}
}

fn update(&mut self, ctx: &Context<Benchmarks>, msg: Self::Message) -> bool {
match msg {
Self::Message::Step(next_results) => {
self.results = next_results;
self.step(ctx);
}
}
true
}

fn changed(&mut self, _: &Context<Benchmarks>) -> bool {
false
}

fn view(&self, _: &Context<Benchmarks>) -> Html {
let results = &self.results;
html! {
<div class={self.style()}>
{
if !self.finished {
html!{<div class="running">{"Benchmarking..."}<br />{"The browser may be unresponsive during the benchmark."}</div>}
} else {
html!{<div class="running" />}
}
}

<table>
<thead>
<tr>
<th>{"Benchmark"}</th>
<th>{"Result"}</th>
</tr>
</thead>
<tbody>
<tr>
<th>{"Parse Simple (10,000,000 iterations): "}</th>
<th>{results.parse_simple.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Macro (Literal) Simple (10,000,000 iterations): "}</th>
<th>{results.macro_simple.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Macro (Inline) Simple (10,000,000 iterations): "}</th>
<th>{results.macro_inline_simple.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Parse Simple, No Cache (100,000 iterations): "}</th>
<th>{results.parse_simple_no_cache.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Parse Complex (1,000,000 iterations): "}</th>
<th>{results.parse_complex.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Macro (Literal) Complex (1,000,000 iterations): "}</th>
<th>{results.macro_complex.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Macro (Inline) Complex (1,000,000 iterations): "}</th>
<th>{results.macro_inline_complex.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Parse Complex, No Cache (100,000 iterations): "}</th>
<th>{results.parse_complex_no_cache.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Cached Lookup (1,000,000 iterations): "}</th>
<th>{results.cached_lookup.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Cached Lookup, Big Sheet (100,000 iterations): "}</th>
<th>{results.cached_lookup_big_sheet.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
<tr>
<th>{"Mounting (2,000 iterations): "}</th>
<th>{results.mounting.map(|m| {format!("{:.0}ms", m)}).unwrap_or_else(|| "".to_string())}</th>
</tr>
</tbody>
</table>
</div>
}
}
}

impl YieldStyle for Benchmarks {
fn style_from(&self) -> StyleSource<'static> {
r#"
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;

.running {
height: 50px;
}

table {
border: 1px solid black;
border-collapse: collapse;
}

thead {
font-weight: bold;
background-color: rgb(240, 240, 240);
}

th {
text-align: left;
border: 1px solid black;
border-collapse: collapse;
padding: 5px;
}

tbody th {
font-weight: normal;
}

th:nth-child(1) {
padding-right: 20px;
}

th:nth-child(2) {
padding-left: 20px;
padding-right: 20px;
}

tbody tr:nth-child(even) {
background-color: rgb(240, 240, 240);
}
"#
.into()
}
}

#[derive(Debug, PartialEq)]
pub enum AppMsg {
Start,
}

pub struct App {
started: bool,
}

impl Component for App {
type Message = AppMsg;
type Properties = ();

fn create(_: &Context<App>) -> Self {
Self { started: false }
}

fn update(&mut self, _: &Context<App>, msg: Self::Message) -> bool {
assert_eq!(msg, AppMsg::Start);

self.started = true;

true
}

fn changed(&mut self, _: &Context<App>) -> bool {
false
}

fn view(&self, ctx: &Context<App>) -> Html {
html! {
<>
<Global css={GLOBAL_STYLE} />
<div class={self.style()}>
<h1>{"Stylist Benchmark"}</h1>
{
if self.started {
html!{<Benchmarks />}
} else {
html!{
<>
<div class="before-intro">{"To start benchmarking, please click start:"}</div>
<button onclick={ctx.link().callback(|_| AppMsg::Start)}>
{"Start!"}
</button>
</>
}
}
}
</div>
</>
}
}
}

impl YieldStyle for App {
fn style_from(&self) -> StyleSource<'static> {
r#"
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;

.before-intro {
padding-bottom: 20px;
}

button {
width: 300px;
height: 50px;
font-size: 20px;
}
"#
.into()
}
}
File renamed without changes.
Loading