snapshot of https://github.com/tauri-apps/tauri.git (dev, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Audit / audit-js (push) Failing after 0s
bench / bench (map[os:ubuntu-latest runner:xvfb-run --auto-servernum target:x86_64-unknown-linux-gnu], nightly) (push) Failing after 0s
covector version or publish / run-integration-tests (ubuntu-latest) (push) Failing after 0s
Audit / audit-rust (push) Failing after 0s
test `@tauri-apps/cli` / test (ubuntu-latest) (push) Failing after 0s
test `tauri-cli` / test (map[os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:ubuntu-latest target:x86_64-unknown-linux-gnu toolchain:1.90]) (push) Failing after 0s
test core / test (map[args:--no-default-features key:no-default], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
deploy schema worker / deploy (push) Failing after 0s
lint rust / clippy (map[cargo:cargo include: os:ubuntu-latest target:x86_64-unknown-linux-gnu]) (push) Failing after 0s
lint rust / clippy (map[cargo:cross include:--package api os:ubuntu-latest target:aarch64-linux-android]) (push) Failing after 0s
supply chain health status / check rust dependencies with cargo vet (push) Failing after 0s
test `@tauri-apps/api` e2e (mobile) / android (push) Failing after 0s
test `@tauri-apps/api` e2e / test (map[name:Linux os:ubuntu-latest]) (push) Failing after 0s
test core / test (map[args:--all-features key:all], map[cargo:cross command:build os:ubuntu-latest target:aarch64-linux-android toolchain:1.90]) (push) Failing after 0s
Udeps / changes (push) Failing after 0s
Udeps / setup (push) Has been skipped
Udeps / udeps (push) Has been skipped
bench / bench (map[os:macos-latest runner: target:aarch64-apple-darwin], nightly) (push) Has been cancelled
bench / bench (map[os:windows-latest runner: target:x86_64-pc-windows-msvc], nightly) (push) Has been cancelled
covector version or publish / run-integration-tests (macos-latest) (push) Has been cancelled
covector version or publish / run-integration-tests (windows-latest) (push) Has been cancelled
covector version or publish / version-or-publish (push) Has been cancelled
test `@tauri-apps/cli` / test (macos-latest) (push) Has been cancelled
test `tauri-cli` / test (map[args:--no-default-features --features native-tls-vendored os:windows-latest restore-only:true target:aarch64-pc-windows-msvc]) (push) Has been cancelled
test `tauri-cli` / test (map[os:macos-latest target:x86_64-apple-darwin]) (push) Has been cancelled
test `tauri-cli` / test (map[os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:macos-latest target:aarch64-apple-darwin]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include: os:windows-latest target:x86_64-pc-windows-msvc]) (push) Has been cancelled
lint rust / clippy (map[cargo:cargo include:--package api os:macos-latest target:aarch64-apple-ios]) (push) Has been cancelled
test `@tauri-apps/api` e2e (mobile) / ios (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:Windows os:windows-latest]) (push) Has been cancelled
test `@tauri-apps/api` e2e / test (map[name:macOS os:macos-latest]) (push) Has been cancelled
test `@tauri-apps/cli` / test (windows-latest) (push) Has been cancelled
test core / test (map[args:--all-features key:all], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:build os:macos-latest target:aarch64-apple-ios toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:macos-14 target:aarch64-apple-darwin toolchain:1.90]) (push) Has been cancelled
test core / test (map[args:--no-default-features key:no-default], map[cargo:cargo command:test os:windows-latest target:x86_64-pc-windows-msvc toolchain:1.90]) (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-23 17:00:42 +05:00
commit feda81bf2f
1178 changed files with 156404 additions and 0 deletions

27
bench/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "tauri_bench"
version = "0.1.0"
authors = ["Tauri Programme within The Commons Conservancy"]
edition = "2024"
rust-version = "1.90"
license = "Apache-2.0 OR MIT"
description = "Cross-platform WebView rendering library"
repository = "https://github.com/tauri-apps/wry"
[dependencies]
anyhow = "1"
time = { version = "0.3", features = ["formatting"] }
tempfile = "3"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
[[bin]]
name = "run_benchmark"
path = "src/run_benchmark.rs"
[[bin]]
name = "build_benchmark_jsons"
path = "src/build_benchmark_jsons.rs"
[lints]
workspace = true

38
bench/README.md Normal file
View File

@@ -0,0 +1,38 @@
# Tauri Bench
<img align="right" src="https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png" height="128" width="128">
[![status](https://img.shields.io/badge/Status-beta-green.svg)](https://github.com/tauri-apps/tauri)
[![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri)
[![test core](https://img.shields.io/github/actions/workflow/status/tauri-apps/tauri/test-core.yml?label=test%20core&logo=github)](https://github.com/tauri-apps/tauri/actions/workflows/test-core.yml)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftauri-apps%2Ftauri?ref=badge_shield)
[![Chat Server](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/SpmNs4S)
[![website](https://img.shields.io/badge/website-tauri.app-purple.svg)](https://tauri.app)
[![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-Open%20Collective-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,69 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! This Rust binary runs on CI and provides internal metrics results of Tauri. To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
//!
//! ***_Internal use only_**
#![doc(
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
)]
// file is used by multiple binaries
#![allow(dead_code)]
use std::{fs::File, io::BufReader};
mod utils;
fn main() {
let platform = if cfg!(target_os = "macos") {
"macos"
} else if cfg!(target_os = "windows") {
"windows"
} else {
"linux"
};
let tauri_data = &utils::tauri_root_path()
.join("gh-pages")
.join(format!("tauri-data-{platform}.json"));
let tauri_recent = &utils::tauri_root_path()
.join("gh-pages")
.join(format!("tauri-recent-{platform}.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 all data
all_data.push(current_data);
// use only latest 20 elements from all data
let recent: Vec<utils::BenchResult> = if all_data.len() > 20 {
all_data[all_data.len() - 20..].to_vec()
} else {
all_data.clone()
};
// write json's
utils::write_json(
tauri_data,
&serde_json::to_value(&all_data).expect("Unable to build final json (all)"),
)
.unwrap_or_else(|_| panic!("Unable to write {}", tauri_data.display()));
utils::write_json(
tauri_recent,
&serde_json::to_value(recent).expect("Unable to build final json (recent)"),
)
.unwrap_or_else(|_| panic!("Unable to write {}", tauri_recent.display()));
}

394
bench/src/run_benchmark.rs Normal file
View File

@@ -0,0 +1,394 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! This Rust binary runs on CI and provides internal metrics results of Tauri.
//! To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
//!
//! ***_Internal use only_***
#![doc(
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
)]
use anyhow::{Context, Result};
use std::{
collections::{HashMap, HashSet},
env,
path::Path,
process::{Command, Stdio},
};
use time::format_description::well_known::Rfc3339;
mod utils;
/// The list of examples for benchmarks
fn get_all_benchmarks(target: &str) -> Vec<(String, String)> {
let extension = if cfg!(windows) { ".exe" } else { "" };
vec![
(
"tauri_hello_world".into(),
format!("../target/{target}/release/bench_helloworld{extension}"),
),
(
"tauri_cpu_intensive".into(),
format!("../target/{target}/release/bench_cpu_intensive{extension}"),
),
(
"tauri_3mb_transfer".into(),
format!("../target/{target}/release/bench_files_transfer{extension}"),
),
]
}
fn run_strace_benchmarks(new_data: &mut utils::BenchResult, target: &str) -> 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(target) {
let mut file = tempfile::NamedTempFile::new()
.context("failed to create temporary file for strace output")?;
let exe_path = utils::bench_root_path().join(&example_exe);
let exe_path_str = exe_path
.to_str()
.context("executable path contains invalid UTF-8")?;
let temp_path_str = file
.path()
.to_str()
.context("temporary file path contains invalid UTF-8")?;
Command::new("strace")
.args(["-c", "-f", "-o", temp_path_str, exe_path_str])
.stdout(Stdio::inherit())
.spawn()
.context("failed to spawn strace process")?
.wait()
.context("failed to wait for strace process")?;
let mut output = String::new();
file
.as_file_mut()
.read_to_string(&mut output)
.context("failed to read strace output")?;
let strace_result = utils::parse_strace_output(&output);
// Count clone/clone3 syscalls as thread creation indicators
let clone_calls = strace_result.get("clone").map(|d| d.calls).unwrap_or(0)
+ strace_result.get("clone3").map(|d| d.calls).unwrap_or(0);
if let Some(total) = strace_result.get("total") {
thread_count.insert(name.clone(), clone_calls);
syscall_count.insert(name, total.calls);
}
}
new_data.thread_count = thread_count;
new_data.syscall_count = syscall_count;
Ok(())
}
fn run_max_mem_benchmark(target: &str) -> Result<HashMap<String, u64>> {
let mut results = HashMap::<String, u64>::new();
for (name, example_exe) in get_all_benchmarks(target) {
let benchmark_file = utils::target_dir().join(format!("mprof{name}_.dat"));
let benchmark_file_str = benchmark_file
.to_str()
.context("benchmark file path contains invalid UTF-8")?;
let exe_path = utils::bench_root_path().join(&example_exe);
let exe_path_str = exe_path
.to_str()
.context("executable path contains invalid UTF-8")?;
let proc = Command::new("mprof")
.args(["run", "-C", "-o", benchmark_file_str, exe_path_str])
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.with_context(|| format!("failed to spawn mprof for benchmark {name}"))?;
let proc_result = proc
.wait_with_output()
.with_context(|| format!("failed to wait for mprof {name}"))?;
if !proc_result.status.success() {
eprintln!(
"mprof failed for {name}: {}",
String::from_utf8_lossy(&proc_result.stderr)
);
}
if let Some(mem) = utils::parse_max_mem(benchmark_file_str)
.with_context(|| format!("failed to parse mprof data for {name}"))?
{
results.insert(name, mem);
}
}
Ok(results)
}
fn rlib_size(target_dir: &std::path::Path, library: &str) -> Result<u64> {
let prefix = format!("lib{library}");
let mut size = 0;
let mut seen = HashSet::new();
let build_dir = target_dir.join("build").join(library);
for entry in std::fs::read_dir(&build_dir).with_context(|| {
format!(
"failed to read target build directory: {}",
build_dir.display()
)
})? {
let entry = entry.context("failed to read directory entry")?;
let out_path = entry.path().join("out");
for file in std::fs::read_dir(&out_path).with_context(|| {
format!(
"failed to read target build output directory: {}",
out_path.display()
)
})? {
let file = file.context("failed to read build output directory entry")?;
let name = file.file_name().to_string_lossy().to_string();
if name.starts_with(&prefix) && name.ends_with(".rlib") {
let start = name.split('-').next().unwrap();
if seen.insert(start.to_string()) {
size += file
.metadata()
.context("failed to read file metadata")?
.len();
}
}
}
}
if size == 0 {
anyhow::bail!(
"no rlib files found for prefix {prefix} in {}",
build_dir.display()
);
}
Ok(size)
}
fn get_binary_sizes(target_dir: &Path, target: &str) -> Result<HashMap<String, u64>> {
let mut sizes = HashMap::<String, u64>::new();
let wry_size = rlib_size(target_dir, "wry")?;
sizes.insert("wry_rlib".to_string(), wry_size);
for (name, example_exe) in get_all_benchmarks(target) {
let exe_path = utils::bench_root_path().join(&example_exe);
let meta = std::fs::metadata(&exe_path)
.with_context(|| format!("failed to read metadata for {}", exe_path.display()))?;
sizes.insert(name, 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());
match cmd.output() {
Ok(output) if output.status.success() => {
let full_deps = String::from_utf8_lossy(&output.stdout);
let count = full_deps
.lines()
.collect::<HashSet<_>>()
.len()
.saturating_sub(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);
if count <= 10 {
eprintln!("Warning: dependency count for {target} seems low: {count}");
}
}
Ok(output) => {
eprintln!(
"cargo tree failed for {target}: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Err(e) => {
eprintln!("Failed to run cargo tree for {target}: {e}");
}
}
}
}
results
}
const RESULT_KEYS: &[&str] = &["mean", "stddev", "user", "system", "min", "max"];
fn run_exec_time(target: &str) -> Result<HashMap<String, HashMap<String, f64>>> {
let target_dir = utils::target_dir();
let benchmark_file = target_dir.join("hyperfine_results.json");
let benchmark_file_str = benchmark_file
.to_str()
.context("benchmark file path contains invalid UTF-8")?;
let mut command = vec![
"hyperfine",
"--export-json",
benchmark_file_str,
"--show-output",
"--warmup",
"3",
// It seems like if we run them back to back,
// the execution time will get longer and longer for some reason on macOS and Windows
"--prepare",
"sleep 1",
];
if cfg!(target_os = "windows") {
// For `sleep 1` to work
// hyperfine uses cmd by default and it would fail with
// 'Input redirection is not supported, exiting the process immediately.'
command.push("--shell");
command.push("powershell");
}
let benchmarks = get_all_benchmarks(target);
let mut benchmark_paths = Vec::new();
for (_, example_exe) in &benchmarks {
let exe_path = utils::bench_root_path().join(example_exe);
let exe_path_str = exe_path
.to_str()
.context("executable path contains invalid UTF-8")?;
benchmark_paths.push(exe_path_str.to_string());
}
for path in &benchmark_paths {
command.push(path.as_str());
}
utils::run(&command)?;
let mut results = HashMap::<String, HashMap<String, f64>>::new();
let hyperfine_results = utils::read_json(benchmark_file_str)?;
if let Some(results_array) = hyperfine_results
.as_object()
.and_then(|obj| obj.get("results"))
.and_then(|val| val.as_array())
{
for ((name, _), data) in benchmarks.iter().zip(results_array.iter()) {
if let Some(data_obj) = data.as_object() {
let filtered_data: HashMap<String, f64> = data_obj
.iter()
.filter(|(key, _)| RESULT_KEYS.contains(&key.as_str()))
.filter_map(|(key, val)| val.as_f64().map(|v| (key.clone(), v)))
.collect();
results.insert(name.clone(), filtered_data);
}
}
}
Ok(results)
}
fn main() -> Result<()> {
let json_3mb = utils::home_path().join(".tauri_3mb.json");
if !json_3mb.exists() {
println!("Downloading test data...");
utils::download_file(
"https://github.com/lemarier/tauri-test/releases/download/v2.0.0/json_3mb.json",
json_3mb,
)
.context("failed to download test data")?;
}
println!("Starting tauri benchmark");
let target_dir = utils::target_dir();
let target = utils::get_target();
env::set_current_dir(utils::bench_root_path())
.context("failed to set working directory to bench root")?;
println!("Running execution time benchmarks...");
let exec_time = run_exec_time(target)?;
println!("Getting binary sizes...");
let binary_size = get_binary_sizes(&target_dir, target)?;
println!("Analyzing cargo dependencies...");
let cargo_deps = cargo_deps();
let mut new_data = utils::BenchResult {
created_at: time::OffsetDateTime::now_utc().format(&Rfc3339).unwrap(),
sha1: {
let output = utils::run_collect(&["git", "rev-parse", "HEAD"])?;
output.0.trim().to_string()
},
exec_time,
binary_size,
cargo_deps,
..Default::default()
};
if cfg!(target_os = "linux") {
println!("Running Linux-specific benchmarks...");
run_strace_benchmarks(&mut new_data, target)?;
new_data.max_memory = run_max_mem_benchmark(target)?;
}
println!("===== <BENCHMARK RESULTS>");
serde_json::to_writer_pretty(std::io::stdout(), &new_data)
.context("failed to serialize benchmark results")?;
println!("\n===== </BENCHMARK RESULTS>");
let bench_file = target_dir.join("bench.json");
utils::write_json(&bench_file, &serde_json::to_value(&new_data)?)
.context("failed to write benchmark results to file")?;
println!("Results written to: {}", bench_file.display());
Ok(())
}

275
bench/src/utils.rs Normal file
View File

@@ -0,0 +1,275 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! Utility functions for benchmarking tasks in the Tauri project.
//!
//! This module provides helpers for:
//! - Paths to project directories and targets
//! - Running and collecting process outputs
//! - Parsing memory profiler (`mprof`) and syscall profiler (`strace`) outputs
//! - JSON read/write utilities
//! - File download utilities (via `curl` or file copy)
use anyhow::{Context, Result, bail};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{
collections::HashMap,
fs,
io::{BufRead, BufReader},
path::{Path, PathBuf},
process::{Command, Output, Stdio},
};
/// Holds the results of a benchmark run.
#[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>,
}
/// Represents a single line of parsed `strace` output.
#[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,
}
/// Get the compilation target triple for the current platform.
pub fn get_target() -> &'static str {
#[cfg(target_os = "macos")]
return if cfg!(target_arch = "aarch64") {
"aarch64-apple-darwin"
} else {
"x86_64-apple-darwin"
};
#[cfg(target_os = "ios")]
return if cfg!(target_arch = "aarch64") {
"aarch64-apple-ios"
} else {
"x86_64-apple-ios"
};
#[cfg(target_os = "linux")]
return "x86_64-unknown-linux-gnu";
#[cfg(target_os = "windows")]
return "x86_64-pc-windows-msvc";
}
/// Get the `target/release` directory path for benchmarks.
pub fn target_dir() -> PathBuf {
bench_root_path()
.join("..")
.join("target")
.join(get_target())
.join("release")
}
/// Get the root path of the current benchmark crate.
pub fn bench_root_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
/// Get the home directory of the current user.
pub fn home_path() -> PathBuf {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux"))]
{
PathBuf::from(std::env::var("HOME").unwrap_or_default())
}
#[cfg(target_os = "windows")]
{
PathBuf::from(std::env::var("USERPROFILE").unwrap_or_default())
}
}
/// Get the root path of the Tauri repository.
pub fn tauri_root_path() -> PathBuf {
bench_root_path().parent().map(|p| p.to_path_buf()).unwrap()
}
/// Run a command and collect its stdout and stderr as strings.
/// Returns an error if the command fails or exits with a non-zero status.
pub fn run_collect(cmd: &[&str]) -> Result<(String, String)> {
let output: Output = Command::new(cmd[0])
.args(&cmd[1..])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.with_context(|| format!("failed to execute command: {cmd:?}"))?;
if !output.status.success() {
bail!(
"Command {:?} exited with {:?}\nstdout:\n{}\nstderr:\n{}",
cmd,
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
Ok((
String::from_utf8_lossy(&output.stdout).to_string(),
String::from_utf8_lossy(&output.stderr).to_string(),
))
}
/// Parse a memory profiler (`mprof`) output file and return the maximum
/// memory usage in bytes. Returns `None` if no values are found.
pub fn parse_max_mem(file_path: &str) -> Result<Option<u64>> {
let file = fs::File::open(file_path)
.with_context(|| format!("failed to open mprof output file {file_path}"))?;
let output = BufReader::new(file);
let mut highest: u64 = 0;
for line in output.lines().map_while(Result::ok) {
let split: Vec<&str> = line.split(' ').collect();
if split.len() == 3 {
if let Ok(mb) = split[1].parse::<f64>() {
let current_bytes = (mb * 1024.0 * 1024.0) as u64;
highest = highest.max(current_bytes);
}
}
}
// Best-effort cleanup
let _ = fs::remove_file(file_path);
Ok(if highest > 0 { Some(highest) } else { None })
}
/// Parse the output of `strace -c` and return a summary of syscalls.
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: Vec<&str> = line.split_whitespace().collect();
let len = syscall_fields.len();
if let Some(&syscall_name) = syscall_fields.last() {
if (5..=6).contains(&len) {
let output = StraceOutput {
percent_time: syscall_fields[0].parse().unwrap_or(0.0),
seconds: syscall_fields[1].parse().unwrap_or(0.0),
usecs_per_call: syscall_fields[2].parse().ok(),
calls: syscall_fields[3].parse().unwrap_or(0),
errors: if len < 6 {
0
} else {
syscall_fields[4].parse().unwrap_or(0)
},
};
summary.insert(syscall_name.to_string(), output);
}
}
}
let total_fields: Vec<&str> = total_line.split_whitespace().collect();
let total = match total_fields.len() {
5 => StraceOutput {
percent_time: total_fields[0].parse().unwrap_or(0.0),
seconds: total_fields[1].parse().unwrap_or(0.0),
usecs_per_call: None,
calls: total_fields[2].parse().unwrap_or(0),
errors: total_fields[3].parse().unwrap_or(0),
},
6 => StraceOutput {
percent_time: total_fields[0].parse().unwrap_or(0.0),
seconds: total_fields[1].parse().unwrap_or(0.0),
usecs_per_call: total_fields[2].parse().ok(),
calls: total_fields[3].parse().unwrap_or(0),
errors: total_fields[4].parse().unwrap_or(0),
},
_ => {
panic!("Unexpected total field count: {}", total_fields.len());
}
};
summary.insert("total".to_string(), total);
summary
}
/// Run a command and wait for completion.
/// Returns an error if the command fails.
pub fn run(cmd: &[&str]) -> Result<()> {
let status = Command::new(cmd[0])
.args(&cmd[1..])
.stdin(Stdio::piped())
.status()
.with_context(|| format!("failed to execute command: {cmd:?}"))?;
if !status.success() {
bail!("Command {:?} exited with {:?}", cmd, status.code());
}
Ok(())
}
/// Read a JSON file into a [`serde_json::Value`].
pub fn read_json(filename: &str) -> Result<Value> {
let f =
fs::File::open(filename).with_context(|| format!("failed to open JSON file {filename}"))?;
Ok(serde_json::from_reader(f)?)
}
/// Write a [`serde_json::Value`] into a JSON file.
pub fn write_json(filename: &Path, value: &Value) -> Result<()> {
let f = fs::File::create(filename)
.with_context(|| format!("failed to create JSON file {}", filename.display()))?;
serde_json::to_writer(f, value)?;
Ok(())
}
/// Download a file from either a local path or an HTTP/HTTPS URL.
/// Falls back to copying the file if the URL does not start with http/https.
pub fn download_file(url: &str, filename: PathBuf) -> Result<()> {
if !url.starts_with("http:") && !url.starts_with("https:") {
fs::copy(url, &filename).with_context(|| format!("failed to copy from {url}"))?;
return Ok(());
}
println!("Downloading {url}");
let status = Command::new("curl")
.arg("-L")
.arg("-s")
.arg("-o")
.arg(&filename)
.arg(url)
.status()
.with_context(|| format!("failed to execute curl for {url}"))?;
if !status.success() {
bail!("curl failed with exit code {:?}", status.code());
}
if !filename.exists() {
bail!("expected file {:?} to exist after download", filename);
}
Ok(())
}

View File

@@ -0,0 +1,8 @@
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,57 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// 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__.core.invoke('app_completed_successfully')
}
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,49 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
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,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

View File

@@ -0,0 +1,19 @@
[package]
name = "bench_cpu_intensive"
version = "0.1.0"
description = "A very simple Tauri Application"
edition = "2024"
rust-version = "1.90"
[build-dependencies]
tauri-build = { workspace = true, default-features = true, features = [
"codegen",
] }
[dependencies]
serde_json = "1"
serde = { version = "1", features = ["derive"] }
tauri = { workspace = true, default-features = true }
[lints]
workspace = true

View File

@@ -0,0 +1,7 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,18 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// The benchmark can't use this or we can't measure the command time
// #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[tauri::command]
fn app_completed_successfully() {
std::process::exit(0);
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![app_completed_successfully])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "../../../../crates/tauri-schema-generator/schemas/config.schema.json",
"identifier": "com.tauri.dev",
"build": {
"frontendDist": "../public"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: http://ipc.localhost"
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"../../../../examples/.icons/32x32.png",
"../../../../examples/.icons/128x128.png",
"../../../../examples/.icons/128x128@2x.png",
"../../../../examples/.icons/icon.icns",
"../../../../examples/.icons/icon.ico"
]
}
}

View File

@@ -0,0 +1,30 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<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__.core
.invoke('read_file')
.then((_data) => {
// success
window.__TAURI__.core.invoke('app_should_close', {
exitCode: 0
})
})
.catch((_error) => {
// error
window.__TAURI__.core.invoke('app_should_close', {
exitCode: 1
})
})
})
</script>
</body>
</html>

