feat(benches): Basic bench implementation (#1871)

This commit is contained in:
david
2021-05-21 15:50:40 -04:00
committed by GitHub
parent 8c0d0739ee
commit 977b3a8a08
36 changed files with 10151 additions and 0 deletions

120
.github/workflows/bench.yml vendored Normal file
View File

@@ -0,0 +1,120 @@
name: benches
on:
push:
branches:
- dev
workflow_dispatch:
jobs:
bench:
strategy:
fail-fast: false
matrix:
rust_version: [stable]
platform:
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
runs-on: ${{ matrix.platform.os }}
steps:
- uses: actions/checkout@v2
- name: install nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
components: rust-src
target: ${{ matrix.platform.target }}
- name: setup python
uses: actions/setup-python@v2
with:
python-version: "3.x"
architecture: x64
- name: install depedencies
run: |
python -m pip install --upgrade pip
sudo apt-get update
sudo apt-get install -y webkit2gtk-4.0 libgtksourceview-3.0-dev libappindicator3-dev xvfb
wget https://github.com/sharkdp/hyperfine/releases/download/v1.11.0/hyperfine_1.11.0_amd64.deb
sudo dpkg -i hyperfine_1.11.0_amd64.deb
pip install memory_profiler
- name: Get current date
run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- name: Cache cargo registry
uses: actions/cache@v2.1.4
with:
path: ~/.cargo/registry
# Add date to the cache to keep it up to date
key: ${{ matrix.platform }}-nightly-cargo-registry-${{ hashFiles('**/Cargo.toml') }}-${{ env.CURRENT_DATE }}
# Restore from outdated cache for speed
restore-keys: |
${{ matrix.platform }}-nightly-cargo-registry-${{ hashFiles('**/Cargo.toml') }}
${{ matrix.platform }}-nightly-cargo-registry-
- name: Cache cargo index
uses: actions/cache@v2.1.4
with:
path: ~/.cargo/git
# Add date to the cache to keep it up to date
key: ${{ matrix.platform }}-nightly-cargo-index-${{ hashFiles('**/Cargo.toml') }}-${{ env.CURRENT_DATE }}
# Restore from outdated cache for speed
restore-keys: |
${{ matrix.platform }}-nightly-cargo-index-${{ hashFiles('**/Cargo.toml') }}
${{ matrix.platform }}-nightly-cargo-index-
- name: Cache core cargo target
uses: actions/cache@v2
with:
path: target
# Add date to the cache to keep it up to date
key: ${{ matrix.platform }}-nightly-cargo-core-${{ hashFiles('core/**/Cargo.toml') }}-${{ env.CURRENT_DATE }}
# Restore from outdated cache for speed
restore-keys: |
${{ matrix.platform }}-nightly-cargo-core-${{ hashFiles('core/**/Cargo.toml') }}
${{ matrix.platform }}-nightly-cargo-core-
- name: cache cargo `tooling/bench/tests` target
uses: actions/cache@v2
with:
path: tooling/bench/tests/target
# Add date to the cache to keep it up to date
key: ubuntu-latest-nightly-cargo-benches-${{ hashFiles('tooling/bench/tests/Cargo.lock') }}-${{ env.CURRENT_DATE }}
# Restore from outdated cache for speed
restore-keys: |
${{ matrix.platform }}-nightly-cargo-benches-${{ hashFiles('tooling/bench/tests/Cargo.lock') }}
${{ matrix.platform }}-nightly-cargo-benches-
- name: run benchmarks
run: |
cargo +nightly build --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target ${{ matrix.platform.target }} --manifest-path tooling/bench/tests/Cargo.toml
xvfb-run --auto-servernum cargo run --manifest-path tooling/bench/Cargo.toml --bin run_benchmark
- name: clone benchmarks_results
if: github.repository == 'tauri-apps/tauri' && github.ref == 'refs/heads/dev'
uses: actions/checkout@v2
with:
token: ${{ secrets.BENCH_PAT }}
path: gh-pages
repository: tauri-apps/benchmark_results
- name: push new benchmarks
if: github.repository == 'tauri-apps/tauri' && github.ref == 'refs/heads/dev'
run: |
cargo run --manifest-path tooling/bench/Cargo.toml --bin build_benchmark_jsons
cd gh-pages
git pull
git config user.name "tauri-bench"
git config user.email "gh.tauribot@gmail.com"
git add .
git commit --message "Update Tauri benchmarks"
git push origin gh-pages
- name: Worker info
run: |
cat /proc/cpuinfo
cat /proc/meminfo

3
.gitignore vendored
View File

@@ -89,3 +89,6 @@ target
# ignore frida handlers
__handlers__/
# benches
gh-pages

282
tooling/bench/Cargo.lock generated Normal file
View File

@@ -0,0 +1,282 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "libc"
version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro2"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
dependencies = [
"bitflags",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "serde"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tauri_bench"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"serde",
"serde_json",
"tempfile",
]
[[package]]
name = "tempfile"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

25
tooling/bench/Cargo.toml Normal file
View File

@@ -0,0 +1,25 @@
workspace = {}
[package]
name = "tauri_bench"
version = "0.1.0"
authors = [ "Tauri Programme within The Commons Conservancy" ]
edition = "2018"
license = "Apache-2.0 OR MIT"
description = "Cross-platform WebView rendering library"
repository = "https://github.com/tauri-apps/wry"
[dependencies]
anyhow = "1.0.40"
chrono = "0.4.19"
tempfile = "3.2.0"
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
[[bin]]
name = "run_benchmark"
path = "src/run_benchmark.rs"
[[bin]]
name = "build_benchmark_jsons"
path = "src/build_benchmark_jsons.rs"

34
tooling/bench/README.md Normal file
View File

@@ -0,0 +1,34 @@
# Tauri Bench
<img align="right" src="https://github.com/tauri-apps/tauri/raw/dev/app-icon.png" height="128" width="128">
[![status](https://img.shields.io/badge/Status-Beta-green.svg)](https://github.com/tauri-apps/tauri)
[![Chat Server](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/SpmNs4S)
[![devto](https://img.shields.io/badge/blog-dev.to-black.svg)](https://dev.to/tauri)
![](https://img.shields.io/github/workflow/status/tauri-apps/tauri/test%20library?label=test%20library
)
[![devto](https://img.shields.io/badge/documentation-site-purple.svg)](https://tauri.studio)
[![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
[![support](https://img.shields.io/badge/sponsor-Opencollective-blue.svg)](https://opencollective.com/tauri)
## About Tauri
Tauri is a polyglot and generic system that is very composable and allows engineers to make a wide variety of applications. It is used for building applications for Desktop Computers using a combination of Rust tools and HTML rendered in a Webview. Apps built with Tauri can ship with any number of pieces of an optional JS API / Rust API so that webviews can control the system via message passing. In fact, developers can extend the default API with their own functionality and bridge the Webview and Rust-based backend easily.
Tauri apps can have custom menus and have tray-type interfaces. They can be updated, and are managed by the user's operating system as expected. They are very small, because they use the system's webview. They do not ship a runtime, since the final binary is compiled from rust. This makes the reversing of Tauri apps not a trivial task.
## This module
This rust module run on CI, provides internal metrics results of Tauri. To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
***_Internal use only_**
## Semver
**tauri** is following [Semantic Versioning 2.0](https://semver.org/).
## Licenses
Code: (c) 2015 - 2021 - The Tauri Programme within The Commons Conservancy.
MIT or MIT/Apache 2.0 where applicable.
Logo: CC-BY-NC-ND
- Original Tauri Logo Designs by [Daniel Thompson-Yvetot](https://github.com/nothingismagick) and [Guillaume Chau](https://github.com/akryum)

View File

@@ -0,0 +1,54 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{fs::File, io::BufReader};
mod utils;
fn main() {
let tauri_data = &utils::tauri_root_path()
.join("gh-pages")
.join("tauri-data.json");
let tauri_recent = &utils::tauri_root_path()
.join("gh-pages")
.join("tauri-recent.json");
// current data
let current_data_buffer = BufReader::new(
File::open(&utils::target_dir().join("bench.json")).expect("Unable to read current data file"),
);
let current_data: utils::BenchResult =
serde_json::from_reader(current_data_buffer).expect("Unable to read current data buffer");
// all data's
let all_data_buffer =
BufReader::new(File::open(&tauri_data).expect("Unable to read all data file"));
let mut all_data: Vec<utils::BenchResult> =
serde_json::from_reader(all_data_buffer).expect("Unable to read all data buffer");
// add current data to alls data
all_data.push(current_data);
// use only latest 20 elements from alls data
let recent: Vec<utils::BenchResult>;
if all_data.len() > 20 {
recent = all_data[all_data.len() - 20..].to_vec();
} else {
recent = all_data.clone();
}
// write json's
utils::write_json(
tauri_data.to_str().expect("Something wrong with tauri_data"),
&serde_json::to_value(&all_data).expect("Unable to build final json (alls)"),
)
.expect(format!("Unable to write {:?}", tauri_data).as_str());
utils::write_json(
tauri_recent
.to_str()
.expect("Something wrong with tauri_recent"),
&serde_json::to_value(&recent).expect("Unable to build final json (recent)"),
)
.expect(format!("Unable to write {:?}", tauri_recent).as_str());
}

View File

@@ -0,0 +1,287 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use anyhow::Result;
use std::{
collections::{HashMap, HashSet},
env,
path::Path,
process::{Command, Stdio},
};
mod utils;
/// The list of the examples of the benchmark name and binary relative path
fn get_all_benchmarks() -> Vec<(String, String)> {
vec![
(
"tauri_hello_world".into(),
format!(
"tests/target/{}/release/bench_helloworld",
utils::get_target()
),
),
(
"tauri_cpu_intensive".into(),
format!(
"tests/target/{}/release/bench_cpu_intensive",
utils::get_target()
),
),
/*
FIXME: require next release of tao
(
"tauri_3mb_transfer".into(),
format!(
"tests/target/{}/release/tauri_3mb_transfer",
utils::get_target()
),
),
*/
]
}
fn run_strace_benchmarks(new_data: &mut utils::BenchResult) -> Result<()> {
use std::io::Read;
let mut thread_count = HashMap::<String, u64>::new();
let mut syscall_count = HashMap::<String, u64>::new();
for (name, example_exe) in get_all_benchmarks() {
let mut file = tempfile::NamedTempFile::new()?;
Command::new("strace")
.args(&[
"-c",
"-f",
"-o",
file.path().to_str().unwrap(),
utils::bench_root_path().join(example_exe).to_str().unwrap(),
])
.stdout(Stdio::inherit())
.spawn()?
.wait()?;
let mut output = String::new();
file.as_file_mut().read_to_string(&mut output)?;
let strace_result = utils::parse_strace_output(&output);
let clone = strace_result.get("clone").map(|d| d.calls).unwrap_or(0) + 1;
let total = strace_result.get("total").unwrap().calls;
thread_count.insert(name.to_string(), clone);
syscall_count.insert(name.to_string(), total);
}
new_data.thread_count = thread_count;
new_data.syscall_count = syscall_count;
Ok(())
}
fn run_max_mem_benchmark() -> Result<HashMap<String, u64>> {
let mut results = HashMap::<String, u64>::new();
for (name, example_exe) in get_all_benchmarks() {
let benchmark_file = utils::target_dir().join(format!("mprof{}_.dat", name));
let benchmark_file = benchmark_file.to_str().unwrap();
let proc = Command::new("mprof")
.args(&[
"run",
"-C",
"-o",
benchmark_file,
utils::bench_root_path().join(example_exe).to_str().unwrap(),
])
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()?;
let proc_result = proc.wait_with_output()?;
println!("{:?}", proc_result);
results.insert(
name.to_string(),
utils::parse_max_mem(&benchmark_file).unwrap(),
);
}
Ok(results)
}
fn rlib_size(target_dir: &std::path::Path, prefix: &str) -> u64 {
let mut size = 0;
let mut seen = std::collections::HashSet::new();
for entry in std::fs::read_dir(target_dir.join("deps")).unwrap() {
let entry = entry.unwrap();
let os_str = entry.file_name();
let name = os_str.to_str().unwrap();
if name.starts_with(prefix) && name.ends_with(".rlib") {
let start = name.split('-').next().unwrap().to_string();
if seen.contains(&start) {
println!("skip {}", name);
} else {
seen.insert(start);
size += entry.metadata().unwrap().len();
println!("check size {} {}", name, size);
}
}
}
assert!(size > 0);
size
}
fn get_binary_sizes(target_dir: &Path) -> Result<HashMap<String, u64>> {
let mut sizes = HashMap::<String, u64>::new();
let wry_size = rlib_size(&target_dir, "libwry");
println!("wry {} bytes", wry_size);
sizes.insert("wry_rlib".to_string(), wry_size);
// add size for all EXEC_TIME_BENCHMARKS
for (name, example_exe) in get_all_benchmarks() {
let meta = std::fs::metadata(example_exe).unwrap();
sizes.insert(name.to_string(), meta.len());
}
Ok(sizes)
}
/// (target OS, target triple)
const TARGETS: &[(&str, &[&str])] = &[
(
"Windows",
&[
"x86_64-pc-windows-gnu",
"i686-pc-windows-gnu",
"i686-pc-windows-msvc",
"x86_64-pc-windows-msvc",
],
),
(
"Linux",
&[
"x86_64-unknown-linux-gnu",
"i686-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
],
),
("macOS", &["x86_64-apple-darwin", "aarch64-apple-darwin"]),
];
fn cargo_deps() -> HashMap<String, usize> {
let mut results = HashMap::new();
for (os, targets) in TARGETS {
for target in *targets {
let mut cmd = Command::new("cargo");
cmd.arg("tree");
cmd.arg("--no-dedupe");
cmd.args(&["--edges", "normal"]);
cmd.args(&["--prefix", "none"]);
cmd.args(&["--target", target]);
cmd.current_dir(&utils::tauri_root_path());
let full_deps = cmd.output().expect("failed to run cargo tree").stdout;
let full_deps = String::from_utf8(full_deps).expect("cargo tree output not utf-8");
let count = full_deps.lines().collect::<HashSet<_>>().len() - 1; // output includes wry itself
// set the count to the highest count seen for this OS
let existing = results.entry(os.to_string()).or_default();
*existing = count.max(*existing);
assert!(count > 10); // sanity check
}
}
results
}
const RESULT_KEYS: &[&str] = &["mean", "stddev", "user", "system", "min", "max"];
fn run_exec_time(target_dir: &Path) -> Result<HashMap<String, HashMap<String, f64>>> {
let benchmark_file = target_dir.join("hyperfine_results.json");
let benchmark_file = benchmark_file.to_str().unwrap();
let mut command = [
"hyperfine",
"--export-json",
benchmark_file,
"--warmup",
"3",
]
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
for (_, example_exe) in get_all_benchmarks() {
command.push(
utils::bench_root_path()
.join(example_exe)
.to_str()
.unwrap()
.to_string(),
);
}
utils::run(&command.iter().map(|s| s.as_ref()).collect::<Vec<_>>());
let mut results = HashMap::<String, HashMap<String, f64>>::new();
let hyperfine_results = utils::read_json(benchmark_file)?;
for ((name, _), data) in get_all_benchmarks().iter().zip(
hyperfine_results
.as_object()
.unwrap()
.get("results")
.unwrap()
.as_array()
.unwrap(),
) {
let data = data.as_object().unwrap().clone();
results.insert(
name.to_string(),
data
.into_iter()
.filter(|(key, _)| RESULT_KEYS.contains(&key.as_str()))
.map(|(key, val)| (key, val.as_f64().unwrap()))
.collect(),
);
}
Ok(results)
}
fn main() -> Result<()> {
println!("Starting tauri benchmark");
let target_dir = utils::target_dir();
env::set_current_dir(&utils::bench_root_path())?;
let mut new_data = utils::BenchResult {
created_at: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
sha1: utils::run_collect(&["git", "rev-parse", "HEAD"])
.0
.trim()
.to_string(),
exec_time: run_exec_time(&target_dir)?,
binary_size: get_binary_sizes(&target_dir)?,
cargo_deps: cargo_deps(),
..Default::default()
};
if cfg!(target_os = "linux") {
run_strace_benchmarks(&mut new_data)?;
new_data.max_memory = run_max_mem_benchmark()?;
}
println!("===== <BENCHMARK RESULTS>");
serde_json::to_writer_pretty(std::io::stdout(), &new_data)?;
println!("\n===== </BENCHMARK RESULTS>");
if let Some(filename) = target_dir.join("bench.json").to_str() {
utils::write_json(filename, &serde_json::to_value(&new_data)?)?;
} else {
eprintln!("Cannot write bench.json, path is invalid");
}
Ok(())
}

194
tooling/bench/src/utils.rs Normal file
View File

@@ -0,0 +1,194 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use anyhow::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{collections::HashMap, fs, io::{BufRead, BufReader}, path::PathBuf, process::{Command, Output, Stdio}};
#[derive(Default, Clone, Serialize, Deserialize, Debug)]
pub struct BenchResult {
pub created_at: String,
pub sha1: String,
pub exec_time: HashMap<String, HashMap<String, f64>>,
pub binary_size: HashMap<String, u64>,
pub max_memory: HashMap<String, u64>,
pub thread_count: HashMap<String, u64>,
pub syscall_count: HashMap<String, u64>,
pub cargo_deps: HashMap<String, usize>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize)]
pub struct StraceOutput {
pub percent_time: f64,
pub seconds: f64,
pub usecs_per_call: Option<u64>,
pub calls: u64,
pub errors: u64,
}
pub fn get_target() -> &'static str {
#[cfg(target_os = "macos")]
return "x86_64-apple-darwin";
#[cfg(target_os = "linux")]
return "x86_64-unknown-linux-gnu";
#[cfg(target_os = "windows")]
return unimplemented!();
}
pub fn target_dir() -> PathBuf {
let target_dir = bench_root_path()
.join("tests")
.join("target")
.join(get_target())
.join("release");
target_dir.into()
}
pub fn bench_root_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
#[allow(dead_code)]
pub fn tauri_root_path() -> PathBuf {
bench_root_path()
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf()
}
#[allow(dead_code)]
pub fn run_collect(cmd: &[&str]) -> (String, String) {
let mut process_builder = Command::new(cmd[0]);
process_builder
.args(&cmd[1..])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let prog = process_builder.spawn().expect("failed to spawn script");
let Output {
stdout,
stderr,
status,
} = prog.wait_with_output().expect("failed to wait on child");
let stdout = String::from_utf8(stdout).unwrap();
let stderr = String::from_utf8(stderr).unwrap();
if !status.success() {
eprintln!("stdout: <<<{}>>>", stdout);
eprintln!("stderr: <<<{}>>>", stderr);
panic!("Unexpected exit code: {:?}", status.code());
}
(stdout, stderr)
}
#[allow(dead_code)]
pub fn parse_max_mem(file_path: &str) -> Option<u64> {
let file = fs::File::open(file_path).unwrap();
let output = BufReader::new(file);
let mut highest: u64 = 0;
// MEM 203.437500 1621617192.4123
for line in output.lines() {
if let Ok(line) = line {
// split line by space
let split = line.split(" ").collect::<Vec<_>>();
if split.len() == 3 {
// mprof generate result in MB
let current_bytes = str::parse::<f64>(split[1]).unwrap() as u64 * 1024 * 1024;
if current_bytes > highest {
highest = current_bytes;
}
}
}
}
fs::remove_file(file_path).unwrap();
if highest > 0 {
return Some(highest);
}
None
}
#[allow(dead_code)]
pub fn parse_strace_output(output: &str) -> HashMap<String, StraceOutput> {
let mut summary = HashMap::new();
let mut lines = output
.lines()
.filter(|line| !line.is_empty() && !line.contains("detached ..."));
let count = lines.clone().count();
if count < 4 {
return summary;
}
let total_line = lines.next_back().unwrap();
lines.next_back(); // Drop separator
let data_lines = lines.skip(2);
for line in data_lines {
let syscall_fields = line.split_whitespace().collect::<Vec<_>>();
let len = syscall_fields.len();
let syscall_name = syscall_fields.last().unwrap();
if (5..=6).contains(&len) {
summary.insert(
syscall_name.to_string(),
StraceOutput {
percent_time: str::parse::<f64>(syscall_fields[0]).unwrap(),
seconds: str::parse::<f64>(syscall_fields[1]).unwrap(),
usecs_per_call: Some(str::parse::<u64>(syscall_fields[2]).unwrap()),
calls: str::parse::<u64>(syscall_fields[3]).unwrap(),
errors: if syscall_fields.len() < 6 {
0
} else {
str::parse::<u64>(syscall_fields[4]).unwrap()
},
},
);
}
}
let total_fields = total_line.split_whitespace().collect::<Vec<_>>();
summary.insert(
"total".to_string(),
StraceOutput {
percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
seconds: str::parse::<f64>(total_fields[1]).unwrap(),
usecs_per_call: None,
calls: str::parse::<u64>(total_fields[2]).unwrap(),
errors: str::parse::<u64>(total_fields[3]).unwrap(),
},
);
summary
}
#[allow(dead_code)]
pub fn run(cmd: &[&str]) {
let mut process_builder = Command::new(cmd[0]);
process_builder.args(&cmd[1..]).stdin(Stdio::piped());
let mut prog = process_builder.spawn().expect("failed to spawn script");
let status = prog.wait().expect("failed to wait on child");
if !status.success() {
panic!("Unexpected exit code: {:?}", status.code());
}
}
#[allow(dead_code)]
pub fn read_json(filename: &str) -> Result<Value> {
let f = fs::File::open(filename)?;
Ok(serde_json::from_reader(f)?)
}
#[allow(dead_code)]
pub fn write_json(filename: &str, value: &Value) -> Result<()> {
let f = fs::File::create(filename)?;
serde_json::to_writer(f, value)?;
Ok(())
}

2884
tooling/bench/tests/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
[workspace]
members = [
"./*/src-tauri/",
]
[profile.release]
panic = "abort"
codegen-units = 1
lto = true
incremental = false
opt-level = "s"

View File

@@ -0,0 +1,7 @@
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
Arial, sans-serif;
margin: auto;
max-width: 38rem;
padding: 2rem;
}

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<h1>Calculate prime numbers</h1>
<p></p>
<button type="button" id="start">Start</button>
<p id="status"></p>
<p id="results"></p>
<script src="site.js"></script>
</body>
</html>

View File

@@ -0,0 +1,59 @@
// Create web worker
const THRESHOLD = 10000000;
const worker = new Worker("worker.js");
/** @type {HTMLButtonElement} */
const start = document.getElementById("start");
/** @type {HTMLParagraphElement} */
const status = document.getElementById("status");
const results = document.getElementById("results");
const ITERATIONS = 1;
let resolver;
const onMessage = (message) => {
// Update the UI
let prefix = "[Calculating]";
if (message.data.status === "done") {
// tell tauri that we are done
window.__TAURI__.invoke("tauri", {
__tauriModule: "Process",
message: {
cmd: "exit",
exitCode: 0,
},
});
}
status.innerHTML = `${prefix} Found <code>${message.data.count}</code> prime numbers in <code>${message.data.time}ms</code>`;
if (message.data.status === "done") {
resolver(message.data.time);
}
};
worker.addEventListener("message", onMessage);
const benchmark = () => {
return new Promise((resolve) => {
const startTime = Date.now();
resolver = resolve;
worker.postMessage({ value: THRESHOLD, startTime });
});
};
const calculate = async () => {
let total = 0;
for (let i = 0; i < ITERATIONS; i++) {
const result = await benchmark();
total += result;
}
const average = total / ITERATIONS;
results.innerText = `Average time: ${average}ms`;
};
window.addEventListener("DOMContentLoaded", calculate);

View File

@@ -0,0 +1,45 @@
const isPrime = (number) => {
if (number % 2 === 0 && number > 2) {
return false;
}
let start = 2;
const limit = Math.sqrt(number);
while (start <= limit) {
if (number % start++ < 1) {
return false;
}
}
return number > 1;
};
addEventListener("message", (e) => {
const { startTime } = e.data;
let n = 0;
let total = 0;
const THRESHOLD = e.data.value;
const primes = [];
let previous = startTime;
while (++n <= THRESHOLD) {
if (isPrime(n)) {
primes.push(n);
total++;
const now = Date.now();
if (now - previous > 250) {
previous = now;
postMessage({
status: "calculating",
count: total,
time: Date.now() - startTime,
});
}
}
}
postMessage({ status: "done", count: total, time: Date.now() - startTime });
});

View File

@@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
# These are backup files generated by rustfmt
**/*.rs.bk
config.json
bundle.json

View File

@@ -0,0 +1 @@
../../../.license_template

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
[package]
name = "bench_cpu_intensive"
version = "0.1.0"
description = "A very simple Tauri Appplication"
edition = "2018"
[build-dependencies]
tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../../../../core/tauri", features = [] }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]

View File

@@ -0,0 +1,13 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri_build::{try_build, Attributes, WindowsAttributes};
fn main() {
if let Err(error) = try_build(Attributes::new().windows_attributes(
WindowsAttributes::new().window_icon_path("../../../../../examples/.icons/icon.ico"),
)) {
panic!("error found during tauri-build: {}", error);
}
}

View File

@@ -0,0 +1,14 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,56 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"../../../../../examples/.icons/32x32.png",
"../../../../../examples/.icons/128x128.png",
"../../../../../examples/.icons/128x128@2x.png",
"../../../../../examples/.icons/icon.icns",
"../../../../../examples/.icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": ""
}
},
"allowlist": {
"all": false
},
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline' 'self'"
},
"updater": {
"active": false
}
}
}

View File

@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to Tauri!</title>
</head>
<body>
<h1>Welcome to Tauri!</h1>
<script>
window.addEventListener("DOMContentLoaded", (event) => {
window.__TAURI__.invoke("tauri", {
__tauriModule: "Fs",
message: {
cmd: "readBinaryFile",
path: ".tauri_3mb.json",
options: {
// home folder
dir: 11
}
},
}).then((_data) => {
window.__TAURI__.invoke("tauri", {
__tauriModule: "Process",
message: {
cmd: "exit",
exitCode: 0,
},
});
}).catch((_error) => {
// error
window.__TAURI__.invoke("tauri", {
__tauriModule: "Process",
message: {
cmd: "exit",
exitCode: 1,
},
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
# These are backup files generated by rustfmt
**/*.rs.bk
config.json
bundle.json

View File

@@ -0,0 +1 @@
../../../.license_template

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
[package]
name = "bench_files_transfer"
version = "0.1.0"
description = "A very simple Tauri Appplication"
edition = "2018"
[build-dependencies]
tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../../../../core/tauri", features = [] }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]

View File

@@ -0,0 +1,13 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri_build::{try_build, Attributes, WindowsAttributes};
fn main() {
if let Err(error) = try_build(Attributes::new().windows_attributes(
WindowsAttributes::new().window_icon_path("../../../../../examples/.icons/icon.ico"),
)) {
panic!("error found during tauri-build: {}", error);
}
}

View File

@@ -0,0 +1,14 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,56 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"../../../../../examples/.icons/32x32.png",
"../../../../../examples/.icons/128x128.png",
"../../../../../examples/.icons/128x128@2x.png",
"../../../../../examples/.icons/icon.icns",
"../../../../../examples/.icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": ""
}
},
"allowlist": {
"all": false
},
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline' 'self'"
},
"updater": {
"active": false
}
}
}

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to Tauri!</title>
</head>
<body>
<h1>Welcome to Tauri!</h1>
<script>
window.addEventListener("DOMContentLoaded", (event) => {
window.__TAURI__.invoke("tauri", {
__tauriModule: "Process",
message: {
cmd: "exit",
exitCode: 0,
},
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
# These are backup files generated by rustfmt
**/*.rs.bk
config.json
bundle.json

View File

@@ -0,0 +1 @@
../../../.license_template

View File

@@ -0,0 +1,17 @@
[package]
name = "bench_helloworld"
version = "0.1.0"
description = "A very simple Tauri Appplication"
edition = "2018"
[build-dependencies]
tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../../../../core/tauri", features = [] }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]

View File

@@ -0,0 +1,13 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri_build::{try_build, Attributes, WindowsAttributes};
fn main() {
if let Err(error) = try_build(Attributes::new().windows_attributes(
WindowsAttributes::new().window_icon_path("../../../../../examples/.icons/icon.ico"),
)) {
panic!("error found during tauri-build: {}", error);
}
}

View File

@@ -0,0 +1,14 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,56 @@
{
"build": {
"distDir": "../public",
"devPath": "../public",
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"../../../../../examples/.icons/32x32.png",
"../../../../../examples/.icons/128x128.png",
"../../../../../examples/.icons/128x128@2x.png",
"../../../../../examples/.icons/icon.icns",
"../../../../../examples/.icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": ""
}
},
"allowlist": {
"all": false
},
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline' 'self'"
},
"updater": {
"active": false
}
}
}