[feat] dynamic batches in 20ms frame-rate

This commit is contained in:
2025-07-28 22:07:07 +02:00
parent 1fa53efc72
commit d8190bdff1
2 changed files with 60 additions and 10 deletions

View File

@@ -1,7 +1,7 @@
[package]
name = "slowcat"
version = "1.0.0"
edition = "2021"
edition = "2024"
[dependencies]

View File

@@ -1,20 +1,70 @@
use std::{
env,
io::{stdin, stdout, Read, StdoutLock, Write},
time::Duration,
io::{Read, StdoutLock, Write, stdin, stdout},
time::{Duration, Instant},
};
static DEFAULT_HZ: u32 = 100;
fn output_slow(data: &[u8], hz: u32, out: &mut StdoutLock) {
// write 1 byte at a time
data.iter().for_each(|&b| {
let interval = Duration::from_secs_f64(1.0 / hz as f64);
// For very high frequencies, use adaptive batching
if hz > 1000 {
adaptive_batch_output(data, interval, out);
} else {
// Original behavior for lower frequencies
let mut next_time = Instant::now();
for &b in data {
out.write_all(&[b]).unwrap();
out.flush().unwrap();
// sleep according to given frequency
std::thread::sleep(Duration::from_secs_f64(1.0 / hz as f64));
});
next_time += interval;
let now = Instant::now();
if next_time > now {
std::thread::sleep(next_time - now);
}
}
}
}
fn adaptive_batch_output(data: &[u8], interval: Duration, out: &mut StdoutLock) {
let frame_duration = Duration::from_millis(20); // 20ms target window
let mut batch_size = 1usize;
let mut i = 0;
let mut last_batch_start = Instant::now();
while i < data.len() {
let batch_start = Instant::now();
let end = (i + batch_size).min(data.len());
let actual_bytes = end - i;
// Output the batch
out.write_all(&data[i..end]).unwrap();
out.flush().unwrap();
let batch_duration = batch_start.elapsed();
i = end;
// Calculate how many bytes we should output per 20ms window
let target_bytes_per_frame = (frame_duration.as_secs_f64() / interval.as_secs_f64()) as usize;
// Calculate optimal batch size based on measured throughput
if batch_duration.as_secs_f64() > 0.0 {
let measured_bytes_per_sec = actual_bytes as f64 / batch_duration.as_secs_f64();
let optimal_batch_size = (measured_bytes_per_sec * frame_duration.as_secs_f64()) as usize;
batch_size = optimal_batch_size.max(1).min(target_bytes_per_frame);
}
// Sleep for the expected time for this batch
let expected_sleep = interval * actual_bytes as u32;
let elapsed_since_batch_start = last_batch_start.elapsed();
if expected_sleep > elapsed_since_batch_start {
std::thread::sleep(expected_sleep - elapsed_since_batch_start);
}
last_batch_start = batch_start;
}
}
fn main() {