sim_lib_interference_core/
budget.rs1use std::fmt;
4
5use crate::{
6 InterferenceError, InterferenceProblem, SamplingCertificate, SamplingPlane, SamplingPolicy,
7 SamplingThresholds,
8};
9
10const PHASOR_RESULT_BYTES_PER_CELL: u64 = 2 * size_of::<f64>() as u64;
11const CERTIFICATE_STENCIL_POINTS_PER_CELL: u64 = 7;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum WorkMetric {
16 Cells,
18 EmitterEvaluations,
20 HostBytes,
22 ResultBytes,
24 CertificateStencilWork,
26}
27
28impl fmt::Display for WorkMetric {
29 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
30 formatter.write_str(match self {
31 Self::Cells => "cells",
32 Self::EmitterEvaluations => "emitter-evaluations",
33 Self::HostBytes => "host-bytes",
34 Self::ResultBytes => "result-bytes",
35 Self::CertificateStencilWork => "certificate-stencil-work",
36 })
37 }
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub struct WorkEstimate {
43 pub cells: u64,
45 pub emitters: u64,
47 pub emitter_evaluations: u64,
49 pub host_bytes: u64,
51 pub result_bytes: u64,
53 pub certificate_stencil_work: u64,
55}
56
57impl WorkEstimate {
58 pub fn new(cells: u64, emitters: u64) -> Result<Self, InterferenceError> {
63 let emitter_evaluations = checked_product(cells, emitters, WorkMetric::EmitterEvaluations)?;
64 let result_bytes =
65 checked_product(cells, PHASOR_RESULT_BYTES_PER_CELL, WorkMetric::ResultBytes)?;
66 let host_bytes = result_bytes;
67 let certificate_stencil_work = checked_product(
68 cells,
69 CERTIFICATE_STENCIL_POINTS_PER_CELL,
70 WorkMetric::CertificateStencilWork,
71 )?;
72 Ok(Self {
73 cells,
74 emitters,
75 emitter_evaluations,
76 host_bytes,
77 result_bytes,
78 certificate_stencil_work,
79 })
80 }
81
82 pub fn for_request(
84 problem: &InterferenceProblem,
85 plane: &SamplingPlane,
86 ) -> Result<Self, InterferenceError> {
87 let cells = u64::try_from(plane.cell_count()).map_err(|_| {
88 InterferenceError::WorkEstimateOverflow {
89 metric: WorkMetric::Cells,
90 }
91 })?;
92 let emitters = u64::try_from(problem.sources.len()).map_err(|_| {
93 InterferenceError::WorkEstimateOverflow {
94 metric: WorkMetric::EmitterEvaluations,
95 }
96 })?;
97 Self::new(cells, emitters)
98 }
99}
100
101#[derive(Clone, Copy, Debug, PartialEq, Eq)]
103pub struct WorkBudget {
104 pub max_cells: u64,
106 pub max_emitter_evaluations: u64,
108 pub max_host_bytes: u64,
110 pub max_result_bytes: u64,
112 pub max_certificate_stencil_work: u64,
114}
115
116impl WorkBudget {
117 pub fn admit(self, estimate: &WorkEstimate) -> Result<(), InterferenceError> {
121 for (metric, requested, limit) in [
122 (WorkMetric::Cells, estimate.cells, self.max_cells),
123 (
124 WorkMetric::EmitterEvaluations,
125 estimate.emitter_evaluations,
126 self.max_emitter_evaluations,
127 ),
128 (
129 WorkMetric::HostBytes,
130 estimate.host_bytes,
131 self.max_host_bytes,
132 ),
133 (
134 WorkMetric::ResultBytes,
135 estimate.result_bytes,
136 self.max_result_bytes,
137 ),
138 (
139 WorkMetric::CertificateStencilWork,
140 estimate.certificate_stencil_work,
141 self.max_certificate_stencil_work,
142 ),
143 ] {
144 if requested > limit {
145 return Err(InterferenceError::WorkBudgetExceeded {
146 metric,
147 estimate: requested,
148 limit,
149 });
150 }
151 }
152 Ok(())
153 }
154}
155
156impl Default for WorkBudget {
157 fn default() -> Self {
158 const MAX_CELLS: u64 = 4_096 * 4_096;
159 Self {
160 max_cells: MAX_CELLS,
161 max_emitter_evaluations: 2_000_000_000,
162 max_host_bytes: 1 << 30,
163 max_result_bytes: 1 << 29,
164 max_certificate_stencil_work: MAX_CELLS * CERTIFICATE_STENCIL_POINTS_PER_CELL,
165 }
166 }
167}
168
169#[derive(Clone, Copy, Debug, PartialEq)]
171pub struct RequestPreflight {
172 pub sampling_policy: SamplingPolicy,
174 pub sampling_certificate: SamplingCertificate,
176 pub work_estimate: WorkEstimate,
178}
179
180impl RequestPreflight {
181 pub fn admit(
183 problem: &InterferenceProblem,
184 plane: &SamplingPlane,
185 sampling_policy: SamplingPolicy,
186 sampling_thresholds: SamplingThresholds,
187 work_budget: WorkBudget,
188 ) -> Result<Self, InterferenceError> {
189 let work_estimate = WorkEstimate::for_request(problem, plane)?;
190 work_budget.admit(&work_estimate)?;
191 let sampling_certificate =
192 SamplingCertificate::measure_with_thresholds(problem, plane, sampling_thresholds)?;
193 sampling_policy.admit(&sampling_certificate)?;
194 Ok(Self {
195 sampling_policy,
196 sampling_certificate,
197 work_estimate,
198 })
199 }
200}
201
202fn checked_product(left: u64, right: u64, metric: WorkMetric) -> Result<u64, InterferenceError> {
203 left.checked_mul(right)
204 .ok_or(InterferenceError::WorkEstimateOverflow { metric })
205}