use std::fmt;
use crate::{
InterferenceError, InterferenceProblem, SamplingCertificate, SamplingPlane, SamplingPolicy,
SamplingThresholds,
};
const PHASOR_RESULT_BYTES_PER_CELL: u64 = 2 * size_of::<f64>() as u64;
const CERTIFICATE_STENCIL_POINTS_PER_CELL: u64 = 7;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WorkMetric {
Cells,
EmitterEvaluations,
HostBytes,
ResultBytes,
CertificateStencilWork,
}
impl fmt::Display for WorkMetric {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Cells => "cells",
Self::EmitterEvaluations => "emitter-evaluations",
Self::HostBytes => "host-bytes",
Self::ResultBytes => "result-bytes",
Self::CertificateStencilWork => "certificate-stencil-work",
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WorkEstimate {
pub cells: u64,
pub emitters: u64,
pub emitter_evaluations: u64,
pub host_bytes: u64,
pub result_bytes: u64,
pub certificate_stencil_work: u64,
}
impl WorkEstimate {
pub fn new(cells: u64, emitters: u64) -> Result<Self, InterferenceError> {
let emitter_evaluations = checked_product(cells, emitters, WorkMetric::EmitterEvaluations)?;
let result_bytes =
checked_product(cells, PHASOR_RESULT_BYTES_PER_CELL, WorkMetric::ResultBytes)?;
let host_bytes = result_bytes;
let certificate_stencil_work = checked_product(
cells,
CERTIFICATE_STENCIL_POINTS_PER_CELL,
WorkMetric::CertificateStencilWork,
)?;
Ok(Self {
cells,
emitters,
emitter_evaluations,
host_bytes,
result_bytes,
certificate_stencil_work,
})
}
pub fn for_request(
problem: &InterferenceProblem,
plane: &SamplingPlane,
) -> Result<Self, InterferenceError> {
let cells = u64::try_from(plane.cell_count()).map_err(|_| {
InterferenceError::WorkEstimateOverflow {
metric: WorkMetric::Cells,
}
})?;
let emitters = u64::try_from(problem.sources.len()).map_err(|_| {
InterferenceError::WorkEstimateOverflow {
metric: WorkMetric::EmitterEvaluations,
}
})?;
Self::new(cells, emitters)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WorkBudget {
pub max_cells: u64,
pub max_emitter_evaluations: u64,
pub max_host_bytes: u64,
pub max_result_bytes: u64,
pub max_certificate_stencil_work: u64,
}
impl WorkBudget {
pub fn admit(self, estimate: &WorkEstimate) -> Result<(), InterferenceError> {
for (metric, requested, limit) in [
(WorkMetric::Cells, estimate.cells, self.max_cells),
(
WorkMetric::EmitterEvaluations,
estimate.emitter_evaluations,
self.max_emitter_evaluations,
),
(
WorkMetric::HostBytes,
estimate.host_bytes,
self.max_host_bytes,
),
(
WorkMetric::ResultBytes,
estimate.result_bytes,
self.max_result_bytes,
),
(
WorkMetric::CertificateStencilWork,
estimate.certificate_stencil_work,
self.max_certificate_stencil_work,
),
] {
if requested > limit {
return Err(InterferenceError::WorkBudgetExceeded {
metric,
estimate: requested,
limit,
});
}
}
Ok(())
}
}
impl Default for WorkBudget {
fn default() -> Self {
const MAX_CELLS: u64 = 4_096 * 4_096;
Self {
max_cells: MAX_CELLS,
max_emitter_evaluations: 2_000_000_000,
max_host_bytes: 1 << 30,
max_result_bytes: 1 << 29,
max_certificate_stencil_work: MAX_CELLS * CERTIFICATE_STENCIL_POINTS_PER_CELL,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RequestPreflight {
pub sampling_policy: SamplingPolicy,
pub sampling_certificate: SamplingCertificate,
pub work_estimate: WorkEstimate,
}
impl RequestPreflight {
pub fn admit(
problem: &InterferenceProblem,
plane: &SamplingPlane,
sampling_policy: SamplingPolicy,
sampling_thresholds: SamplingThresholds,
work_budget: WorkBudget,
) -> Result<Self, InterferenceError> {
let work_estimate = WorkEstimate::for_request(problem, plane)?;
work_budget.admit(&work_estimate)?;
let sampling_certificate =
SamplingCertificate::measure_with_thresholds(problem, plane, sampling_thresholds)?;
sampling_policy.admit(&sampling_certificate)?;
Ok(Self {
sampling_policy,
sampling_certificate,
work_estimate,
})
}
}
fn checked_product(left: u64, right: u64, metric: WorkMetric) -> Result<u64, InterferenceError> {
left.checked_mul(right)
.ok_or(InterferenceError::WorkEstimateOverflow { metric })
}