use super::types::should_use_lazy_spatial_design;
use super::*;
use gam_runtime::resource::{DerivativeStorageMode, ResourcePolicy};
use gam_runtime::test_support::simulated_cgroup_memory_environment;
use ndarray::Array2;
const REPORTED_DESIGN_BYTES: usize = 300 * 12 * std::mem::size_of::<f64>();
const HOST_AVAILABLE_BYTES: u64 = 448_648_040_448;
const HOST_TOTAL_BYTES: u64 = 527_799_400 * 1024;
const JOB_LIMIT_BYTES: u64 = 6 * 1024 * 1024 * 1024;
fn fixture_data() -> Array2<f64> {
let rows = 300;
let mut data = Array2::<f64>::zeros((rows, 2));
for i in 0..rows {
let t = i as f64 / (rows as f64 - 1.0);
let u = ((i * 7) % rows) as f64 / (rows as f64 - 1.0);
data[[i, 0]] = t;
data[[i, 1]] = u;
}
data
}
fn fixture_spec() -> DuchonBasisSpec {
DuchonBasisSpec {
center_strategy: CenterStrategy::EqualMass { num_centers: 12 },
periodic: None,
length_scale: None,
power: 0.0,
nullspace_order: DuchonNullspaceOrder::Linear,
identifiability: SpatialIdentifiability::default(),
aniso_log_scales: None,
operator_penalties: Default::default(),
boundary: OneDimensionalBoundary::Open,
radial_reparam: None,
}
}
fn build_under(policy: ResourcePolicy) -> BasisBuildResult {
let data = fixture_data();
let spec = fixture_spec();
let mut workspace = BasisWorkspace::with_policy(policy);
build_duchon_basiswithworkspace(data.view(), &spec, &mut workspace)
.expect("the 300x12 Duchon fixture must build; refusing it is the defect under test")
}
#[test]
fn one_cgroup_read_at_two_load_levels_yields_one_policy_and_one_basis_2684() {
let idle = simulated_cgroup_memory_environment(
HOST_AVAILABLE_BYTES,
HOST_TOTAL_BYTES,
JOB_LIMIT_BYTES,
92_827_648,
);
let pinned = simulated_cgroup_memory_environment(
HOST_AVAILABLE_BYTES,
HOST_TOTAL_BYTES,
JOB_LIMIT_BYTES,
JOB_LIMIT_BYTES - 4_096,
);
assert!(
idle.available_bytes() > 6_000_000_000,
"idle arm must have the ceiling free, got {}",
idle.available_bytes()
);
assert_eq!(pinned.available_bytes(), 4_096);
assert_eq!(idle.capacity_bytes(), pinned.capacity_bytes());
assert_eq!(idle.capacity_bytes(), JOB_LIMIT_BYTES);
let idle_policy = ResourcePolicy::for_observed_memory(&idle);
let pinned_policy = ResourcePolicy::for_observed_memory(&pinned);
let availability_denominated_cap = (pinned.available_bytes() as usize) / 4 * 3;
assert!(
availability_denominated_cap < REPORTED_DESIGN_BYTES,
"the pinned arm must be a reading under which a free-memory ceiling would refuse the \
design, or this test cannot detect the defect: cap-from-free={availability_denominated_cap}, \
design={REPORTED_DESIGN_BYTES}"
);
assert_eq!(
idle_policy.max_single_materialization_bytes,
pinned_policy.max_single_materialization_bytes,
"the materialization ceiling moved with ambient load"
);
assert!(
pinned_policy.max_single_materialization_bytes > REPORTED_DESIGN_BYTES,
"a 6 GiB job must admit a 28,800-byte design however busy it is, got a cap of {}",
pinned_policy.max_single_materialization_bytes
);
let from_idle = build_under(idle_policy);
let from_pinned = build_under(pinned_policy);
assert_eq!(
(from_idle.design.nrows(), from_idle.design.ncols()),
(from_pinned.design.nrows(), from_pinned.design.ncols()),
"the basis changed SHAPE with ambient load"
);
let idle_columns = from_idle
.design
.as_dense_ref()
.expect("the 6 GiB-capacity arm routes dense")
.clone();
let pinned_columns = from_pinned
.design
.as_dense_ref()
.expect("the 6 GiB-capacity arm routes dense under load too");
assert_eq!(
idle_columns.dim(),
pinned_columns.dim(),
"dense design shapes disagree"
);
for (row, (left, right)) in idle_columns.iter().zip(pinned_columns.iter()).enumerate() {
assert_eq!(
left.to_bits(),
right.to_bits(),
"design element {row} differs between the idle and the loaded process: {left} vs {right}"
);
}
assert_eq!(
from_idle.active_penalties.len(),
from_pinned.active_penalties.len(),
"penalty count changed with ambient load"
);
for (index, (left, right)) in from_idle
.active_penalties
.iter()
.zip(from_pinned.active_penalties.iter())
.enumerate()
{
assert_eq!(
left.matrix.dim(),
right.matrix.dim(),
"penalty {index} changed shape with ambient load"
);
for (cell, (a, b)) in left.matrix.iter().zip(right.matrix.iter()).enumerate() {
assert_eq!(
a.to_bits(),
b.to_bits(),
"penalty {index} cell {cell} differs between the idle and the loaded process"
);
}
}
}
#[test]
fn the_ceiling_still_refuses_what_capacity_cannot_hold_2684() {
let tiny = simulated_cgroup_memory_environment(
HOST_AVAILABLE_BYTES,
HOST_TOTAL_BYTES,
4_096,
0,
);
assert_eq!(tiny.capacity_bytes(), 4_096);
assert_eq!(tiny.available_bytes(), 4_096);
let tiny_policy = ResourcePolicy::for_observed_memory(&tiny);
assert!(
tiny_policy.max_single_materialization_bytes < REPORTED_DESIGN_BYTES,
"a 4 KiB cgroup must not admit a 28,800-byte dense design"
);
assert!(
should_use_lazy_spatial_design(300, 12, &tiny_policy),
"the 300x12 design must route streamed when capacity cannot hold it"
);
let roomy = simulated_cgroup_memory_environment(
HOST_AVAILABLE_BYTES,
HOST_TOTAL_BYTES,
JOB_LIMIT_BYTES,
JOB_LIMIT_BYTES - 4_096,
);
let roomy_policy = ResourcePolicy::for_observed_memory(&roomy);
assert!(
!should_use_lazy_spatial_design(300, 12, &roomy_policy),
"the same design under a 6 GiB ceiling must route dense, however loaded the cgroup is"
);
}
#[test]
fn the_storage_route_changes_how_the_basis_is_carried_not_which_basis_it_is_2684() {
let roomy = simulated_cgroup_memory_environment(
HOST_AVAILABLE_BYTES,
HOST_TOTAL_BYTES,
JOB_LIMIT_BYTES,
92_827_648,
);
let dense_policy = ResourcePolicy::for_observed_memory(&roomy);
let streamed_policy = ResourcePolicy {
derivative_storage_mode: DerivativeStorageMode::AnalyticOperatorRequired,
..dense_policy.clone()
};
let dense = build_under(dense_policy);
let streamed = build_under(streamed_policy);
assert!(
dense.design.is_materialized_dense(),
"the permissive arm must actually materialize, or the routes are not being contrasted"
);
assert!(
!streamed.design.is_materialized_dense(),
"the strict arm must actually stream, or the routes are not being contrasted"
);
assert_eq!(
(dense.design.nrows(), dense.design.ncols()),
(streamed.design.nrows(), streamed.design.ncols()),
"the storage route changed the WIDTH of the basis, which is the #2684 defect one layer in: \
the column space a fit works in must not depend on a memory decision"
);
assert_eq!(
dense.active_penalties.len(),
streamed.active_penalties.len(),
"the storage route changed how many penalties the basis carries"
);
for (index, (left, right)) in dense
.active_penalties
.iter()
.zip(streamed.active_penalties.iter())
.enumerate()
{
assert_eq!(
left.matrix.dim(),
right.matrix.dim(),
"penalty {index} changed shape with the storage route"
);
}
}