From 3034268d4b3d84ef6d663eb7e46fd9c15298378d Mon Sep 17 00:00:00 2001 From: ROMemories Date: Thu, 21 Mar 2024 13:36:42 +0100 Subject: [PATCH] refactor(bench)!: move benchmarking facilities to their own crate --- .github/workflows/main.yml | 4 +- Cargo.lock | 9 +++ Cargo.toml | 2 + examples/benchmark/Cargo.toml | 2 +- examples/benchmark/src/main.rs | 2 +- src/riot-rs-bench/Cargo.toml | 15 +++++ src/riot-rs-bench/src/cortexm.rs | 36 ++++++++++++ src/riot-rs-bench/src/lib.rs | 55 +++++++++++++++++++ src/riot-rs-rt/src/cortexm.rs | 31 ----------- src/riot-rs-rt/src/esp.rs | 4 -- src/riot-rs-rt/src/lib.rs | 5 -- src/riot-rs/Cargo.toml | 3 + src/riot-rs/src/lib.rs | 4 ++ tests/benchmarks/bench_sched_yield/Cargo.toml | 1 + .../benchmarks/bench_sched_yield/src/main.rs | 2 +- 15 files changed, 130 insertions(+), 45 deletions(-) create mode 100644 src/riot-rs-bench/Cargo.toml create mode 100644 src/riot-rs-bench/src/cortexm.rs create mode 100644 src/riot-rs-bench/src/lib.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a474106c1..8933cc57c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -136,11 +136,11 @@ jobs: - name: clippy uses: clechasseur/rs-clippy-check@v3 with: - args: --verbose --locked -p riot-rs-embassy + args: --verbose --locked -p riot-rs-embassy -p riot-rs-bench # TODO: we'll eventually want to enable relevant features - name: "rustdoc" - run: cargo rustdoc -p riot-rs --features no-boards -- -D warnings + run: cargo rustdoc -p riot-rs --features no-boards,bench -- -D warnings - name: rustfmt run: cargo fmt --check --all diff --git a/Cargo.lock b/Cargo.lock index 3fdc88121..d8d22ceaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2323,6 +2323,7 @@ version = "0.1.0" dependencies = [ "document-features", "linkme", + "riot-rs-bench", "riot-rs-boards", "riot-rs-buildinfo", "riot-rs-debug", @@ -2333,6 +2334,14 @@ dependencies = [ "static_cell", ] +[[package]] +name = "riot-rs-bench" +version = "0.1.0" +dependencies = [ + "cfg-if", + "cortex-m", +] + [[package]] name = "riot-rs-boards" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a7b34eb30..66600b760 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "src/lib/rbi", "src/lib/ringbuffer", "src/riot-rs", + "src/riot-rs-bench", "src/riot-rs-boards", "src/riot-rs-boards/nrf52", "src/riot-rs-boards/nrf52840dk", @@ -51,6 +52,7 @@ esp-wifi = { git = "https://github.com/kaspar030/esp-wifi", branch = "update-esp linkme = { version = "0.3.21", features = ["used_linker"] } riot-rs = { path = "src/riot-rs", default-features = false } +riot-rs-bench = { path = "src/riot-rs-bench", default-features = false } riot-rs-boards = { path = "src/riot-rs-boards", default-features = false } riot-rs-debug = { path = "src/riot-rs-debug", default-features = false } riot-rs-rt = { path = "src/riot-rs-rt" } diff --git a/examples/benchmark/Cargo.toml b/examples/benchmark/Cargo.toml index f6003671d..6ebe0d7e1 100644 --- a/examples/benchmark/Cargo.toml +++ b/examples/benchmark/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" publish = false [dependencies] -riot-rs = { path = "../../src/riot-rs", features = ["threading"] } +riot-rs = { path = "../../src/riot-rs", features = ["bench", "threading"] } riot-rs-boards = { path = "../../src/riot-rs-boards" } diff --git a/examples/benchmark/src/main.rs b/examples/benchmark/src/main.rs index 83cb1098b..83c4c3363 100644 --- a/examples/benchmark/src/main.rs +++ b/examples/benchmark/src/main.rs @@ -7,7 +7,7 @@ use riot_rs::debug::println; #[riot_rs::thread(autostart)] fn main() { - match riot_rs::rt::benchmark(10000, || { + match riot_rs::bench::benchmark(10000, || { // }) { Ok(ticks) => { diff --git a/src/riot-rs-bench/Cargo.toml b/src/riot-rs-bench/Cargo.toml new file mode 100644 index 000000000..0386892a0 --- /dev/null +++ b/src/riot-rs-bench/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "riot-rs-bench" +version.workspace = true +authors.workspace = true +edition.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +cfg-if = { workspace = true } + +[target.'cfg(context = "cortex-m")'.dependencies] +cortex-m = { workspace = true, features = ["critical-section-single-core"] } diff --git a/src/riot-rs-bench/src/cortexm.rs b/src/riot-rs-bench/src/cortexm.rs new file mode 100644 index 000000000..833b48966 --- /dev/null +++ b/src/riot-rs-bench/src/cortexm.rs @@ -0,0 +1,36 @@ +use cortex_m::{ + peripheral::{syst::SystClkSource, SYST}, + Peripherals, +}; + +use crate::Error; + +#[allow(missing_docs)] +pub fn benchmark ()>(iterations: usize, f: F) -> Result { + let mut p = unsafe { Peripherals::steal() }; + // + p.SCB.clear_sleepdeep(); + + // + p.SYST.set_clock_source(SystClkSource::Core); + p.SYST.set_reload(0x00FFFFFF); + p.SYST.clear_current(); + p.SYST.enable_counter(); + + // Wait for the system timer to be ready + while SYST::get_current() == 0 {} + + let before = SYST::get_current(); + + for _ in 0..iterations { + f(); + } + + let total = before - SYST::get_current(); + + if p.SYST.has_wrapped() { + Err(Error::SystemTimerWrapped) + } else { + Ok(total as usize / iterations) + } +} diff --git a/src/riot-rs-bench/src/lib.rs b/src/riot-rs-bench/src/lib.rs new file mode 100644 index 000000000..c4f381c0a --- /dev/null +++ b/src/riot-rs-bench/src/lib.rs @@ -0,0 +1,55 @@ +//! Provides on-board benchmarking facilities. + +#![cfg_attr(not(test), no_std)] +#![feature(error_in_core)] +#![deny(clippy::pedantic)] +#![deny(missing_docs)] + +cfg_if::cfg_if! { + if #[cfg(context = "cortex-m")] { + mod cortexm; + use cortexm as bench; + } + else if #[cfg(context = "riot-rs")] { + // When run with laze but the architecture is not supported + compile_error!("benchmarking is not supported for this architecture"); + } else { + // Provide a default bench module, for arch-independent tooling + mod bench { + use crate::Error; + + /// Benchmarks "time" required to run the provided function. + /// + /// Runs the provided function `iterations` times, and returns the mean number of system timer + /// increments per iteration. + /// + /// # Errors + /// + /// Returns [`Error::SystemTimerWrapped`] if the system timer counter has wrapped when + /// benchmarking. + #[allow(unused_variables)] + pub fn benchmark(iterations: usize, f: F) -> Result { + unimplemented!(); + } + } + } +} + +pub use bench::benchmark; + +/// Possible errors happening when benchmarking. +#[derive(Debug)] +pub enum Error { + /// The system timer wrapped when benchmarking. + SystemTimerWrapped, +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::SystemTimerWrapped => write!(f, "system timer wrapped"), + } + } +} + +impl core::error::Error for Error {} diff --git a/src/riot-rs-rt/src/cortexm.rs b/src/riot-rs-rt/src/cortexm.rs index 89ef23362..52a370e32 100644 --- a/src/riot-rs-rt/src/cortexm.rs +++ b/src/riot-rs-rt/src/cortexm.rs @@ -208,34 +208,3 @@ pub fn init() { p.SCB.set_priority(SystemHandler::PendSV, 0xFF); } } - -pub fn benchmark ()>(iterations: usize, f: F) -> core::result::Result { - use cortex_m::peripheral::syst::SystClkSource; - use cortex_m::Peripherals; - - let mut p = unsafe { Peripherals::steal() }; - // - p.SCB.clear_sleepdeep(); - - // - p.SYST.set_clock_source(SystClkSource::Core); - p.SYST.set_reload(0x00FFFFFF); - p.SYST.clear_current(); - p.SYST.enable_counter(); - - while cortex_m::peripheral::SYST::get_current() == 0 {} - - let before = cortex_m::peripheral::SYST::get_current(); - - for _ in 0..iterations { - f(); - } - - let total = before - cortex_m::peripheral::SYST::get_current(); - - if p.SYST.has_wrapped() { - Err(()) - } else { - Ok((total) as usize / iterations) - } -} diff --git a/src/riot-rs-rt/src/esp.rs b/src/riot-rs-rt/src/esp.rs index 2498f79bb..527a2baa7 100644 --- a/src/riot-rs-rt/src/esp.rs +++ b/src/riot-rs-rt/src/esp.rs @@ -4,7 +4,3 @@ fn main() -> ! { } pub fn init() {} - -pub fn benchmark ()>(_iterations: usize, _f: F) -> core::result::Result { - unimplemented!() -} diff --git a/src/riot-rs-rt/src/lib.rs b/src/riot-rs-rt/src/lib.rs index bc77a03e1..66e721948 100644 --- a/src/riot-rs-rt/src/lib.rs +++ b/src/riot-rs-rt/src/lib.rs @@ -37,15 +37,10 @@ cfg_if::cfg_if! { mod arch { #[cfg_attr(not(context = "riot-rs"), allow(dead_code))] pub fn init() {} - pub fn benchmark(_iterations: usize, _f: F) -> core::result::Result { - unimplemented!(); - } } } } -pub use arch::benchmark; - const ISR_STACKSIZE: usize = riot_rs_utils::usize_from_env_or!("CONFIG_ISR_STACKSIZE", 8192, "ISR stack size (in bytes)"); diff --git a/src/riot-rs/Cargo.toml b/src/riot-rs/Cargo.toml index a020f5702..8dcfbce73 100644 --- a/src/riot-rs/Cargo.toml +++ b/src/riot-rs/Cargo.toml @@ -10,6 +10,7 @@ workspace = true [dependencies] document-features = { workspace = true } linkme = { workspace = true } +riot-rs-bench = { workspace = true, optional = true } riot-rs-boards = { path = "../riot-rs-boards" } riot-rs-buildinfo = { path = "../riot-rs-buildinfo" } riot-rs-debug = { workspace = true } @@ -74,6 +75,8 @@ wifi-esp = ["riot-rs-embassy/wifi-esp"] ## Enables the debug console, required to use ## [`println!`](riot_rs_debug::println). debug-console = ["riot-rs-rt/debug-console"] +## Enables benchmarking facilities. +bench = ["dep:riot-rs-bench"] ## Prints nothing in case of panics (may help reduce binary size). silent-panic = ["riot-rs-rt/silent-panic"] ## Allows to have no boards selected, useful to run target-independent tooling. diff --git a/src/riot-rs/src/lib.rs b/src/riot-rs/src/lib.rs index 85149cd61..ce516d039 100644 --- a/src/riot-rs/src/lib.rs +++ b/src/riot-rs/src/lib.rs @@ -7,6 +7,10 @@ #![no_std] #![feature(doc_cfg)] +#[cfg(feature = "bench")] +#[doc(cfg(feature = "bench"))] +#[doc(inline)] +pub use riot_rs_bench as bench; pub use riot_rs_buildinfo as buildinfo; pub use riot_rs_debug as debug; pub use riot_rs_embassy::{self as embassy, define_peripherals, group_peripherals}; diff --git a/tests/benchmarks/bench_sched_yield/Cargo.toml b/tests/benchmarks/bench_sched_yield/Cargo.toml index dfc42febc..a72b07495 100644 --- a/tests/benchmarks/bench_sched_yield/Cargo.toml +++ b/tests/benchmarks/bench_sched_yield/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] riot-rs = { workspace = true, default-features = true, features = [ + "bench", "threading", ] } riot-rs-boards = { workspace = true } diff --git a/tests/benchmarks/bench_sched_yield/src/main.rs b/tests/benchmarks/bench_sched_yield/src/main.rs index 5c7fc5e5e..ec3a6f68f 100644 --- a/tests/benchmarks/bench_sched_yield/src/main.rs +++ b/tests/benchmarks/bench_sched_yield/src/main.rs @@ -7,7 +7,7 @@ use riot_rs::{debug::println, thread}; #[riot_rs::thread(autostart)] fn thread0() { - match riot_rs::rt::benchmark(10000, || thread::yield_same()) { + match riot_rs::bench::benchmark(10000, || thread::yield_same()) { Ok(ticks) => { println!( "took {} ticks per iteration ({} per context switch)",