perf(journal): use incremental refresh instead of re-reading entire file

Track a byte offset so refresh() only parses lines appended since the
last read, reducing total I/O from O(n²) to O(n) over an n-trial run.
This commit is contained in:
Manuel Raimann
2026-02-12 14:43:24 +01:00
parent 5da63d8976
commit ddbbe294bc
2 changed files with 144 additions and 17 deletions
+56
View File
@@ -3,6 +3,8 @@
use std::collections::HashMap;
use std::sync::Arc;
use std::io::Write;
use optimizer::parameter::{FloatParam, Parameter};
use optimizer::sampler::CompletedTrial;
use optimizer::sampler::random::RandomSampler;
@@ -317,3 +319,57 @@ fn accepts_valid_journal_with_distributions() {
std::fs::remove_file(&path).ok();
}
#[test]
fn refresh_skips_own_writes() {
let path = temp_path();
let storage = JournalStorage::new(&path);
for i in 0..5 {
storage.push(sample_trial(i, i as f64));
// Our own push advanced the offset, so refresh should find nothing new.
assert!(!storage.refresh(), "refresh returned true after push {i}");
}
assert_eq!(storage.trials_arc().read().len(), 5);
std::fs::remove_file(&path).ok();
}
#[test]
fn refresh_picks_up_external_writes() {
let path = temp_path();
let storage = JournalStorage::new(&path);
// Push 3 trials through the storage (advances offset).
for i in 0..3 {
storage.push(sample_trial(i, i as f64));
}
assert_eq!(storage.trials_arc().read().len(), 3);
// Simulate an external process appending 2 more lines directly.
{
let mut file = std::fs::OpenOptions::new()
.append(true)
.open(&path)
.unwrap();
for i in 3..5u64 {
let trial = sample_trial(i, i as f64);
let line = serde_json::to_string(&trial).unwrap();
writeln!(file, "{line}").unwrap();
}
file.sync_all().unwrap();
}
// refresh() should pick up the 2 external trials.
assert!(storage.refresh(), "refresh should detect external writes");
assert_eq!(storage.trials_arc().read().len(), 5);
// A second refresh should be a no-op.
assert!(
!storage.refresh(),
"second refresh should return false (no new data)"
);
assert_eq!(storage.trials_arc().read().len(), 5);
std::fs::remove_file(&path).ok();
}