From ccd7f4586b63841c4bac51f24dc38570c9f89726 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 22 Apr 2019 22:21:46 +0200 Subject: [PATCH] book: indirection for faster message passing --- Cargo.toml | 10 ++++- book/en/src/by-example/tips.md | 24 ++++++++++++ ci/expected/pool.run | 2 + ci/script.sh | 28 +++++++++++++- examples/pool.rs | 67 ++++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 ci/expected/pool.run create mode 100644 examples/pool.rs diff --git a/Cargo.toml b/Cargo.toml index 236f3091f5..b0df0483b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,12 @@ required-features = ["timer-queue"] name = "periodic" required-features = ["timer-queue"] +[[example]] +name = "pool" +# this example doesn't need this feature but only works on ARMv7-M +# specifying the feature here avoids compiling this for ARMv6-M +required-features = ["timer-queue"] + [[example]] name = "schedule" required-features = ["timer-queue"] @@ -39,8 +45,8 @@ cortex-m-rt = "0.6.7" cortex-m-rtfm-macros = { path = "macros", version = "0.5.0-alpha.1" } [dependencies.heapless] -features = ["smaller-atomics"] -version = "0.4.1" +features = ["smaller-atomics", "min-const-fn"] +version = "0.4.3" [dev-dependencies] cortex-m-semihosting = "0.3.2" diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index 07a5c0b8aa..79b9d71e0f 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -89,3 +89,27 @@ after the function, not the interrupt / exception. Example below: ``` console $ cargo run --example binds {{#include ../../../../ci/expected/binds.run}}``` + +## Indirection for faster message passing + +Message passing always involves copying the payload from the sender into a +static variable and then from the static variable into the receiver. Thus +sending a large buffer, like a `[u8; 128]`, as a message involves two expensive +`memcpy`s. To minimize the message passing overhead one can use indirection: +instead of sending the buffer by value, one can send an owning pointer into the +buffer. + +One can use a global allocator to achieve indirection (`alloc::Box`, +`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.34.0, +or one can use a statically allocated memory pool like [`heapless::Pool`]. + +[`heapless::Pool`]: https://docs.rs/heapless/0.4.3/heapless/pool/index.html + +Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. + +``` rust +{{#include ../../../../examples/pool.rs}} +``` +``` console +$ cargo run --example binds +{{#include ../../../../ci/expected/pool.run}}``` diff --git a/ci/expected/pool.run b/ci/expected/pool.run new file mode 100644 index 0000000000..040dcee888 --- /dev/null +++ b/ci/expected/pool.run @@ -0,0 +1,2 @@ +bar(0x2000008c) +foo(0x20000110) diff --git a/ci/script.sh b/ci/script.sh index 00162ebdd4..b64617d000 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -110,6 +110,7 @@ main() { shared-with-init generics + pool ramfunc ) @@ -119,6 +120,31 @@ main() { continue fi + if [ $ex = pool ]; then + if [ $TARGET != thumbv6m-none-eabi ]; then + local td=$(mktemp -d) + + local features="$nightly,timer-queue" + cargo run --example $ex --target $TARGET --features $features >\ + $td/pool.run + grep 'foo(0x2' $td/pool.run + grep 'bar(0x2' $td/pool.run + arm-none-eabi-objcopy -O ihex target/$TARGET/debug/examples/$ex \ + ci/builds/${ex}_${features/,/_}_debug_1.hex + + cargo run --example $ex --target $TARGET --features $features --release >\ + $td/pool.run + grep 'foo(0x2' $td/pool.run + grep 'bar(0x2' $td/pool.run + arm-none-eabi-objcopy -O ihex target/$TARGET/release/examples/$ex \ + ci/builds/${ex}_${features/,/_}_release_1.hex + + rm -rf $td + fi + + continue + fi + if [ $ex != types ]; then arm_example "run" $ex "debug" "$nightly" "1" arm_example "run" $ex "release" "$nightly" "1" @@ -138,7 +164,7 @@ main() { continue fi - if [ $ex != types ]; then + if [ $ex != types ] && [ $ex != pool ]; then arm_example "build" $ex "debug" "$nightly" "2" cmp ci/builds/${ex}_${nightly/nightly/nightly_}debug_1.hex \ ci/builds/${ex}_${nightly/nightly/nightly_}debug_2.hex diff --git a/examples/pool.rs b/examples/pool.rs new file mode 100644 index 0000000000..0b594b192d --- /dev/null +++ b/examples/pool.rs @@ -0,0 +1,67 @@ +//! examples/pool.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::{debug, hprintln}; +use heapless::{ + pool, + pool::singleton::{Box, Pool}, +}; +use lm3s6965::Interrupt; +use rtfm::app; + +// Declare a pool of 128-byte memory blocks +pool!(P: [u8; 128]); + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init(_: init::Context) { + static mut MEMORY: [u8; 512] = [0; 512]; + + // Increase the capacity of the memory pool by ~4 + P::grow(MEMORY); + + rtfm::pend(Interrupt::I2C0); + } + + #[interrupt(priority = 2, spawn = [foo, bar])] + fn I2C0(c: I2C0::Context) { + // claim a memory block, leave it uninitialized and .. + let x = P::alloc().unwrap().freeze(); + + // .. send it to the `foo` task + c.spawn.foo(x).ok().unwrap(); + + // send another block to the task `bar` + c.spawn.bar(P::alloc().unwrap().freeze()).ok().unwrap(); + } + + #[task] + fn foo(_: foo::Context, x: Box

) { + hprintln!("foo({:?})", x.as_ptr()).unwrap(); + + // explicitly return the block to the pool + drop(x); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2)] + fn bar(_: bar::Context, x: Box

) { + hprintln!("bar({:?})", x.as_ptr()).unwrap(); + + // this is done automatically so we can omit the call to `drop` + // drop(x); + } + + extern "C" { + fn UART0(); + fn UART1(); + } +};