From 0cc456ba8075a63cd57179acee53e1f266249d17 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Fri, 4 May 2018 10:59:23 +0200 Subject: [PATCH] reduce memory overhead by storing indices (u8) in the queues instead of pointers (*mut u8) in the binary heap we store the baseline inline along with the index and the task name. Before we stored a pointer to the message and had to lookup the baseline when comparing two nodes in the heap. --- Cargo.toml | 4 +- examples/async.rs | 8 ++ examples/periodic-payload.rs | 22 +++- examples/periodic-preemption-payload.rs | 43 ++++++- examples/periodic-preemption.rs | 40 +++++- examples/periodic.rs | 26 +++- macros/src/trans.rs | 89 +++++++------ src/lib.rs | 7 +- src/node.rs | 164 +----------------------- src/tq.rs | 57 ++++++-- 10 files changed, 227 insertions(+), 233 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e3d6731fb..57432690c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ required-features = ["timer-queue"] [dependencies] cortex-m = "0.4.0" cortex-m-rtfm-macros = { path = "macros", version = "0.3.1" } -heapless = "0.3.5" +heapless = "0.3.6" typenum = "1.10.0" [target.'cfg(target_arch = "x86_64")'.dev-dependencies] @@ -71,4 +71,4 @@ cm7-r0p1 = ["cortex-m/cm7-r0p1"] timer-queue = ["cortex-m-rtfm-macros/timer-queue"] [profile.release] -lto = true +lto = true \ No newline at end of file diff --git a/examples/async.rs b/examples/async.rs index f56e9a495b..03435caa76 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -1,3 +1,11 @@ +// # Pointers (old) +// +// ~40~ 32 bytes .bss +// +// # Indices (new) +// +// 12 bytes .bss + #![deny(unsafe_code)] #![deny(warnings)] #![feature(proc_macro)] diff --git a/examples/periodic-payload.rs b/examples/periodic-payload.rs index 24a203f9c7..bd6cf76303 100644 --- a/examples/periodic-payload.rs +++ b/examples/periodic-payload.rs @@ -1,16 +1,36 @@ -// 52 bytes .bss +// # Pointers (old) +// +// ~52~ 48 bytes .bss // // # -Os +// // init // a(bl=8000000, now=8000180, input=0) // a(bl=16000000, now=16000180, input=1) // a(bl=24000000, now=24000180, input=2) // // # -O3 +// // init // a(bl=8000000, now=8000168, input=0) // a(bl=16000000, now=16000168, input=1) // a(bl=24000000, now=24000168, input=2) +// +// # Indices (new) +// +// 32 bytes .bss +// +// ## -O3 +// +// init +// a(bl=8000000, now=8000170, input=0) +// a(bl=16000000, now=16000170, input=1) +// +// ## -Os +// +// init +// a(bl=8000000, now=8000179, input=0) +// a(bl=16000000, now=16000179, input=1) #![deny(unsafe_code)] #![deny(warnings)] diff --git a/examples/periodic-preemption-payload.rs b/examples/periodic-preemption-payload.rs index 5b1b143782..b92d474943 100644 --- a/examples/periodic-preemption-payload.rs +++ b/examples/periodic-preemption-payload.rs @@ -1,6 +1,9 @@ -// 104 bytes .bss +// # Pointers (old) +// +// ~104~ 88 bytes .bss +// +// ## -Os // -// # -Os // a(bl=16000000, now=16000248, input=0) // b(bl=24000000, now=24000251, input=0) // a(bl=32000000, now=32000248, input=1) @@ -11,8 +14,9 @@ // a(bl=80000000, now=80000248, input=4) // b(bl=96000000, now=96000283, input=3) // a(bl=96000000, now=96002427, input=5) - -// # -O3 +// +// ## -O3 +// // init // a(bl=16000000, now=16000231, input=0) // b(bl=24000000, now=24000230, input=0) @@ -24,6 +28,37 @@ // a(bl=80000000, now=80000231, input=4) // b(bl=96000000, now=96000259, input=3) // a(bl=96000000, now=96002397, input=5) +// +// # Indices (new) +// +// 56 bytes .bss +// +// ## -O3 +// +// a(bl=16000000, now=16000215, input=0) +// b(bl=24000000, now=24000214, input=0) +// a(bl=32000000, now=32000215, input=1) +// b(bl=48000000, now=48000236, input=1) +// a(bl=48000000, now=48002281, input=2) +// a(bl=64000000, now=64000215, input=3) +// b(bl=72000000, now=72000214, input=2) +// a(bl=80000000, now=80000215, input=4) +// b(bl=96000000, now=96000236, input=3) +// a(bl=96000000, now=96002281, input=5) +// +// ## -Os +// +// init +// a(bl=16000000, now=16000257, input=0) +// b(bl=24000000, now=24000252, input=0) +// a(bl=32000000, now=32000257, input=1) +// b(bl=48000000, now=48000284, input=1) +// a(bl=48000000, now=48002326, input=2) +// a(bl=64000000, now=64000257, input=3) +// b(bl=72000000, now=72000252, input=2) +// a(bl=80000000, now=80000257, input=4) +// b(bl=96000000, now=96000284, input=3) +// a(bl=96000000, now=96002326, input=5) #![deny(unsafe_code)] #![deny(warnings)] diff --git a/examples/periodic-preemption.rs b/examples/periodic-preemption.rs index 968e8be72f..1527864e3e 100644 --- a/examples/periodic-preemption.rs +++ b/examples/periodic-preemption.rs @@ -1,6 +1,9 @@ -// 96 bytes .bss +// # Pointers (old) +// +// ~96~ 80 bytes .bss // // # -Os +// // init // a(bl=16000000, now=16000249) // b(bl=24000000, now=24000248) @@ -12,8 +15,9 @@ // a(bl=80000000, now=80000249) // b(bl=96000000, now=96000282) // a(bl=96000000, now=96001731) - +// // # -O3 +// // init // a(bl=16000000, now=16000228) // b(bl=24000000, now=24000231) @@ -25,6 +29,38 @@ // a(bl=80000000, now=80000228) // b(bl=96000000, now=96000257) // a(bl=96000000, now=96001705) +// +// # Indices (new) +// +// 48 bytes .bss +// +// ## -O3 +// +// init +// a(bl=16000000, now=16000213) +// b(bl=24000000, now=24000212) +// a(bl=32000000, now=32000213) +// b(bl=48000000, now=48000234) +// a(bl=48000000, now=48001650) +// a(bl=64000000, now=64000213) +// b(bl=72000000, now=72000212) +// a(bl=80000000, now=80000213) +// b(bl=96000000, now=96000234) +// a(bl=96000000, now=96001650) +// +// ## -Os +// +// init +// a(bl=16000000, now=16000253) +// b(bl=24000000, now=24000251) +// a(bl=32000000, now=32000253) +// b(bl=48000000, now=48000283) +// a(bl=48000000, now=48001681) +// a(bl=64000000, now=64000253) +// b(bl=72000000, now=72000251) +// a(bl=80000000, now=80000253) +// b(bl=96000000, now=96000283) +// a(bl=96000000, now=96001681) #![deny(unsafe_code)] #![deny(warnings)] diff --git a/examples/periodic.rs b/examples/periodic.rs index 2d8d43ecc3..544b37612c 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -1,13 +1,33 @@ -// 52 bytes .bss +// # Pointers (old) +// +// ~52~ 40 bytes .bss +// +// ## -Os // -// # -Os // init // a(bl=8000000, now=8000180) // a(bl=16000000, now=16000180) // -// # -O3 +// ## -O3 +// // a(bl=8000000, now=8000168) // a(bl=16000000, now=16000168) +// +// # Indices (new) +// +// 28 bytes .bss +// +// ## -Os +// +// init +// a(bl=8000000, now=8000176) +// a(bl=16000000, now=16000176) +// +// ## -O3 +// +// init +// a(bl=8000000, now=8000167) +// a(bl=16000000, now=16000167) #![deny(unsafe_code)] #![deny(warnings)] diff --git a/macros/src/trans.rs b/macros/src/trans.rs index ab74ee0878..635855f060 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -280,18 +280,19 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { }); } Either::Right(capacity) => { - let capacity = Ident::from(format!("U{}", capacity)); + let ucapacity = Ident::from(format!("U{}", capacity)); + let capacity = capacity as usize; root.push(quote! { #[allow(unsafe_code)] unsafe impl #hidden::#krate::Resource for #name::SQ { const NVIC_PRIO_BITS: u8 = ::#device::NVIC_PRIO_BITS; type Ceiling = #name::Ceiling; - type Data = #hidden::#krate::SlotQueue<#input, #hidden::#krate::#capacity>; + type Data = #hidden::#krate::SlotQueue<#hidden::#krate::#ucapacity>; unsafe fn get() -> &'static mut Self::Data { static mut SQ: - #hidden::#krate::SlotQueue<#input, #hidden::#krate::#capacity> = + #hidden::#krate::SlotQueue<#hidden::#krate::#ucapacity> = #hidden::#krate::SlotQueue::u8(); &mut SQ @@ -306,6 +307,10 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { .unwrap_or(0) )); mod_.push(quote! { + #[allow(unsafe_code)] + pub static mut BUFFER: [#krate::Node<#input>; #capacity] = + unsafe { #krate::uninitialized() }; + pub struct SQ { _0: () } #[allow(unsafe_code)] @@ -372,13 +377,15 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { { unsafe { let slot = ::#name::SQ::new().claim_mut(t, |sq, _| sq.dequeue()); - if let Some(slot) = slot { - let tp = slot - .write(self.baseline, payload) - .tag(::#__priority::Task::#name); + if let Some(index) = slot { + let task = ::#__priority::Task::#name; + core::ptr::write( + ::#name::BUFFER.get_unchecked_mut(index as usize), + #krate::Node { baseline: self.baseline, payload } + ); ::#__priority::Q::new().claim_mut(t, |q, _| { - q.split().0.enqueue_unchecked(tp); + q.split().0.enqueue_unchecked((task, index)); }); #krate::set_pending(#device::Interrupt::#interrupt); @@ -417,17 +424,19 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { #krate::Maximum: #krate::Unsigned, { unsafe { - if let Some(slot) = + if let Some(index) = ::#name::SQ::new().claim_mut(t, |sq, _| sq.dequeue()) { - let tp = slot - .write(payload) - .tag(::#__priority::Task::#name); + let task = ::#__priority::Task::#name; + core::ptr::write( + ::#name::BUFFER.get_unchecked_mut(index as usize), + #krate::Node { payload } + ); ::#__priority::Q::new().claim_mut(t, |q, _| { - q.split().0.enqueue_unchecked(tp); + q.split().0.enqueue_unchecked((task, index)); }); - #krate::set_pending(#device::Interrupt::#interrupt); + #krate::set_pending(#device::Interrupt::#interrupt); Ok(()) } else { @@ -487,14 +496,21 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { #krate::Maximum: #krate::Unsigned, { unsafe { - if let Some(slot) = + if let Some(index) = ::#name::SQ::new().claim_mut(t, |sq, _| sq.dequeue()) { let bl = self.baseline + after; - let tp = slot - .write(bl, payload) - .tag(::__tq::Task::#name); + let task = ::__tq::Task::#name; + core::ptr::write( + ::#name::BUFFER.get_unchecked_mut(index as usize), + #krate::Node { baseline: bl, payload }, + ); + let m = #krate::Message { + baseline: bl, + index, + task, + }; - ::__tq::TQ::new().claim_mut(t, |tq, _| tq.enqueue(bl, tp)); + ::__tq::TQ::new().claim_mut(t, |tq, _| tq.enqueue(m)); Ok(()) } else { @@ -531,7 +547,7 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { quote! { __tq::Task::#name => { #__priority::Q::new().claim_mut(t, |q, _| { - q.split().0.enqueue_unchecked(tp.retag(#__priority::Task::#name)) + q.split().0.enqueue_unchecked((#__priority::Task::#name, index)) }); #hidden::#krate::set_pending(#device::Interrupt::#interrupt); } @@ -585,8 +601,8 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { #hidden::#krate::dispatch( &mut #hidden::#krate::Threshold::<__tq::Priority>::new(), &mut __tq::TQ::new(), - |t, tp| { - match tp.tag() { + |t, task, index| { + match task { #(#arms,)* } }) @@ -644,20 +660,18 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { if cfg!(feature = "timer-queue") { quote! { #__priority::Task::#name => { - let (bl, payload, slot) = payload.coerce().read(); - // priority - #name::SQ::get().split().0.enqueue_unchecked(slot); - #name::HANDLER(#name::Context::new(bl, payload)); + let node = core::ptr::read(::#name::BUFFER.get_unchecked(index as usize)); + #name::SQ::get().split().0.enqueue_unchecked(index); + #name::HANDLER(#name::Context::new(node.baseline, node.payload)); } } } else { quote! { #__priority::Task::#name => { - let (payload, slot) = payload.coerce().read(); - // priority - #name::SQ::get().split().0.enqueue_unchecked(slot); - #name::HANDLER(#name::Context::new(payload)); + let node = core::ptr::read(::#name::BUFFER.get_unchecked(index as usize)); + #name::SQ::get().split().0.enqueue_unchecked(index); + #name::HANDLER(#name::Context::new(node.payload)); } } } @@ -675,8 +689,8 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { use #hidden::#krate::Resource; // NOTE(get) the dispatcher is the only consumer of this queue - while let Some(payload) = #__priority::Q::get().split().1.dequeue() { - match payload.tag() { + while let Some((task, index)) = #__priority::Q::get().split().1.dequeue() { + match task { #(#arms,)* } } @@ -691,16 +705,9 @@ pub fn app(ctxt: &Context, app: &App) -> Tokens { let input = &task.input; if let Either::Right(capacity) = task.interrupt_or_capacity { - let capacity = capacity as usize; - pre_init.push(quote! { - { - static mut N: [#hidden::#krate::Node<#input>; #capacity] = - unsafe { #hidden::#krate::uninitialized() }; - - for node in N.iter_mut() { - #name::SQ::get().enqueue_unchecked(node.into()); - } + for i in 0..#capacity { + #name::SQ::get().enqueue_unchecked(i); } }) } diff --git a/src/lib.rs b/src/lib.rs index e60c915d21..67ae649dca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,13 +37,12 @@ pub use typenum::{Max, Maximum, Unsigned}; pub use instant::Instant; pub use node::Node; -use node::{Slot, TaggedPayload}; pub use resource::{Resource, Threshold}; #[cfg(feature = "timer-queue")] -pub use tq::{dispatch, TimerQueue}; +pub use tq::{dispatch, Message, TimerQueue}; -pub type PayloadQueue = RingBuffer, N, u8>; -pub type SlotQueue = RingBuffer, N, u8>; +pub type PayloadQueue = RingBuffer<(T, u8), N, u8>; +pub type SlotQueue = RingBuffer; pub type Ceiling = ::Ceiling; pub struct Core { diff --git a/src/node.rs b/src/node.rs index 43794a8d4a..0950d994bc 100644 --- a/src/node.rs +++ b/src/node.rs @@ -4,171 +4,11 @@ use core::{mem, ptr}; use instant::Instant; #[doc(hidden)] -#[repr(C)] pub struct Node where T: 'static, { #[cfg(feature = "timer-queue")] - baseline: Instant, - payload: T, -} - -#[cfg(feature = "timer-queue")] -impl Eq for Node {} - -#[cfg(feature = "timer-queue")] -impl PartialEq for Node { - fn eq(&self, other: &Node) -> bool { - self.baseline == other.baseline - } -} - -#[cfg(feature = "timer-queue")] -impl Ord for Node { - fn cmp(&self, other: &Node) -> Ordering { - self.baseline.cmp(&other.baseline) - } -} - -#[cfg(feature = "timer-queue")] -impl PartialOrd for Node { - fn partial_cmp(&self, other: &Node) -> Option { - Some(self.cmp(other)) - } -} - -#[doc(hidden)] -pub struct Slot -where - T: 'static, -{ - node: &'static mut Node, -} - -impl Slot { - #[cfg(feature = "timer-queue")] - pub fn write(self, bl: Instant, data: T) -> Payload { - self.node.baseline = bl; - unsafe { ptr::write(&mut self.node.payload, data) } - Payload { node: self.node } - } - - #[cfg(not(feature = "timer-queue"))] - pub fn write(self, data: T) -> Payload { - unsafe { ptr::write(&mut self.node.payload, data) } - Payload { node: self.node } - } -} - -impl Into> for &'static mut Node { - fn into(self) -> Slot { - Slot { node: self } - } -} - -#[doc(hidden)] -pub struct Payload -where - T: 'static, -{ - node: &'static mut Node, -} - -impl Payload { - #[cfg(feature = "timer-queue")] - pub fn read(self) -> (Instant, T, Slot) { - let data = unsafe { ptr::read(&self.node.payload) }; - (self.node.baseline, data, Slot { node: self.node }) - } - - #[cfg(not(feature = "timer-queue"))] - pub fn read(self) -> (T, Slot) { - let data = unsafe { ptr::read(&self.node.payload) }; - (data, Slot { node: self.node }) - } - - pub fn tag(self, tag: A) -> TaggedPayload - where - A: Copy, - { - TaggedPayload { - tag, - payload: unsafe { mem::transmute(self) }, - } - } -} - -#[doc(hidden)] -pub struct TaggedPayload -where - A: Copy, -{ - tag: A, - payload: Payload, -} - -impl TaggedPayload -where - A: Copy, -{ - pub unsafe fn coerce(self) -> Payload { - mem::transmute(self.payload) - } - - #[cfg(feature = "timer-queue")] - pub fn baseline(&self) -> Instant { - self.payload.node.baseline - } - - pub fn tag(&self) -> A { - self.tag - } - - pub fn retag(self, tag: B) -> TaggedPayload - where - B: Copy, - { - TaggedPayload { - tag, - payload: self.payload, - } - } -} - -#[cfg(feature = "timer-queue")] -impl Eq for TaggedPayload -where - T: Copy, -{ -} - -#[cfg(feature = "timer-queue")] -impl Ord for TaggedPayload -where - T: Copy, -{ - fn cmp(&self, rhs: &Self) -> Ordering { - self.payload.node.cmp(&rhs.payload.node) - } -} - -#[cfg(feature = "timer-queue")] -impl PartialEq for TaggedPayload -where - T: Copy, -{ - fn eq(&self, rhs: &Self) -> bool { - self.payload.node.eq(&rhs.payload.node) - } -} - -#[cfg(feature = "timer-queue")] -impl PartialOrd for TaggedPayload -where - T: Copy, -{ - fn partial_cmp(&self, rhs: &Self) -> Option { - Some(self.cmp(rhs)) - } + pub baseline: Instant, + pub payload: T, } diff --git a/src/tq.rs b/src/tq.rs index 3bdb41ceb4..0a8e3ecd72 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -1,4 +1,4 @@ -use core::cmp; +use core::cmp::{self, Ordering}; use cortex_m::peripheral::{SCB, SYST}; use heapless::binary_heap::{BinaryHeap, Min}; @@ -6,14 +6,39 @@ use heapless::ArrayLength; use typenum::{Max, Maximum, Unsigned}; use instant::Instant; -use node::{Slot, TaggedPayload}; use resource::{Resource, Threshold}; +pub struct Message { + pub baseline: Instant, + pub index: u8, + pub task: T, +} + +impl Eq for Message {} + +impl Ord for Message { + fn cmp(&self, other: &Message) -> Ordering { + self.baseline.cmp(&other.baseline) + } +} + +impl PartialEq for Message { + fn eq(&self, other: &Message) -> bool { + self.baseline == other.baseline + } +} + +impl PartialOrd for Message { + fn partial_cmp(&self, other: &Message) -> Option { + Some(self.cmp(other)) + } +} + enum State where T: Copy, { - Payload(TaggedPayload), + Payload { task: T, index: u8 }, Baseline(Instant), Done, } @@ -21,16 +46,16 @@ where #[doc(hidden)] pub struct TimerQueue where - N: ArrayLength>, + N: ArrayLength>, T: Copy, { pub syst: SYST, - pub queue: BinaryHeap, N, Min>, + pub queue: BinaryHeap, N, Min>, } impl TimerQueue where - N: ArrayLength>, + N: ArrayLength>, T: Copy, { pub const fn new(syst: SYST) -> Self { @@ -41,10 +66,10 @@ where } #[inline] - pub unsafe fn enqueue(&mut self, bl: Instant, tp: TaggedPayload) { + pub unsafe fn enqueue(&mut self, m: Message) { if self.queue .peek() - .map(|head| bl < head.baseline()) + .map(|head| m.baseline < head.baseline) .unwrap_or(true) { self.syst.enable_interrupt(); @@ -52,25 +77,29 @@ where unsafe { (*SCB::ptr()).icsr.write(1 << 26) } } - self.queue.push_unchecked(tp); + self.queue.push_unchecked(m); } } pub fn dispatch(t: &mut Threshold

, tq: &mut TQ, mut f: F) where - F: FnMut(&mut Threshold

, TaggedPayload), + F: FnMut(&mut Threshold

, T, u8), Maximum: Unsigned, - N: 'static + ArrayLength>, + N: 'static + ArrayLength>, P: Unsigned + Max, T: 'static + Copy + Send, TQ: Resource>, { loop { let state = tq.claim_mut(t, |tq, _| { - if let Some(bl) = tq.queue.peek().map(|p| p.baseline()) { + if let Some(bl) = tq.queue.peek().map(|p| p.baseline) { if Instant::now() >= bl { // message ready - State::Payload(unsafe { tq.queue.pop_unchecked() }) + let m = unsafe { tq.queue.pop_unchecked() }; + State::Payload { + task: m.task, + index: m.index, + } } else { // set a new timeout State::Baseline(bl) @@ -83,7 +112,7 @@ where }); match state { - State::Payload(p) => f(t, p), + State::Payload { task, index } => f(t, task, index), State::Baseline(bl) => { const MAX: u32 = 0x00ffffff;