diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2024-07-14 18:15:35 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2024-07-14 18:58:01 -0400 |
commit | dbae42c7300313f6062ebfe41b24f90c0b75ae9e (patch) | |
tree | 3e127936853ebaefd809cf782a3fd73f8e376677 | |
parent | 349da300a45e742c304e0ed51b78f046469fadcd (diff) |
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 <kent.overstreet@linux.dev>
-rw-r--r-- | src/bin/gen-avg-duration.rs | 4 | ||||
-rw-r--r-- | src/bin/gen-job-list.rs | 2 | ||||
-rw-r--r-- | src/bin/get-test-job.rs | 66 | ||||
-rw-r--r-- | src/lib.rs | 16 |
4 files changed, 69 insertions, 19 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/gen-job-list.rs b/src/bin/gen-job-list.rs index c03109f..c41fc29 100644 --- a/src/bin/gen-job-list.rs +++ b/src/bin/gen-job-list.rs @@ -118,7 +118,7 @@ fn branch_test_jobs(rc: &CiConfig, repo: &git2::Repository, let missing_subtests: Vec<_> = subtests .iter() .filter(|i| { - let full_subtest_name = subtest_full_name(&test_path, &i); + let full_subtest_name = subtest_full_name(&test_path.to_string_lossy(), &i); !have_result(&results, &full_subtest_name) && !lockfile_exists(&rc.ktest, &commit, &full_subtest_name, diff --git a/src/bin/get-test-job.rs b/src/bin/get-test-job.rs index 5d48a95..ff12edc 100644 --- a/src/bin/get-test-job.rs +++ b/src/bin/get-test-job.rs @@ -1,6 +1,6 @@ extern crate libc; use std::collections::HashSet; -use std::path::Path; +use std::fs::File; use std::process; use ci_cgi::{Ktestrc, ciconfig_read, lockfile_exists, commit_update_results_from_fs, subtest_full_name}; use ci_cgi::{Worker, workers_update}; @@ -34,6 +34,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<TestJob>, commit: &str, test: &str) -> bool { if let Some(job) = job { if job.commit == commit && job.test == test { @@ -44,7 +47,35 @@ fn commit_test_matches(job: &Option<TestJob>, commit: &str, test: &str) -> bool false } -fn get_test_job(rc: &Ktestrc) -> Option<TestJob> { +fn test_duration(durations: &Option<durations::Reader>, test: &str, subtest: &str) -> Option<u64> { + use std::cmp::Ordering::*; + + if let Some(d) = durations.as_ref() { + let full_test = subtest_full_name(test, 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); + let d_m_test = d_m.get_test().unwrap().to_str().unwrap(); + + match full_test.cmp(d_m_test) { + Less => r = m, + Equal => return Some(d_m.get_duration()), + Greater => l = m, + } + } + } + + None +} + +fn get_test_job(args: &Args, rc: &Ktestrc, durations: &Option<durations::Reader>) -> Option<TestJob> { let file = OpenOptions::new() .read(true) .write(true) @@ -54,6 +85,8 @@ fn get_test_job(rc: &Ktestrc) -> Option<TestJob> { 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<TestJob> { 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; @@ -104,7 +145,7 @@ fn create_job_lockfiles(rc: &Ktestrc, mut job: TestJob) -> Option<TestJob> { job.subtests = job.subtests.iter() .filter(|i| lockfile_exists(rc, &job.commit, - &subtest_full_name(&Path::new(&job.test), &i), + &subtest_full_name(&job.test, &i), true, &mut commits_updated)) .map(|i| i.to_string()) @@ -117,9 +158,9 @@ fn create_job_lockfiles(rc: &Ktestrc, mut job: TestJob) -> Option<TestJob> { if !job.subtests.is_empty() { Some(job) } else { None } } -fn get_and_lock_job(rc: &Ktestrc) -> Option<TestJob> { +fn get_and_lock_job(args: &Args, rc: &Ktestrc, durations: &Option<durations::Reader>) -> Option<TestJob> { 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::<durations::Reader>().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); @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashSet}; use std::fs::{File, OpenOptions, create_dir_all, read_to_string}; use std::io::ErrorKind; use std::io::prelude::*; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::time::SystemTime; use die::die; use serde_derive::Deserialize; @@ -33,10 +33,12 @@ pub fn git_get_commit(repo: &git2::Repository, reference: String) -> Result<git2 #[derive(Deserialize)] pub struct Ktestrc { - pub linux_repo: PathBuf, - pub output_dir: PathBuf, - pub ktest_dir: PathBuf, - pub users_dir: PathBuf, + pub linux_repo: PathBuf, + pub output_dir: PathBuf, + pub ktest_dir: PathBuf, + pub users_dir: PathBuf, + pub subtest_duration_max: u64, + pub subtest_duration_def: u64, } pub fn ktestrc_read() -> anyhow::Result<Ktestrc> { @@ -362,8 +364,8 @@ pub fn update_lcov(rc: &Ktestrc, commit_id: &String) -> Option<()> { Some(()) } -pub fn subtest_full_name(test: &Path, subtest: &String) -> String { - let test = test.to_string_lossy(); +pub fn subtest_full_name(test: &str, subtest: &str) -> String { + let test = test.to_owned(); let test = test.replace(".ktest", ""); let test = test + "." + subtest; let test = test.replace("/", "."); |