mirror of https://github.com/yewstack/yew
Benchmarks completely redone (#2352)
This commit is contained in:
parent
1c367a7b6f
commit
4be9308b84
|
@ -1,94 +1,234 @@
|
|||
name: Benchmark
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
if: >-
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'performance') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'performance'))
|
||||
branches: [master]
|
||||
types: [labeled, synchronize]
|
||||
|
||||
workflow_dispatch:
|
||||
branches: [master]
|
||||
permissions:
|
||||
# deployments permission to deploy GitHub pages website
|
||||
deployments: write
|
||||
# contents permission to update benchmark contents in gh-pages branch
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
results:
|
||||
runs-on: ubuntu-latest
|
||||
needs: benchmark
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- run: |
|
||||
touch results.json
|
||||
echo '${{ needs.benchmark.outputs.results }}' >> results.json
|
||||
|
||||
# gh-pages branch is updated and pushed automatically with extracted benchmark data
|
||||
- name: Store benchmark result
|
||||
uses: benchmark-action/github-action-benchmark@v1
|
||||
with:
|
||||
name: "Yew master branch benchmarks (Lower is better)"
|
||||
tool: "customSmallerIsBetter"
|
||||
output-file-path: results.json
|
||||
gh-pages-branch: "gh-pages"
|
||||
# Access token to deploy GitHub Pages branch
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Push and deploy GitHub pages branch automatically
|
||||
comment-always: true
|
||||
alert-threshold: "150%"
|
||||
comment-on-alert: true
|
||||
alert-comment-cc-users: "@yewstack/yew"
|
||||
# Don't push to gh-pages if its a pull request
|
||||
auto-push: ${{ github.event_name != 'pull_request' }}
|
||||
save-data-file: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
results: ${{ steps.results.outputs.stdout }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: yewstack/js-framework-benchmark
|
||||
path: "./yew"
|
||||
|
||||
- name: Configure benchmark
|
||||
run: |
|
||||
replace="yew = { git = \"${REPO_HTML_URL}\", branch = \"${BRANCH}\" }"
|
||||
input=$(cat frameworks/keyed/yew/Cargo.toml)
|
||||
output=$(echo "$input" | sed -e "s@yew = .*}@$replace@g")
|
||||
if [[ "$input" == "$output" ]]; then
|
||||
echo "ERROR: failed to configure Cargo.toml"
|
||||
exit 1
|
||||
fi
|
||||
echo "$output" > frameworks/keyed/yew/Cargo.toml
|
||||
echo "$output"
|
||||
env:
|
||||
# REPO_HTML_URL: ${{ github.event.pull_request.head.repo.html_url }}
|
||||
REPO_HTML_URL: https://github.com/voidpumpkin/yew.git
|
||||
# HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
BRANCH: master
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: krausest/js-framework-benchmark
|
||||
path: "./js-framework-benchmark"
|
||||
|
||||
- name: Setup ChromeDriver
|
||||
uses: nanasess/setup-chromedriver@master
|
||||
|
||||
- name: Setup Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- uses: actions-rs/install@v0.1
|
||||
- uses: jetli/wasm-pack-action@v0.3.0
|
||||
with:
|
||||
crate: wasm-bindgen-cli
|
||||
version: latest
|
||||
use-tool-cache: true
|
||||
|
||||
- uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: wasm-pack
|
||||
version: latest
|
||||
use-tool-cache: true
|
||||
|
||||
- uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: https
|
||||
version: latest
|
||||
use-tool-cache: true
|
||||
|
||||
- name: Start Server
|
||||
run: http -p 8080 &
|
||||
version: "latest"
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 16
|
||||
|
||||
- name: npm Install
|
||||
run: |
|
||||
npm install
|
||||
(cd webdriver-ts && npm install)
|
||||
(cd webdriver-ts-results && npm install)
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
with:
|
||||
working-directory: yew
|
||||
|
||||
- name: Build
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-benchmark-${{ hashFiles('js-framework-benchmark/package-lock.json') }}-${{ hashFiles('js-framework-benchmark/webdriver-ts/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-benchmark-
|
||||
${{ runner.os }}
|
||||
|
||||
- name: save yew-struct setup
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir yew-struct-setup
|
||||
cp -r js-framework-benchmark/frameworks/keyed/yew/* yew-struct-setup/
|
||||
cd yew-struct-setup/bundled-dist
|
||||
rm -rf ./*
|
||||
# Will be enabled after https://github.com/krausest/js-framework-benchmark/pull/985 gets merged
|
||||
# - name: save yew-hooks setup
|
||||
# shell: bash
|
||||
# run: |
|
||||
# mkdir yew-hooks-setup
|
||||
# cp -r js-framework-benchmark/frameworks/keyed/yew-hooks/* yew-hooks-setup/
|
||||
# cd yew-hooks-setup/bundled-dist
|
||||
# rm -rf ./*
|
||||
|
||||
- name: replace framework version in yew
|
||||
shell: bash
|
||||
run: |
|
||||
replace=" \"frameworkVersion\": \"\""
|
||||
input=$(cat yew-struct-setup/package.json)
|
||||
output=$(echo "$input" | sed -e "s@\"frameworkVersion\": .*\"@$replace@g")
|
||||
if [[ "$input" == "$output" ]]; then
|
||||
echo "ERROR: failed to configure framework version"
|
||||
exit 1
|
||||
fi
|
||||
echo "$output" > yew-struct-setup/package.json
|
||||
echo "$output"
|
||||
|
||||
# Will be enabled after https://github.com/krausest/js-framework-benchmark/pull/985 gets merged
|
||||
# - name: replace framework version in yew-hooks
|
||||
# shell: bash
|
||||
# run: |
|
||||
# replace=" \"frameworkVersion\": \"\""
|
||||
# input=$(cat yew-hooks-setup/package.json)
|
||||
# output=$(echo "$input" | sed -e "s@\"frameworkVersion\": .*\"@$replace@g")
|
||||
# if [[ "$input" == "$output" ]]; then
|
||||
# echo "ERROR: failed to configure framework version"
|
||||
# exit 1
|
||||
# fi
|
||||
# echo "$output" > yew-hooks-setup/package.json
|
||||
# echo "$output"
|
||||
|
||||
- name: delete all frameworks
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark/frameworks/keyed
|
||||
rm -rf ./*
|
||||
cd ../non-keyed
|
||||
rm -rf ./*
|
||||
|
||||
- name: create framework folders
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark/frameworks/keyed
|
||||
mkdir yew-struct
|
||||
# Will be enabled after https://github.com/krausest/js-framework-benchmark/pull/985 gets merged
|
||||
# mkdir yew-hooks
|
||||
|
||||
- name: copy necessary framework files
|
||||
shell: bash
|
||||
run: |
|
||||
cp -r yew-struct-setup/* js-framework-benchmark/frameworks/keyed/yew-struct/
|
||||
# Will be enabled after https://github.com/krausest/js-framework-benchmark/pull/985 gets merged
|
||||
# cp -r yew-hooks-setup/* js-framework-benchmark/frameworks/keyed/yew-hooks/
|
||||
|
||||
- name: build benchmark-struct app
|
||||
shell: bash
|
||||
run: |
|
||||
cd yew/tools/benchmark-struct
|
||||
npm ci
|
||||
npm run build-prod-without-tools-install
|
||||
|
||||
- name: build benchmark-hooks app
|
||||
shell: bash
|
||||
run: |
|
||||
cd yew/tools/benchmark-hooks
|
||||
npm ci
|
||||
npm run build-prod-without-tools-install
|
||||
|
||||
- name: move dist files
|
||||
shell: bash
|
||||
run: |
|
||||
mv yew/tools/benchmark-struct/bundled-dist js-framework-benchmark/frameworks/keyed/yew-struct/
|
||||
# Will be enabled after https://github.com/krausest/js-framework-benchmark/pull/985 gets merged
|
||||
# mv yew/tools/benchmark-hooks/bundled-dist js-framework-benchmark/frameworks/keyed/yew-hooks/
|
||||
|
||||
- name: js-framework-benchmark npm ci
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark
|
||||
npm ci
|
||||
|
||||
- name: js-framework-benchmark npm start
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark
|
||||
npm start &
|
||||
|
||||
- name: js-framework-benchmark/webdriver-ts npm ci
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark/webdriver-ts
|
||||
npm ci
|
||||
npm install chromedriver --chromedriver-force-download
|
||||
|
||||
- name: js-framework-benchmark/webdriver-ts npm run compile
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark/webdriver-ts
|
||||
npm run compile
|
||||
|
||||
- name: js-framework-benchmark npm run build-prod
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark
|
||||
npm run build-prod
|
||||
(cd webdriver-ts && npm run build-prod)
|
||||
|
||||
- name: Benchmark
|
||||
run: npm run bench -- --headless
|
||||
|
||||
- name: Results
|
||||
run: npm run results
|
||||
|
||||
- name: Log results
|
||||
- name: js-framework-benchmark/webdriver-ts npm run bench
|
||||
shell: bash
|
||||
run: |
|
||||
msg=$(cd results_diff && cargo run)
|
||||
echo "$msg"
|
||||
cd js-framework-benchmark/webdriver-ts
|
||||
npm run bench -- --headless
|
||||
|
||||
- name: transform results into json
|
||||
shell: bash
|
||||
run: |
|
||||
cd js-framework-benchmark/webdriver-ts/results
|
||||
touch temp.txt
|
||||
echo "[" >> temp.txt
|
||||
for filename in *.json; do cat ${filename} >> temp.txt; echo "," >> temp.txt; done
|
||||
sed -i '$ s/.$//' temp.txt #remove trailing comma
|
||||
echo "]" >> temp.txt
|
||||
mv temp.txt results.json
|
||||
|
||||
- name: Build process-benchmark-results
|
||||
shell: bash
|
||||
run: |
|
||||
cd yew
|
||||
cargo build --release -p process-benchmark-results
|
||||
|
||||
- name: transform results to be fit for display benchmark-action/github-action-benchmark@v1
|
||||
uses: mathiasvr/command-output@v1
|
||||
id: results
|
||||
with:
|
||||
run: cat js-framework-benchmark/webdriver-ts/results/results.json | ./yew/target/release/process-benchmark-results
|
||||
|
|
|
@ -35,6 +35,9 @@ members = [
|
|||
|
||||
# Tools
|
||||
"tools/changelog",
|
||||
"tools/process-benchmark-results",
|
||||
"tools/benchmark-struct",
|
||||
"tools/benchmark-hooks",
|
||||
]
|
||||
exclude = [
|
||||
# Tools
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
/target
|
||||
/bundled-dist
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "js-framework-benchmark-yew-hooks"
|
||||
version = "1.0.0"
|
||||
authors = ["Julius Lungys <juliuslungys@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
getrandom = { version = "0.2.1", features = ["js"] }
|
||||
wasm-bindgen = "0.2.78"
|
||||
web-sys = { version = "0.3.55", features = ["Window"]}
|
||||
yew = "0.19.3"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ['-O4']
|
|
@ -0,0 +1,6 @@
|
|||
Copied from https://github.com/krausest/js-framework-benchmark
|
||||
|
||||
It should ideally stay/be updated to always match what is on https://github.com/krausest/js-framework-benchmark
|
||||
Except the fixes required to tun using unreleased yew version.
|
||||
|
||||
If you want to improve benchmarks, consider first opening a PR to https://github.com/krausest/js-framework-benchmark and only then sync this package with it.
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Yew-Hooks</title>
|
||||
<link href="/css/currentStyle.css" rel="stylesheet"/>
|
||||
<base href="bundled-dist/"></base>
|
||||
</head>
|
||||
<body>
|
||||
<div id='main'></div>
|
||||
<script type="module">
|
||||
import init from './js-framework-benchmark-yew-hooks.js';
|
||||
init('./js-framework-benchmark-yew-hooks_bg.wasm');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,138 @@
|
|||
{
|
||||
"name": "js-framework-benchmark-non-keyed-yew",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"cpr": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz",
|
||||
"integrity": "sha1-uaVQOLfNgaNcF7l2GJW9hJau8eU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.5",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "~0.5.1",
|
||||
"rimraf": "^2.5.4"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.9",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
|
||||
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "js-framework-benchmark-non-keyed-yew-hooks",
|
||||
"version": "1.0.0",
|
||||
"description": "Benchmark for Yew Hooks",
|
||||
"license": "ISC",
|
||||
"js-framework-benchmark": {
|
||||
"frameworkVersion": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"build-prod": "echo This is a no-op. && echo Due to heavy dependencies, the generated javascript is already provided. && echo If you really want to rebuild from source use: && echo npm run build-prod-force",
|
||||
"build-prod-force": "rustup target add wasm32-unknown-unknown && cargo install wasm-pack && npx build-prod-without-tools-install",
|
||||
"build-prod-without-tools-install": "rimraf bundled-dist && wasm-pack build --release --target web --no-typescript --out-name js-framework-benchmark-yew-hooks --out-dir bundled-dist && cpr index.html bundled-dist/index.html && (cd bundled-dist && rimraf .gitignore README.md package.json)"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/krausest/js-framework-benchmark.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^2.6.3",
|
||||
"cpr": "^3.0.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
use rand::prelude::*;
|
||||
use std::cmp::min;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::window;
|
||||
use yew::prelude::*;
|
||||
|
||||
static ADJECTIVES: &[&str] = &[
|
||||
"pretty",
|
||||
"large",
|
||||
"big",
|
||||
"small",
|
||||
"tall",
|
||||
"short",
|
||||
"long",
|
||||
"handsome",
|
||||
"plain",
|
||||
"quaint",
|
||||
"clean",
|
||||
"elegant",
|
||||
"easy",
|
||||
"angry",
|
||||
"crazy",
|
||||
"helpful",
|
||||
"mushy",
|
||||
"odd",
|
||||
"unsightly",
|
||||
"adorable",
|
||||
"important",
|
||||
"inexpensive",
|
||||
"cheap",
|
||||
"expensive",
|
||||
"fancy",
|
||||
];
|
||||
|
||||
static COLOURS: &[&str] = &[
|
||||
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
|
||||
"orange",
|
||||
];
|
||||
|
||||
static NOUNS: &[&str] = &[
|
||||
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
|
||||
"pizza", "mouse", "keyboard",
|
||||
];
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct RowData {
|
||||
id: usize,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl RowData {
|
||||
fn new(id: usize, rng: &mut SmallRng) -> Self {
|
||||
let adjective = *ADJECTIVES.choose(rng).unwrap();
|
||||
let colour = *COLOURS.choose(rng).unwrap();
|
||||
let noun = *NOUNS.choose(rng).unwrap();
|
||||
|
||||
let label = [adjective, colour, noun].join(" ");
|
||||
|
||||
Self { id, label }
|
||||
}
|
||||
}
|
||||
|
||||
enum AppStateAction {
|
||||
Run(usize),
|
||||
Add(usize),
|
||||
Update(usize),
|
||||
Clear,
|
||||
Swap,
|
||||
Remove(usize),
|
||||
Select(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
next_id: usize,
|
||||
selected_id: Option<usize>,
|
||||
rows: Vec<RowData>,
|
||||
rng: SmallRng,
|
||||
}
|
||||
|
||||
impl Default for AppState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
rows: Vec::new(),
|
||||
next_id: 1,
|
||||
selected_id: None,
|
||||
rng: SmallRng::from_entropy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reducible for AppState {
|
||||
type Action = AppStateAction;
|
||||
|
||||
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
|
||||
let mut new_state = self.deref().clone();
|
||||
match action {
|
||||
AppStateAction::Run(amount) => {
|
||||
let rng = &mut new_state.rng;
|
||||
let next_id = new_state.next_id;
|
||||
let update_amount = min(amount, new_state.rows.len());
|
||||
for index in 0..update_amount {
|
||||
new_state.rows[index] = RowData::new(next_id + index, rng);
|
||||
}
|
||||
new_state.rows.extend(
|
||||
(update_amount..amount).map(|index| RowData::new(next_id + index, rng)),
|
||||
);
|
||||
new_state.next_id += amount;
|
||||
}
|
||||
AppStateAction::Add(amount) => {
|
||||
let rng = &mut new_state.rng;
|
||||
let next_id = new_state.next_id;
|
||||
new_state
|
||||
.rows
|
||||
.extend((0..amount).map(|index| RowData::new(next_id + index, rng)));
|
||||
new_state.next_id += amount;
|
||||
}
|
||||
AppStateAction::Update(step) => {
|
||||
for index in (0..new_state.rows.len()).step_by(step) {
|
||||
new_state.rows[index].label += " !!!";
|
||||
}
|
||||
}
|
||||
AppStateAction::Clear => {
|
||||
new_state.rows.clear();
|
||||
}
|
||||
AppStateAction::Swap => {
|
||||
if new_state.rows.len() > 998 {
|
||||
new_state.rows.swap(1, 998);
|
||||
}
|
||||
}
|
||||
AppStateAction::Remove(id) => {
|
||||
if let Some(index) = new_state.rows.iter().position(|row| row.id == id) {
|
||||
new_state.rows.remove(index);
|
||||
}
|
||||
}
|
||||
AppStateAction::Select(id) => {
|
||||
new_state.selected_id = Some(id);
|
||||
}
|
||||
};
|
||||
|
||||
new_state.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
let state = use_reducer(AppState::default);
|
||||
|
||||
let selected_id = state.deref().selected_id;
|
||||
let rows = state.deref().rows.clone();
|
||||
|
||||
let on_run = {
|
||||
let state = state.clone();
|
||||
Callback::from(move |amount| state.dispatch(AppStateAction::Run(amount)))
|
||||
};
|
||||
let on_add = {
|
||||
let state = state.clone();
|
||||
Callback::from(move |amount| state.dispatch(AppStateAction::Add(amount)))
|
||||
};
|
||||
let on_update = {
|
||||
let state = state.clone();
|
||||
Callback::from(move |amount| state.dispatch(AppStateAction::Update(amount)))
|
||||
};
|
||||
let on_clear = {
|
||||
let state = state.clone();
|
||||
Callback::from(move |_| state.dispatch(AppStateAction::Clear))
|
||||
};
|
||||
let on_swap = {
|
||||
let state = state.clone();
|
||||
Callback::from(move |_| state.dispatch(AppStateAction::Swap))
|
||||
};
|
||||
let on_select = {
|
||||
let state = state.clone();
|
||||
Callback::from(move |id| state.dispatch(AppStateAction::Select(id)))
|
||||
};
|
||||
let on_remove = Callback::from(move |id| state.dispatch(AppStateAction::Remove(id)));
|
||||
|
||||
let rows: Html = rows
|
||||
.into_iter()
|
||||
.map(move |row| {
|
||||
let id = row.id;
|
||||
html! {
|
||||
<Row
|
||||
key={id}
|
||||
data={row}
|
||||
selected={selected_id == Some(id)}
|
||||
on_select={on_select.reform(move |_| id)}
|
||||
on_remove={on_remove.reform(move |_| id)}
|
||||
/>
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
html! {
|
||||
<div class="container">
|
||||
<Jumbotron
|
||||
{on_run}
|
||||
{on_add}
|
||||
{on_update}
|
||||
{on_clear}
|
||||
{on_swap}
|
||||
/>
|
||||
<table class="table table-hover table-striped test-data">
|
||||
<tbody id="tbody">
|
||||
{ rows }
|
||||
</tbody>
|
||||
</table>
|
||||
<span class="preloadicon glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
pub struct JumbotronProps {
|
||||
pub on_run: Callback<usize>,
|
||||
pub on_add: Callback<usize>,
|
||||
pub on_update: Callback<usize>,
|
||||
pub on_clear: Callback<()>,
|
||||
pub on_swap: Callback<()>,
|
||||
}
|
||||
|
||||
#[function_component(Jumbotron)]
|
||||
fn jumbotron(props: &JumbotronProps) -> Html {
|
||||
html! {
|
||||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h1>{ "Yew" }</h1>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" id="run" class="btn btn-primary btn-block" onclick={props.on_run.reform(|_| 1_000)}>{ "Create 1,000 rows" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={props.on_run.reform(|_| 10_000)} id="runlots">{ "Create 10,000 rows" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={props.on_add.reform(|_| 1_000)} id="add">{ "Append 1,000 rows" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={props.on_update.reform(|_| 10)} id="update">{ "Update every 10th row" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={props.on_clear.reform(|_| ())} id="clear">{ "Clear" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={props.on_swap.reform(|_| ())} id="swaprows">{ "Swap Rows" }</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
struct RowProps {
|
||||
on_select: Callback<MouseEvent>,
|
||||
on_remove: Callback<MouseEvent>,
|
||||
selected: bool,
|
||||
data: RowData,
|
||||
}
|
||||
|
||||
#[function_component(Row)]
|
||||
fn row(props: &RowProps) -> Html {
|
||||
html! {
|
||||
<tr class={if props.selected { "danger" } else { "" }}>
|
||||
<td class="col-md-1">{ props.data.id }</td>
|
||||
<td class="col-md-4" onclick={props.on_select.clone()}>
|
||||
<a class="lbl">{ props.data.label.clone() }</a>
|
||||
</td>
|
||||
<td class="col-md-1">
|
||||
<a class="remove" onclick={props.on_remove.clone()}>
|
||||
<span class="glyphicon glyphicon-remove remove" aria-hidden="true"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="col-md-6"></td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
let document = window().unwrap().document().unwrap();
|
||||
let mount_el = document.query_selector("#main").unwrap().unwrap();
|
||||
yew::start_app_in_element::<App>(mount_el);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
/target
|
||||
/bundled-dist
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "js-framework-benchmark-yew"
|
||||
version = "1.0.0"
|
||||
authors = ["Isamu Mogi <isamu@leafytree.jp>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
getrandom = { version = "0.2.1", features = ["js"] }
|
||||
wasm-bindgen = "0.2.78"
|
||||
web-sys = { version = "0.3.55", features = ["Window"]}
|
||||
yew = "0.19.3"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = ['-O4']
|
|
@ -0,0 +1,6 @@
|
|||
Copied from https://github.com/krausest/js-framework-benchmark
|
||||
|
||||
It should ideally stay/be updated to always match what is on https://github.com/krausest/js-framework-benchmark
|
||||
Except the fixes required to tun using unreleased yew version.
|
||||
|
||||
If you want to improve benchmarks, consider first opening a PR to https://github.com/krausest/js-framework-benchmark and only then sync this package with it.
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Yew</title>
|
||||
<link href="/css/currentStyle.css" rel="stylesheet"/>
|
||||
<base href="bundled-dist/"></base>
|
||||
</head>
|
||||
<body>
|
||||
<div id='main'></div>
|
||||
<script type="module">
|
||||
import init from './js-framework-benchmark-yew.js';
|
||||
init('./js-framework-benchmark-yew_bg.wasm');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,138 @@
|
|||
{
|
||||
"name": "js-framework-benchmark-non-keyed-yew",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"cpr": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cpr/-/cpr-3.0.1.tgz",
|
||||
"integrity": "sha1-uaVQOLfNgaNcF7l2GJW9hJau8eU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.5",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "~0.5.1",
|
||||
"rimraf": "^2.5.4"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
|
||||
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.9",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
|
||||
"integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "js-framework-benchmark-non-keyed-yew",
|
||||
"version": "1.0.0",
|
||||
"description": "Benchmark for Yew",
|
||||
"license": "ISC",
|
||||
"js-framework-benchmark": {
|
||||
"frameworkVersion": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"build-prod": "echo This is a no-op. && echo Due to heavy dependencies, the generated javascript is already provided. && echo If you really want to rebuild from source use: && echo npm run build-prod-force",
|
||||
"build-prod-force": "rustup target add wasm32-unknown-unknown && cargo install wasm-pack && npx build-prod-without-tools-install",
|
||||
"build-prod-without-tools-install": "rimraf bundled-dist && wasm-pack build --release --target web --no-typescript --out-name js-framework-benchmark-yew --out-dir bundled-dist && cpr index.html bundled-dist/index.html && (cd bundled-dist && rimraf .gitignore README.md package.json)"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/krausest/js-framework-benchmark.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^2.6.3",
|
||||
"cpr": "^3.0.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
use rand::prelude::*;
|
||||
use std::cmp::min;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::window;
|
||||
use yew::prelude::*;
|
||||
|
||||
static ADJECTIVES: &[&str] = &[
|
||||
"pretty",
|
||||
"large",
|
||||
"big",
|
||||
"small",
|
||||
"tall",
|
||||
"short",
|
||||
"long",
|
||||
"handsome",
|
||||
"plain",
|
||||
"quaint",
|
||||
"clean",
|
||||
"elegant",
|
||||
"easy",
|
||||
"angry",
|
||||
"crazy",
|
||||
"helpful",
|
||||
"mushy",
|
||||
"odd",
|
||||
"unsightly",
|
||||
"adorable",
|
||||
"important",
|
||||
"inexpensive",
|
||||
"cheap",
|
||||
"expensive",
|
||||
"fancy",
|
||||
];
|
||||
|
||||
static COLOURS: &[&str] = &[
|
||||
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
|
||||
"orange",
|
||||
];
|
||||
|
||||
static NOUNS: &[&str] = &[
|
||||
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
|
||||
"pizza", "mouse", "keyboard",
|
||||
];
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
struct RowData {
|
||||
id: usize,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl RowData {
|
||||
fn new(id: usize, rng: &mut SmallRng) -> Self {
|
||||
let adjective = *ADJECTIVES.choose(rng).unwrap();
|
||||
let colour = *COLOURS.choose(rng).unwrap();
|
||||
let noun = *NOUNS.choose(rng).unwrap();
|
||||
|
||||
let label = [adjective, colour, noun].join(" ");
|
||||
|
||||
Self { id, label }
|
||||
}
|
||||
}
|
||||
|
||||
struct App {
|
||||
rows: Vec<RowData>,
|
||||
next_id: usize,
|
||||
selected_id: Option<usize>,
|
||||
rng: SmallRng,
|
||||
on_select: Callback<usize>,
|
||||
on_remove: Callback<usize>,
|
||||
}
|
||||
|
||||
enum Msg {
|
||||
Run(usize),
|
||||
Add(usize),
|
||||
Update(usize),
|
||||
Clear,
|
||||
Swap,
|
||||
Remove(usize),
|
||||
Select(usize),
|
||||
}
|
||||
|
||||
impl Component for App {
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
App {
|
||||
rows: Vec::new(),
|
||||
next_id: 1,
|
||||
selected_id: None,
|
||||
rng: SmallRng::from_entropy(),
|
||||
on_select: ctx.link().callback(Msg::Select),
|
||||
on_remove: ctx.link().callback(Msg::Remove),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::Run(amount) => {
|
||||
let rng = &mut self.rng;
|
||||
let next_id = self.next_id;
|
||||
let update_amount = min(amount, self.rows.len());
|
||||
for index in 0..update_amount {
|
||||
self.rows[index] = RowData::new(next_id + index, rng);
|
||||
}
|
||||
self.rows.extend(
|
||||
(update_amount..amount).map(|index| RowData::new(next_id + index, rng)),
|
||||
);
|
||||
self.next_id += amount;
|
||||
}
|
||||
Msg::Add(amount) => {
|
||||
let rng = &mut self.rng;
|
||||
let next_id = self.next_id;
|
||||
self.rows
|
||||
.extend((0..amount).map(|index| RowData::new(next_id + index, rng)));
|
||||
self.next_id += amount;
|
||||
}
|
||||
Msg::Update(step) => {
|
||||
for index in (0..self.rows.len()).step_by(step) {
|
||||
self.rows[index].label += " !!!";
|
||||
}
|
||||
}
|
||||
Msg::Clear => {
|
||||
self.rows.clear();
|
||||
}
|
||||
Msg::Swap => {
|
||||
if self.rows.len() > 998 {
|
||||
self.rows.swap(1, 998);
|
||||
}
|
||||
}
|
||||
Msg::Remove(id) => {
|
||||
if let Some(index) = self.rows.iter().position(|row| row.id == id) {
|
||||
self.rows.remove(index);
|
||||
}
|
||||
}
|
||||
Msg::Select(id) => {
|
||||
self.selected_id = Some(id);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let rows: Html = self
|
||||
.rows
|
||||
.iter()
|
||||
.map(|row| {
|
||||
html! {
|
||||
<Row
|
||||
key={row.id}
|
||||
data={row.clone()}
|
||||
selected={self.selected_id == Some(row.id)}
|
||||
on_select={self.on_select.clone()}
|
||||
on_remove={self.on_remove.clone()}
|
||||
/>
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
html! {
|
||||
<div class="container">
|
||||
<Jumbotron
|
||||
on_run={ctx.link().callback(Msg::Run)}
|
||||
on_add={ctx.link().callback(Msg::Add)}
|
||||
on_update={ctx.link().callback(Msg::Update)}
|
||||
on_clear={ctx.link().callback(|_| Msg::Clear)}
|
||||
on_swap={ctx.link().callback(|_| Msg::Swap)}
|
||||
/>
|
||||
<table class="table table-hover table-striped test-data">
|
||||
<tbody id="tbody">
|
||||
{ rows }
|
||||
</tbody>
|
||||
</table>
|
||||
<span class="preloadicon glyphicon glyphicon-remove" aria-hidden="true"></span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
pub struct JumbotronProps {
|
||||
pub on_run: Callback<usize>,
|
||||
pub on_add: Callback<usize>,
|
||||
pub on_update: Callback<usize>,
|
||||
pub on_clear: Callback<()>,
|
||||
pub on_swap: Callback<()>,
|
||||
}
|
||||
|
||||
pub struct Jumbotron {}
|
||||
|
||||
impl Component for Jumbotron {
|
||||
type Properties = JumbotronProps;
|
||||
type Message = ();
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h1>{ "Yew-Hooks" }</h1>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" id="run" class="btn btn-primary btn-block" onclick={ctx.props().on_run.reform(|_| 1_000)}>{ "Create 1,000 rows" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={ctx.props().on_run.reform(|_| 10_000)} id="runlots">{ "Create 10,000 rows" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={ctx.props().on_add.reform(|_| 1_000)} id="add">{ "Append 1,000 rows" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={ctx.props().on_update.reform(|_| 10)} id="update">{ "Update every 10th row" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={ctx.props().on_clear.reform(|_| ())} id="clear">{ "Clear" }</button>
|
||||
</div>
|
||||
<div class="col-sm-6 smallpad">
|
||||
<button type="button" class="btn btn-primary btn-block" onclick={ctx.props().on_swap.reform(|_| ())} id="swaprows">{ "Swap Rows" }</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
struct RowProps {
|
||||
on_select: Callback<usize>,
|
||||
on_remove: Callback<usize>,
|
||||
selected: bool,
|
||||
data: RowData,
|
||||
}
|
||||
|
||||
struct Row {
|
||||
on_select: Callback<MouseEvent>,
|
||||
on_remove: Callback<MouseEvent>,
|
||||
}
|
||||
|
||||
impl Component for Row {
|
||||
type Properties = RowProps;
|
||||
type Message = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
let id = ctx.props().data.id;
|
||||
Self {
|
||||
on_select: ctx.props().on_select.reform(move |_| id),
|
||||
on_remove: ctx.props().on_remove.reform(move |_| id),
|
||||
}
|
||||
}
|
||||
|
||||
fn changed(&mut self, ctx: &Context<Self>) -> bool {
|
||||
let id = ctx.props().data.id;
|
||||
self.on_select = ctx.props().on_select.reform(move |_| id);
|
||||
self.on_remove = ctx.props().on_remove.reform(move |_| id);
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<tr class={if ctx.props().selected { "danger" } else { "" }}>
|
||||
<td class="col-md-1">{ ctx.props().data.id }</td>
|
||||
<td class="col-md-4" onclick={self.on_select.clone()}>
|
||||
<a class="lbl">{ ctx.props().data.label.clone() }</a>
|
||||
</td>
|
||||
<td class="col-md-1">
|
||||
<a class="remove" onclick={self.on_remove.clone()}>
|
||||
<span class="glyphicon glyphicon-remove remove" aria-hidden="true"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="col-md-6"></td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
let document = window().unwrap().document().unwrap();
|
||||
let mount_el = document.query_selector("#main").unwrap().unwrap();
|
||||
yew::start_app_in_element::<App>(mount_el);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "process-benchmark-results"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1.0"
|
|
@ -0,0 +1,3 @@
|
|||
Processes benchmark results
|
||||
From array of js-framework-benchmark
|
||||
Into array that works for https://github.com/marketplace/actions/continuous-benchmark action
|
|
@ -0,0 +1,33 @@
|
|||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GhActionBenchmark {
|
||||
name: String,
|
||||
unit: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut buffer = "".to_string();
|
||||
io::stdin().read_to_string(&mut buffer)?;
|
||||
|
||||
let input_json: Vec<Value> = serde_json::from_str(buffer.as_str())?;
|
||||
|
||||
let transformed_benchmarks: Vec<GhActionBenchmark> = input_json
|
||||
.into_iter()
|
||||
.map(|v| GhActionBenchmark {
|
||||
name: format!("{} {}", v["framework"], v["benchmark"]).replace("\"", ""),
|
||||
unit: String::default(),
|
||||
value: v["median"].to_string(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let output = serde_json::to_string(&transformed_benchmarks)?;
|
||||
|
||||
io::stdout().write_all(output.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue