Skip to content

Commit

Permalink
get-test-job now uses subtest duration
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Kent Overstreet committed Jul 14, 2024
1 parent 349da30 commit a0220af
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/bin/gen-avg-duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
62 changes: 54 additions & 8 deletions src/bin/get-test-job.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<TestJob>, commit: &str, test: &str) -> bool {
if let Some(job) = job {
if job.commit == commit && job.test == test {
Expand All @@ -44,7 +48,34 @@ 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 = 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<durations::Reader>) -> Option<TestJob> {
let file = OpenOptions::new()
.read(true)
.write(true)
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand All @@ -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);
Expand Down
10 changes: 6 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down

0 comments on commit a0220af

Please sign in to comment.