From c4c8163b0e697f29da39b1b2fe1c495f23679d82 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Sat, 28 Mar 2020 16:03:30 +0300 Subject: [PATCH] optimize/timers: Get rid of the while loop in Timer::update. Profiling with 'perf' I discovered that too much time was spent inside Timer::update when the prescalar was 0 (frequency of 1 cycle). This was due to the while loop I used. I optimized this loop out and there is an appearent performance increase. Former-commit-id: d228450625fad003f054e3161f6eeee888245cac --- src/core/timer.rs | 74 +++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/core/timer.rs b/src/core/timer.rs index a6b53f4..fb99204 100644 --- a/src/core/timer.rs +++ b/src/core/timer.rs @@ -5,18 +5,19 @@ use super::sysbus::SysBus; use num::FromPrimitive; use serde::{Deserialize, Serialize}; +const SHIFT_LUT: [usize; 4] = [0, 6, 8, 10]; + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Timer { // registers pub ctl: TimerCtl, pub data: u16, - - irq: Interrupt, - - timer_id: usize, pub initial_data: u16, - pub cycles: usize, + irq: Interrupt, + timer_id: usize, + cycles: usize, + prescalar_shift: usize, } impl Timer { @@ -31,37 +32,40 @@ impl Timer { ctl: TimerCtl(0), initial_data: 0, cycles: 0, + prescalar_shift: 0, } } - fn frequency(&self) -> usize { - match self.ctl.prescalar() { - 0 => 1, - 1 => 64, - 2 => 256, - 3 => 1024, - _ => unreachable!(), - } + #[inline] + fn ticks_to_overflow(&self) -> u32 { + 0x1_0000 - (self.data as u32) } - /// updates the timer with 'cycles' amount of cycles, returns the number of times it overflowed - fn update(&mut self, cycles: usize, irqs: &mut IrqBitmask) -> usize { - self.cycles += cycles; + /// increments the timer with an amount of ticks + /// returns the number of times it overflowed + fn update(&mut self, ticks: usize, irqs: &mut IrqBitmask) -> usize { + let mut ticks = ticks as u32; let mut num_overflows = 0; - let freq = self.frequency(); - while self.cycles >= freq { - self.cycles -= freq; - self.data = self.data.wrapping_add(1); - if self.data == 0 { - if self.ctl.irq_enabled() { - irqs.add_irq(self.irq); - } - self.data = self.initial_data; - num_overflows += 1; + + let ticks_remaining = self.ticks_to_overflow(); + + if ticks >= ticks_remaining { + num_overflows += 1; + ticks -= ticks_remaining; + self.data = self.initial_data; + + let ticks_remaining = self.ticks_to_overflow(); + num_overflows += ticks / ticks_remaining; + ticks = ticks % ticks_remaining; + + if self.ctl.irq_enabled() { + irqs.add_irq(self.irq); } } - num_overflows + self.data += ticks as u16; + + num_overflows as usize } } @@ -95,10 +99,13 @@ impl Timers { } pub fn write_timer_ctl(&mut self, id: usize, value: u16) { + let new_ctl = TimerCtl(value); let old_enabled = self[id].ctl.enabled(); - self[id].ctl.0 = value; - let new_enabled = self[id].ctl.enabled(); - let cascade = self.timers[id].ctl.cascade(); + let new_enabled = new_ctl.enabled(); + let cascade = new_ctl.cascade(); + self[id].cycles = 0; + self[id].prescalar_shift = SHIFT_LUT[new_ctl.prescalar() as usize]; + self[id].ctl = new_ctl; if new_enabled && !cascade { self.running_timers |= 1 << id; } else { @@ -164,7 +171,12 @@ impl Timers { if !self.timers[id].ctl.cascade() { let timer = &mut self.timers[id]; - let num_overflows = timer.update(cycles, irqs); + + let cycles = timer.cycles + cycles; + let inc = cycles >> timer.prescalar_shift; + let num_overflows = timer.update(inc, irqs); + timer.cycles = cycles & ((1 << timer.prescalar_shift) - 1); + if num_overflows > 0 { if id != 3 { let next_timer = &mut self.timers[id + 1];