[feat] dynamic batches in 20ms frame-rate
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "slowcat"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
68
src/main.rs
68
src/main.rs
@@ -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| {
|
||||
out.write_all(&[b]).unwrap();
|
||||
out.flush().unwrap();
|
||||
let interval = Duration::from_secs_f64(1.0 / hz as f64);
|
||||
|
||||
// sleep according to given frequency
|
||||
std::thread::sleep(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();
|
||||
|
||||
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() {
|
||||
|
Reference in New Issue
Block a user