From a0220af8f10cf0065afae290a5241c2d0da35708 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 14 Jul 2024 18:15:35 -0400 Subject: [PATCH] get-test-job now uses subtest duration Some tests run much quicker than others - but when we give out test jobs, we want to give out a similar amount of work each time - say 5 minutes. This uses the table generated by gen-avg-duration to give out work up to the amount defined by the config file. Signed-off-by: Kent Overstreet --- src/bin/gen-avg-duration.rs | 4 ++- src/bin/get-test-job.rs | 62 ++++++++++++++++++++++++++++++++----- src/lib.rs | 10 +++--- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/bin/gen-avg-duration.rs b/src/bin/gen-avg-duration.rs index 2968895..8deaa57 100644 --- a/src/bin/gen-avg-duration.rs +++ b/src/bin/gen-avg-duration.rs @@ -67,7 +67,9 @@ fn write_durations_capnp(rc: &Ktestrc, durations_in: TestDurationMap) { serialize::write_message(&mut out, &message).unwrap(); drop(out); - std::fs::rename(fname_new, fname).unwrap(); + std::fs::rename(fname_new, &fname).unwrap(); + + println!("wrote durations for {} tests to {}", durations_in.len(), fname.display()); } fn main() { diff --git a/src/bin/get-test-job.rs b/src/bin/get-test-job.rs index 5d48a95..ab0608b 100644 --- a/src/bin/get-test-job.rs +++ b/src/bin/get-test-job.rs @@ -1,5 +1,6 @@ extern crate libc; use std::collections::HashSet; +use std::fs::File; use std::path::Path; use std::process; use ci_cgi::{Ktestrc, ciconfig_read, lockfile_exists, commit_update_results_from_fs, subtest_full_name}; @@ -34,6 +35,9 @@ use memmap::MmapOptions; use std::fs::OpenOptions; use std::str; +use ci_cgi::durations_capnp::durations; +use capnp::serialize; + fn commit_test_matches(job: &Option, commit: &str, test: &str) -> bool { if let Some(job) = job { if job.commit == commit && job.test == test { @@ -44,7 +48,34 @@ fn commit_test_matches(job: &Option, commit: &str, test: &str) -> bool false } -fn get_test_job(rc: &Ktestrc) -> Option { +fn test_duration(durations: &Option, test: &str, subtest: &str) -> Option { + use std::cmp::Ordering::*; + + if let Some(d) = durations.as_ref() { + let full_test = test.to_owned() + "." + subtest; + let full_test = full_test.as_str(); + + let d = d.get_entries().unwrap(); + + let mut l = 0; + let mut r = d.len(); + + while l < r { + let m = l + (r - l) / 2; + let d_m = d.get(m); + + match full_test.cmp(d_m.get_test().unwrap().to_str().unwrap()) { + Less => l = m, + Equal => return Some(d_m.get_duration()), + Greater => r = m, + } + } + } + + None +} + +fn get_test_job(args: &Args, rc: &Ktestrc, durations: &Option) -> Option { let file = OpenOptions::new() .read(true) .write(true) @@ -54,6 +85,8 @@ fn get_test_job(rc: &Ktestrc) -> Option { return None; } + let mut duration_sum: u64 = 0; + let map = unsafe { MmapOptions::new().map(&file).unwrap() }; let mut ret = None; @@ -82,12 +115,20 @@ fn get_test_job(rc: &Ktestrc) -> Option { len = job.as_ptr() as u64 - map.as_ptr() as u64; } else if commit_test_matches(&ret, commit, test) { if let Some(ref mut r) = ret { - r.subtests.push(subtest.to_string()); - len = job.as_ptr() as u64 - map.as_ptr() as u64; + let duration_secs = test_duration(durations, test, subtest); + + if args.verbose { + println!("duration for {}.{}={:?}", test, subtest, duration_secs); + } + + let duration_secs = duration_secs.unwrap_or(rc.subtest_duration_def); - if r.subtests.len() > 20 { + if duration_sum != 0 && duration_sum + duration_secs > rc.subtest_duration_max { break; } + + duration_sum += duration_secs; + r.subtests.push(subtest.to_string()); } } else { break; @@ -117,9 +158,9 @@ fn create_job_lockfiles(rc: &Ktestrc, mut job: TestJob) -> Option { if !job.subtests.is_empty() { Some(job) } else { None } } -fn get_and_lock_job(rc: &Ktestrc) -> Option { +fn get_and_lock_job(args: &Args, rc: &Ktestrc, durations: &Option) -> Option { loop { - let job = get_test_job(rc); + let job = get_test_job(args, rc, durations); if let Some(job) = job { let job = create_job_lockfiles(rc, job); if job.is_some() { @@ -143,13 +184,18 @@ fn main() { let rc = rc.unwrap(); let rc = rc.ktest; + let durations_file = File::open(rc.output_dir.join("test_durations.capnp")).ok(); + let durations_map = durations_file.map(|x| unsafe { MmapOptions::new().map(&x).ok() } ).flatten(); + let durations_reader = durations_map.as_ref().map(|x| serialize::read_message_from_flat_slice(&mut &x[..], capnp::message::ReaderOptions::new()).ok()).flatten(); + let durations = durations_reader.as_ref().map(|x| x.get_root::().ok()).flatten(); + let lockfile = rc.output_dir.join("jobs.lock"); let filelock = FileLock::lock(lockfile, true, FileOptions::new().create(true).write(true)).unwrap(); let job = if !args.dry_run { - get_and_lock_job(&rc) + get_and_lock_job(&args, &rc, &durations) } else { - get_test_job(&rc) + get_test_job(&args, &rc, &durations) }; drop(filelock); diff --git a/src/lib.rs b/src/lib.rs index a503c52..d1c63e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,10 +33,12 @@ pub fn git_get_commit(repo: &git2::Repository, reference: String) -> Result anyhow::Result {