Skip to content

Commit

Permalink
Use token bucket instead feedback rate limiting
Browse files Browse the repository at this point in the history
  • Loading branch information
YushiOMOTE committed Aug 2, 2024
1 parent 6d6c95b commit b90b7ae
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 59 deletions.
9 changes: 4 additions & 5 deletions core/examples/pc/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::time::{Duration, Instant};

use rgy::{Key, Stream, VRAM_HEIGHT, VRAM_WIDTH};

Expand All @@ -23,6 +23,7 @@ pub struct Hardware {
color: bool,
gamepad: Arc<Mutex<Gilrs>>,
gamepad_id: Option<GamepadId>,
instant: Instant,
}

struct Gui {
Expand Down Expand Up @@ -151,6 +152,7 @@ impl Hardware {
escape,
gamepad,
gamepad_id: None,
instant: Instant::now(),
}
}

Expand Down Expand Up @@ -214,10 +216,7 @@ impl rgy::Hardware for Hardware {
}

fn clock(&mut self) -> u64 {
let epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Couldn't get epoch");
epoch.as_micros() as u64
self.instant.elapsed().as_micros() as u64
}

fn load_ram(&mut self, size: usize) -> Vec<u8> {
Expand Down
12 changes: 4 additions & 8 deletions core/examples/pc/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,9 @@ pub struct Opt {
/// Cpu frequency
#[structopt(short = "f", long = "freq", default_value = "4200000")]
freq: u64,
/// Sampling rate for cpu frequency controller
#[structopt(short = "s", long = "sample", default_value = "4200")]
sample: u64,
/// Delay unit for cpu frequency controller
#[structopt(short = "u", long = "delayunit", default_value = "50")]
delay_unit: u64,
/// Interval to refill the token bucket for CPU rate-limiting.
#[structopt(short = "i", long = "interval", default_value = "20000")]
interval: u64,
/// Don't adjust cpu frequency
#[structopt(short = "n", long = "native")]
native_speed: bool,
Expand All @@ -44,8 +41,7 @@ fn to_cfg(opt: Opt) -> rgy::Config {
rgy::Config::new()
.color(opt.color)
.freq(opt.freq)
.sample(opt.sample)
.delay_unit(opt.delay_unit)
.rate_limit_interval(opt.interval)
.native_speed(opt.native_speed)
}

Expand Down
48 changes: 17 additions & 31 deletions core/src/fc.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
use crate::hardware::HardwareHandle;
use crate::system::Config;
use log::*;

pub struct FreqControl {
hw: HardwareHandle,
last: u64,
cycles: u64,
sample: u64,
delay: u64,
delay_unit: u64,
refill_interval: u64,
target_freq: u64,
remaining_cycles: u64,
}

impl FreqControl {
pub fn new(hw: HardwareHandle, cfg: &Config) -> Self {
Self {
hw,
last: 0,
cycles: 0,
delay: 0,
sample: cfg.sample,
delay_unit: cfg.delay_unit,
refill_interval: cfg.rate_limit_interval,
target_freq: cfg.freq,
remaining_cycles: 0,
}
}

Expand All @@ -30,35 +25,26 @@ impl FreqControl {
}

pub fn adjust(&mut self, time: usize) {
self.cycles += time as u64;
let consumed_cycles = time as u64;

for _ in 0..self.delay {
let _ = unsafe { core::ptr::read_volatile(&self.sample) };
}

if self.cycles > self.sample {
self.cycles -= self.sample;
self.try_fill();

let now = self.hw.get().borrow_mut().clock();
let (diff, of) = now.overflowing_sub(self.last);
if of || diff == 0 {
warn!("Overflow: {} - {}", self.last, now);
self.last = now;
return;
}
while self.remaining_cycles < consumed_cycles {
self.try_fill();
}

// get cycles per second
let freq = self.sample * 1_000_000 / diff;
self.remaining_cycles -= consumed_cycles;
}

debug!("Frequency: {}", freq);
fn try_fill(&mut self) {
let now = self.hw.get().borrow_mut().clock();

self.delay = if freq > self.target_freq {
self.delay.saturating_add(self.delay_unit)
} else {
self.delay.saturating_sub(self.delay_unit)
};
let diff = now.saturating_sub(self.last);

if diff >= self.refill_interval {
self.last = now;

self.remaining_cycles += self.target_freq * diff / 1_000_000;
}
}
}
21 changes: 6 additions & 15 deletions core/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ use log::*;
pub struct Config {
/// CPU frequency.
pub(crate) freq: u64,
/// Cycle sampling count in the CPU frequency controller.
pub(crate) sample: u64,
/// Delay unit in CPU frequency controller.
pub(crate) delay_unit: u64,
/// Interval to refill tokens in the token bucket for CPU rate-limiting in micro-seconds.
pub(crate) rate_limit_interval: u64,
/// Don't adjust CPU frequency.
pub(crate) native_speed: bool,
/// Emulate Gameboy Color
Expand All @@ -32,8 +30,7 @@ impl Config {

Self {
freq,
sample: freq / 1000,
delay_unit: 10,
rate_limit_interval: 20_000,
native_speed: false,
color: false,
}
Expand All @@ -45,15 +42,9 @@ impl Config {
self
}

/// Set the sampling count of the CPU frequency controller.
pub fn sample(mut self, sample: u64) -> Self {
self.sample = sample;
self
}

/// Set the delay unit.
pub fn delay_unit(mut self, delay: u64) -> Self {
self.delay_unit = delay;
/// Interval to refill tokens in the token bucket for CPU rate-limiting in micro-seconds.
pub fn rate_limit_interval(mut self, interval: u64) -> Self {
self.rate_limit_interval = interval;
self
}

Expand Down

0 comments on commit b90b7ae

Please sign in to comment.