From a98fbeac3c3684520c3b05b0f1eac0eaed4cc38f Mon Sep 17 00:00:00 2001 From: Raul Victor Trombin Date: Fri, 4 Oct 2024 17:49:32 -0300 Subject: [PATCH] Add benchmark for ping1d and ping360 --- .github/workflows/action.yml | 65 +++++++++++ Cargo.toml | 5 + benches/bench.rs | 209 +++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 benches/bench.rs diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index ff64d8f64..a491041f5 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -36,6 +36,71 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./target/doc + bench: + needs: build + if: ${{ github.repository_owner == 'bluerobotics' }} + runs-on: raspbian-armv7-kernel-5.10.33 + steps: + - uses: actions/checkout@master + - uses: dtolnay/rust-toolchain@stable + - name: Rust | Cache + uses: Swatinem/rust-cache@v2 + with: + prefix-key: "rust-cache" + shared-key: "benchmark" + - name: Get previous benchmark data + run: | + echo "Fetching gh-pages branch" + git fetch origin gh-pages + + echo "Checking out gh-pages branch" + git checkout gh-pages + + if [ ! -d "cache" ]; then + echo "Cache folder does not exist, creating it" + mkdir cache + fi + + echo "Copying data file from gh-pages to cache" + cp dev/cache/benchmark-data.json cache/benchmark-data.json || { echo "Failed to copy data file" ; exit 1; } + + echo "Checking out current preivous branch" + git checkout - + - name: Cargo Bench + run: cargo bench --jobs 1 --bench bench -- --output-format bencher | tee output.txt || { echo "Benchmark failed"; exit 1; } + - name: Compare results & store cached results + uses: benchmark-action/github-action-benchmark@v1.18.0 + with: + tool: 'cargo' + output-file-path: output.txt + summary-always: true + alert-threshold: "130%" + fail-on-alert: true + external-data-json-path: ./cache/benchmark-data.json + skip-fetch-gh-pages: "true" + - name: Update data file + if: ${{ github.ref == 'refs/heads/master' }} + run: | + git config user.name "GitHub Actions Bot" + git config user.email "actions@github.com" + + git fetch origin gh-pages + + git checkout gh-pages + + if [ ! -d "dev/cache" ]; then + echo "Cache folder does not exist, creating it" + mkdir -p dev/cache + fi + + cp cache/benchmark-data.json dev/cache/benchmark-data.json + + tree cache + + git add dev/cache/benchmark-data.json + git commit -m "Update benchmark-data file" + git push origin gh-pages + deploy: needs: build runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 7e65b07a8..9833c113e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ serde_bytes = "0.11" [dev-dependencies] tracing-test = "0.2.4" udp-stream = "0.0.12" +criterion = { version = "0.5.1", features = ["html_reports", "async_tokio"] } [build-dependencies] convert_case = "0.6.0" @@ -38,3 +39,7 @@ serde_json = "1.0.64" [features] local_runner = [] default = ["serde"] + +[[bench]] +name = "bench" +harness = false diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 000000000..2fc9ebd46 --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,209 @@ +use bluerobotics_ping::{ + device::{Ping1D, Ping360, PingDevice}, + message::MessageInfo, + ping1d::{self, ProfileStruct}, + Messages, +}; +use criterion::black_box; +use criterion::{criterion_group, criterion_main, Criterion}; +use std::time::Instant; +use std::{net::SocketAddr, str::FromStr}; +use tokio::runtime::Runtime; +use tokio_serial::{Error, SerialPort, SerialPortBuilderExt}; +use udp_stream::UdpStream; + +fn rt() -> Runtime { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .worker_threads(1) + .thread_name("criterion-tokio-rt") + .build() + .unwrap() +} + +async fn create_ping1d_usb() -> Ping1D { + let port = tokio_serial::new("/dev/ttyUSB0".to_string(), 115200) + .open_native_async() + .map_err(|e| { + eprintln!("Error opening serial port: {}", e); + e + }) + .unwrap(); + port.clear(tokio_serial::ClearBuffer::All).unwrap(); + + Ping1D::new(port) +} + +async fn create_ping360_udp() -> Ping360 { + let socket_addr = SocketAddr::from_str(&format!("192.168.1.197:12345")) + .map_err(|e| { + eprintln!("Error parsing UDP address: {}", e); + e + }) + .unwrap(); + let port = UdpStream::connect(socket_addr) + .await + .map_err(|e| { + eprintln!("Error connecting to UDP socket: {}", e); + e + }) + .unwrap(); + Ping360::new(port) +} + +async fn receive_10_profiles( + mut subscribed: tokio::sync::broadcast::Receiver, +) -> Result<(), Error> { + let mut profile_struct_vector: Vec = Vec::new(); + for _i in 1..10 { + let received = subscribed.recv().await; + + match received { + Ok(msg) => { + if msg.message_id == bluerobotics_ping::ping1d::ProfileStruct::id() { + match Messages::try_from(&msg) { + Ok(Messages::Ping1D(ping1d::Messages::Profile(answer))) => { + profile_struct_vector.push(answer) + } + _ => panic!(), + } + } + } + Err(_e) => { + panic!() + } + } + } + Ok(()) +} + +fn ping1d_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("Ping1D"); + group.sample_size(10); // Reduced sample size + group.measurement_time(std::time::Duration::from_secs(60)); // Increased measurement time + + macro_rules! bench { + ($bench_fn:ident($($arg:tt)*)) => { + group.bench_function(stringify!($bench_fn), move |b| { + b.to_async(rt()).iter_custom(|iters| async move { + let ping1d = create_ping1d_usb().await; + + ping1d.continuous_stop(bluerobotics_ping::ping1d::ProfileStruct::id()) + .await + .unwrap(); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + let mut total_duration = std::time::Duration::new(0, 0); + for _i in 0..iters { + let start = Instant::now(); + let result = black_box(ping1d.$bench_fn($($arg)*).await); + total_duration += start.elapsed(); + result.unwrap(); + } + total_duration + }) + }); + } + } + + // Get Methods + bench!(profile()); + // bench!(ping_interval()); + // bench!(transmit_duration()); + // bench!(range()); + // bench!(speed_of_sound()); + // bench!(firmware_version()); + // bench!(mode_auto()); + // bench!(distance_simple()); + // bench!(pcb_temperature()); + // bench!(ping_enable()); + // bench!(general_info()); + // bench!(distance()); + // bench!(processor_temperature()); + // bench!(voltage_5()); + // bench!(gain_setting()); + // bench!(device_id()); + + // Custom - receive 10 profile packages + group.bench_function("Receive 10 profiles", move |b| { + b.to_async(rt()).iter_custom(|iters| async move { + let ping1d = create_ping1d_usb().await; + + ping1d + .continuous_start(bluerobotics_ping::ping1d::ProfileStruct::id()) + .await + .unwrap(); + + let mut total_duration = std::time::Duration::new(0, 0); + for _i in 0..iters { + let start = Instant::now(); + black_box(receive_10_profiles(ping1d.subscribe()).await.unwrap()); + total_duration += start.elapsed(); + } + + ping1d + .continuous_stop(bluerobotics_ping::ping1d::ProfileStruct::id()) + .await + .unwrap(); + + total_duration + }) + }); + + group.finish(); +} + +fn ping360_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("Ping360"); + group.sample_size(10); + group.measurement_time(std::time::Duration::from_secs(60)); + + macro_rules! bench360 { + ($bench_fn:ident($($arg:tt)*)) => { + group.bench_function(stringify!($bench_fn), move |b| { + b.to_async(rt()).iter_custom(|iters| async move { + let ping360 = create_ping360_udp().await; + ping360.reset(0,0).await.unwrap(); + + let mut total_duration = std::time::Duration::new(0, 0); + for _i in 0..iters { + let start = Instant::now(); + let result = black_box(ping360.$bench_fn($($arg)*).await); + total_duration += start.elapsed(); + result.unwrap(); + } + total_duration + }) + }); + } + } + + let mode: u8 = 1; + let gain_setting: u8 = 0; + let angle: u16 = 360; + let transmit_duration: u16 = 7; + let sample_period: u16 = 80; + let transmit_frequency: u16 = 700; + let number_of_samples: u16 = 1200; + let transmit: u8 = 1; + + // Get Methods + bench360!(transducer( + mode, + gain_setting, + angle, + transmit_duration, + sample_period, + transmit_frequency, + number_of_samples, + transmit, + 0, + )); + bench360!(motor_off()); + + group.finish(); +} + +criterion_group!(benches, ping1d_benchmark, ping360_benchmark); +criterion_main!(benches);