View File

@@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

View File

@@ -0,0 +1,19 @@
[package]
name = "bench_files_transfer"
version = "0.1.0"
description = "A very simple Tauri Application"
edition = "2024"
rust-version = "1.90"
[build-dependencies]
tauri-build = { workspace = true, default-features = true, features = [
"codegen",
] }
[dependencies]
serde_json = "1"
serde = { version = "1", features = ["derive"] }
tauri = { workspace = true, default-features = true }
[lints]
workspace = true

View File

@@ -0,0 +1,7 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,31 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// The benchmark can't use this or we can't measure the command time
// #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::fs::read;
use tauri::{AppHandle, Manager, Runtime, command, ipc::Response, path::BaseDirectory};
#[command]
fn app_should_close(exit_code: i32) {
std::process::exit(exit_code);
}
#[command]
async fn read_file<R: Runtime>(app: AppHandle<R>) -> Result<Response, String> {
let path = app
.path()
.resolve(".tauri_3mb.json", BaseDirectory::Home)
.map_err(|e| e.to_string())?;
let contents = read(path).map_err(|e| e.to_string())?;
Ok(Response::new(contents))
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![app_should_close, read_file])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "../../../../crates/tauri-schema-generator/schemas/config.schema.json",
"identifier": "com.tauri.dev",
"build": {
"frontendDist": "../public"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: http://ipc.localhost"
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"../../../../examples/.icons/32x32.png",
"../../../../examples/.icons/128x128.png",
"../../../../examples/.icons/128x128@2x.png",
"../../../../examples/.icons/icon.icns",
"../../../../examples/.icons/icon.ico"
]
}
}

