jouleclaw-energy: another process's GPU client going back is not this pid's problem #278
1 changed files with 51 additions and 2 deletions
|
|
@ -282,6 +282,25 @@ fn oracle(rail: &OracleRail) -> Oracle {
|
|||
/// the clients in the second (one new there counting its whole time), and the
|
||||
/// IDs of the clients it saw at the first and not the second. `None` if a
|
||||
/// client's time went back.
|
||||
/// The GPU time `pid`'s own clients ran across the window, and nothing else.
|
||||
///
|
||||
/// [`oracle_times`] totals *every* client and gives up if any one of their
|
||||
/// counters went back, because its `all` must be exact to compare against a
|
||||
/// share. A caller that only wants one pid's own time does not need that: a
|
||||
/// client of some other process being destroyed, or its id reused, says nothing
|
||||
/// about this pid's clients and must not take the figure away. Only this pid's
|
||||
/// own clients going back makes it unavailable.
|
||||
fn oracle_pid_time(a: &Oracle, b: &Oracle, pid: i32) -> Option<u64> {
|
||||
let mut me = 0u64;
|
||||
for (id, (p, ns)) in &b.clients {
|
||||
if *p != pid {
|
||||
continue;
|
||||
}
|
||||
me += ns.checked_sub(a.clients.get(id).map_or(0, |c| c.1))?;
|
||||
}
|
||||
Some(me)
|
||||
}
|
||||
|
||||
fn oracle_times(a: &Oracle, b: &Oracle, pid: i32) -> Option<(u64, u64, Vec<u64>)> {
|
||||
let (mut me, mut all) = (0u64, 0u64);
|
||||
for (id, (p, ns)) in &b.clients {
|
||||
|
|
@ -429,6 +448,36 @@ fn assert_exact_zero(what: &str, r: &Result<GpuShare, EnergyError>) {
|
|||
assert_eq!((g.uj, g.self_ns, g.rail_uj, g.all_ns, g.provenance()), (0, 0, None, None, Provenance::HwShunt), "{what}");
|
||||
}
|
||||
|
||||
/// A client of some *other* process going back must not take this pid's own GPU
|
||||
/// figure away. [`oracle_times`] gives up on it, because its `all` has to be
|
||||
/// exact to compare against a share; [`oracle_pid_time`] does not, because it
|
||||
/// totals this pid's clients and no others. Deterministic: it builds the two
|
||||
/// readings rather than waiting for a client to be destroyed on the host.
|
||||
#[test]
|
||||
fn another_process_client_going_back_leaves_this_pids_gpu_time_readable() {
|
||||
const ME: i32 = 4242;
|
||||
const OTHER: i32 = 99;
|
||||
let at = Instant::now();
|
||||
let mk = |clients: &[(u64, i32, u64)]| Oracle {
|
||||
rail_nj: 0,
|
||||
rail_at: at,
|
||||
clients: clients.iter().map(|(id, pid, ns)| (*id, (*pid, *ns))).collect(),
|
||||
clients_from: at,
|
||||
clients_to: at,
|
||||
};
|
||||
// This pid's client advances 5 ms; another process's client goes back.
|
||||
let a = mk(&[(1, ME, 1_000_000), (2, OTHER, 9_000_000)]);
|
||||
let b = mk(&[(1, ME, 6_000_000), (2, OTHER, 1_000_000)]);
|
||||
assert_eq!(oracle_times(&a, &b, ME), None, "oracle_times totals every client, so one going back ends it");
|
||||
assert_eq!(oracle_pid_time(&a, &b, ME), Some(5_000_000), "this pid's own time is still readable");
|
||||
// This pid's own client going back is still no figure.
|
||||
let c = mk(&[(1, ME, 500_000), (2, OTHER, 9_000_000)]);
|
||||
assert_eq!(oracle_pid_time(&a, &c, ME), None, "this pid's own client going back makes it unavailable");
|
||||
// No client of this pid at all is zero, not unavailable.
|
||||
let d = mk(&[(2, OTHER, 9_000_000)]);
|
||||
assert_eq!(oracle_pid_time(&d, &d, ME), Some(0), "a pid with no clients ran no GPU time");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_gpu_span_is_a_sole_users_rail_or_an_exact_zero_or_no_figure() {
|
||||
if let Ok(secs) = std::env::var(CHILD_ENV) {
|
||||
|
|
@ -521,7 +570,7 @@ fn a_gpu_span_is_a_sole_users_rail_or_an_exact_zero_or_no_figure() {
|
|||
// a. No GPU work here beside it: still an exact zero, while the oracle
|
||||
// sees the child's GPU time in the window.
|
||||
let (s, o0, o1, e) = bracketed(&counter, &rail, || std::thread::sleep(Duration::from_millis(400)));
|
||||
let (child_ns, _, _) = oracle_times(&o0, &o1, child_pid).expect("no client's time went back in the oracle's window");
|
||||
let child_ns = oracle_pid_time(&o0, &o1, child_pid).expect("the child's own GPU time went back in the oracle's window");
|
||||
assert_exact_zero(&format!("400 ms idle beside a child that ran {:.1} ms on the GPU", ms(child_ns)), &counter.share(&s, &e));
|
||||
assert!(child_ns >= 20_000_000, "the child did not contend: {} ms", ms(child_ns));
|
||||
// b. GPU work here beside it: `GpuShared`, at the oracle's times.
|
||||
|
|
@ -534,7 +583,7 @@ fn a_gpu_span_is_a_sole_users_rail_or_an_exact_zero_or_no_figure() {
|
|||
line("1 s GPU burn beside a burning child", &r);
|
||||
let Err(EnergyError::GpuShared { self_ns, all_ns }) = r else { panic!("a shared GPU gave {r:?}") };
|
||||
agrees_with_oracle("1 s GPU burn beside a burning child", self_ns, all_ns, &s, &e, &o0, &o1);
|
||||
let (child_ns, _, _) = oracle_times(&o0, &o1, child_pid).expect("no client's time went back in the oracle's window");
|
||||
let child_ns = oracle_pid_time(&o0, &o1, child_pid).expect("the child's own GPU time went back in the oracle's window");
|
||||
println!("process-gpu: oracle: the child ran {:.1} ms on the GPU in that window", ms(child_ns));
|
||||
assert!(child_ns >= 20_000_000 && self_ns > 0, "the child did not contend: {} ms", ms(child_ns));
|
||||
assert!(u128::from(self_ns) * 100 < u128::from(all_ns) * 99);
|
||||
|
|
|
|||
Loading…
Reference in a new issue