[feat] dynamic batches in 20ms frame-rate
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "slowcat"
|
name = "slowcat"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
68
src/main.rs
68
src/main.rs
@@ -1,20 +1,70 @@
|
|||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
io::{stdin, stdout, Read, StdoutLock, Write},
|
io::{Read, StdoutLock, Write, stdin, stdout},
|
||||||
time::Duration,
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFAULT_HZ: u32 = 100;
|
static DEFAULT_HZ: u32 = 100;
|
||||||
|
|
||||||
fn output_slow(data: &[u8], hz: u32, out: &mut StdoutLock) {
|
fn output_slow(data: &[u8], hz: u32, out: &mut StdoutLock) {
|
||||||
// write 1 byte at a time
|
let interval = Duration::from_secs_f64(1.0 / hz as f64);
|
||||||
data.iter().for_each(|&b| {
|
|
||||||
out.write_all(&[b]).unwrap();
|
|
||||||
out.flush().unwrap();
|
|
||||||
|
|
||||||
// sleep according to given frequency
|
// For very high frequencies, use adaptive batching
|
||||||
std::thread::sleep(Duration::from_secs_f64(1.0 / hz as f64));
|
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() {
|
fn main() {
|
||||||
|
Reference in New Issue
Block a user