View File

@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<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__.core.invoke('app_loaded_successfully')
)
</script>
</body>
</html>

View File

@@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

View File

@@ -0,0 +1,19 @@
[package]
name = "bench_helloworld"
version = "0.1.0"
description = "A very simple Tauri Application"
edition = "2024"
rust-version = "1.90"
[build-dependencies]
tauri-build = { workspace = true, default-features = true, features = [
"codegen",
] }
[dependencies]
serde_json = "1"
serde = { version = "1", features = ["derive"] }
tauri = { workspace = true, default-features = true }
[lints]
workspace = true

View File

@@ -0,0 +1,7 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,18 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// The benchmark can't use this or we can't measure the command time
// #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[tauri::command]
fn app_loaded_successfully() {
std::process::exit(0);
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![app_loaded_successfully])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -0,0 +1,33 @@
{
"$schema": "../../../../crates/tauri-schema-generator/schemas/config.schema.json",
"identifier": "com.tauri.dev",
"build": {
"frontendDist": "../public",
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: http://ipc.localhost"
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"../../../../examples/.icons/32x32.png",
"../../../../examples/.icons/128x128.png",
"../../../../examples/.icons/128x128@2x.png",
"../../../../examples/.icons/icon.icns",
"../../../../examples/.icons/icon.ico"
]
}
}