mobench_sdk/lib.rs
1//! # mobench-sdk
2//!
3//! [](https://crates.io/crates/mobench-sdk)
4//! [](https://docs.rs/mobench-sdk)
5//! [](https://github.com/worldcoin/mobile-bench-rs/blob/main/LICENSE)
6//!
7//! A mobile benchmarking SDK for Rust that enables running performance benchmarks
8//! on real Android and iOS devices via BrowserStack App Automate.
9//!
10//! ## Overview
11//!
12//! `mobench-sdk` provides a simple, declarative API for defining benchmarks that can
13//! run on mobile devices. It handles the complexity of cross-compilation, FFI bindings,
14//! and mobile app packaging automatically.
15//!
16//! ## Quick Start
17//!
18//! ### 1. Add Dependencies
19//!
20//! ```toml
21//! [dependencies]
22//! mobench-sdk = "0.1"
23//! inventory = "0.3" # Required for benchmark registration
24//! ```
25//!
26//! ### 2. Define Benchmarks
27//!
28//! Use the [`#[benchmark]`](macro@benchmark) attribute to mark functions for benchmarking:
29//!
30//! ```ignore
31//! use mobench_sdk::benchmark;
32//!
33//! #[benchmark]
34//! fn my_expensive_operation() {
35//! let result = expensive_computation();
36//! std::hint::black_box(result); // Prevent optimization
37//! }
38//!
39//! #[benchmark]
40//! fn another_benchmark() {
41//! for i in 0..1000 {
42//! std::hint::black_box(i * i);
43//! }
44//! }
45//! ```
46//!
47//! ### 3. Build and Run
48//!
49//! Use the `mobench` CLI to build and run benchmarks:
50//!
51//! ```bash
52//! # Install the CLI
53//! cargo install mobench
54//!
55//! # Build for Android (outputs to target/mobench/)
56//! cargo mobench build --target android
57//!
58//! # Build for iOS
59//! cargo mobench build --target ios
60//!
61//! # Run on BrowserStack
62//! cargo mobench run --target android --function my_expensive_operation \
63//! --iterations 100 --warmup 10 --devices "Google Pixel 7-13.0"
64//! ```
65//!
66//! ## Architecture
67//!
68//! The SDK consists of several components:
69//!
70//! | Module | Description |
71//! |--------|-------------|
72//! | [`timing`] | Core timing infrastructure (always available) |
73//! | [`registry`] | Runtime discovery of `#[benchmark]` functions (requires `full` feature) |
74//! | [`runner`] | Benchmark execution engine (requires `full` feature) |
75//! | [`builders`] | Android and iOS build automation (requires `full` feature) |
76//! | [`codegen`] | Mobile app template generation (requires `full` feature) |
77//! | [`types`] | Common types and error definitions |
78//!
79//! ## Crate Ecosystem
80//!
81//! The mobench ecosystem consists of three published crates:
82//!
83//! - **`mobench-sdk`** (this crate) - Core SDK library with timing harness and build automation
84//! - **[`mobench`](https://crates.io/crates/mobench)** - CLI tool for building and running benchmarks
85//! - **[`mobench-macros`](https://crates.io/crates/mobench-macros)** - `#[benchmark]` proc macro
86//!
87//! Note: The `mobench-runner` crate has been consolidated into this crate as the [`timing`] module.
88//!
89//! ## Feature Flags
90//!
91//! | Feature | Default | Description |
92//! |---------|---------|-------------|
93//! | `full` | Yes | Full SDK with build automation, templates, and registry |
94//! | `runner-only` | No | Minimal timing-only mode for mobile binaries |
95//!
96//! For mobile binaries where binary size matters, use `runner-only`:
97//!
98//! ```toml
99//! [dependencies]
100//! mobench-sdk = { version = "0.1", default-features = false, features = ["runner-only"] }
101//! ```
102//!
103//! ## Programmatic Usage
104//!
105//! You can also use the SDK programmatically:
106//!
107//! ### Using the Builder Pattern
108//!
109//! ```ignore
110//! use mobench_sdk::BenchmarkBuilder;
111//!
112//! let report = BenchmarkBuilder::new("my_benchmark")
113//! .iterations(100)
114//! .warmup(10)
115//! .run()?;
116//!
117//! println!("Mean: {} ns", report.samples.iter()
118//! .map(|s| s.duration_ns)
119//! .sum::<u64>() / report.samples.len() as u64);
120//! ```
121//!
122//! ### Using BenchSpec Directly
123//!
124//! ```ignore
125//! use mobench_sdk::{BenchSpec, run_benchmark};
126//!
127//! let spec = BenchSpec {
128//! name: "my_benchmark".to_string(),
129//! iterations: 50,
130//! warmup: 5,
131//! };
132//!
133//! let report = run_benchmark(spec)?;
134//! println!("Collected {} samples", report.samples.len());
135//! ```
136//!
137//! ### Discovering Benchmarks
138//!
139//! ```ignore
140//! use mobench_sdk::{discover_benchmarks, list_benchmark_names};
141//!
142//! // Get all registered benchmark names
143//! let names = list_benchmark_names();
144//! for name in names {
145//! println!("Found benchmark: {}", name);
146//! }
147//!
148//! // Get full benchmark function info
149//! let benchmarks = discover_benchmarks();
150//! for bench in benchmarks {
151//! println!("Benchmark: {}", bench.name);
152//! }
153//! ```
154//!
155//! ## Building Mobile Apps
156//!
157//! The SDK includes builders for automating mobile app creation:
158//!
159//! ### Android Builder
160//!
161//! ```ignore
162//! use mobench_sdk::builders::AndroidBuilder;
163//! use mobench_sdk::{BuildConfig, BuildProfile, Target};
164//!
165//! let builder = AndroidBuilder::new(".", "my-bench-crate")
166//! .verbose(true)
167//! .output_dir("target/mobench"); // Default
168//!
169//! let config = BuildConfig {
170//! target: Target::Android,
171//! profile: BuildProfile::Release,
172//! incremental: true,
173//! };
174//!
175//! let result = builder.build(&config)?;
176//! println!("APK built at: {:?}", result.app_path);
177//! ```
178//!
179//! ### iOS Builder
180//!
181//! ```ignore
182//! use mobench_sdk::builders::{IosBuilder, SigningMethod};
183//! use mobench_sdk::{BuildConfig, BuildProfile, Target};
184//!
185//! let builder = IosBuilder::new(".", "my-bench-crate")
186//! .verbose(true);
187//!
188//! let config = BuildConfig {
189//! target: Target::Ios,
190//! profile: BuildProfile::Release,
191//! incremental: true,
192//! };
193//!
194//! let result = builder.build(&config)?;
195//! println!("xcframework built at: {:?}", result.app_path);
196//!
197//! // Package IPA for distribution
198//! let ipa_path = builder.package_ipa("BenchRunner", SigningMethod::AdHoc)?;
199//! ```
200//!
201//! ## Output Directory
202//!
203//! By default, all mobile artifacts are written to `target/mobench/`:
204//!
205//! ```text
206//! target/mobench/
207//! ├── android/
208//! │ ├── app/
209//! │ │ ├── src/main/jniLibs/ # Native .so libraries
210//! │ │ └── build/outputs/apk/ # Built APK
211//! │ └── ...
212//! └── ios/
213//! ├── sample_fns.xcframework/ # Built xcframework
214//! ├── BenchRunner/ # Xcode project
215//! └── BenchRunner.ipa # Packaged IPA
216//! ```
217//!
218//! This keeps generated files inside `target/`, following Rust conventions
219//! and preventing accidental commits of mobile project files.
220//!
221//! ## Platform Requirements
222//!
223//! ### Android
224//!
225//! - Android NDK (set `ANDROID_NDK_HOME` environment variable)
226//! - `cargo-ndk` (`cargo install cargo-ndk`)
227//! - Rust targets: `rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android`
228//!
229//! ### iOS
230//!
231//! - Xcode with command line tools
232//! - `uniffi-bindgen` (`cargo install uniffi-bindgen`)
233//! - `xcodegen` (optional, `brew install xcodegen`)
234//! - Rust targets: `rustup target add aarch64-apple-ios aarch64-apple-ios-sim`
235//!
236//! ## Best Practices
237//!
238//! ### Use `black_box` to Prevent Optimization
239//!
240//! Always wrap benchmark results with [`std::hint::black_box`] to prevent the
241//! compiler from optimizing away the computation:
242//!
243//! ```ignore
244//! #[benchmark]
245//! fn correct_benchmark() {
246//! let result = expensive_computation();
247//! std::hint::black_box(result); // Result is "used"
248//! }
249//! ```
250//!
251//! ### Avoid Side Effects
252//!
253//! Benchmarks should be deterministic and avoid I/O operations:
254//!
255//! ```ignore
256//! // Good: Pure computation
257//! #[benchmark]
258//! fn good_benchmark() {
259//! let data = vec![1, 2, 3, 4, 5];
260//! let sum: i32 = data.iter().sum();
261//! std::hint::black_box(sum);
262//! }
263//!
264//! // Avoid: File I/O adds noise
265//! #[benchmark]
266//! fn noisy_benchmark() {
267//! let data = std::fs::read_to_string("data.txt").unwrap(); // Don't do this
268//! std::hint::black_box(data);
269//! }
270//! ```
271//!
272//! ### Choose Appropriate Iteration Counts
273//!
274//! - **Warmup**: 5-10 iterations to warm CPU caches and JIT
275//! - **Iterations**: 50-100 for stable statistics
276//! - Mobile devices may have more variance than desktop
277//!
278//! ## License
279//!
280//! MIT License - see repository for details.
281
282#![cfg_attr(docsrs, feature(doc_cfg))]
283
284// Core timing module - always available
285pub mod timing;
286pub mod types;
287
288// Full SDK modules - only with "full" feature
289#[cfg(feature = "full")]
290#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
291pub mod builders;
292#[cfg(feature = "full")]
293#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
294pub mod codegen;
295#[cfg(feature = "full")]
296#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
297pub mod registry;
298#[cfg(feature = "full")]
299#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
300pub mod runner;
301
302// Re-export the benchmark macro from bench-macros (only with full feature)
303#[cfg(feature = "full")]
304#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
305pub use mobench_macros::benchmark;
306
307// Re-export key types for convenience (full feature)
308#[cfg(feature = "full")]
309#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
310pub use registry::{BenchFunction, discover_benchmarks, find_benchmark, list_benchmark_names};
311#[cfg(feature = "full")]
312#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
313pub use runner::{BenchmarkBuilder, run_benchmark};
314
315// Re-export types that are always available
316pub use types::{BenchError, BenchSample, BenchSpec, RunnerReport};
317
318// Re-export types that require full feature
319#[cfg(feature = "full")]
320#[cfg_attr(docsrs, doc(cfg(feature = "full")))]
321pub use types::{BuildConfig, BuildProfile, BuildResult, InitConfig, Target};
322
323// Re-export timing types at the crate root for convenience
324pub use timing::{run_closure, TimingError};
325
326/// Library version, matching `Cargo.toml`.
327///
328/// This can be used to verify SDK compatibility:
329///
330/// ```
331/// assert!(!mobench_sdk::VERSION.is_empty());
332/// ```
333pub const VERSION: &str = env!("CARGO_PKG_VERSION");
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn test_version_is_set() {
341 assert!(!VERSION.is_empty());
342 }
343
344 #[cfg(feature = "full")]
345 #[test]
346 fn test_discover_benchmarks_compiles() {
347 // This test just ensures the function is accessible
348 let _benchmarks = discover_benchmarks();
349 }
350}