Skip to main content

cbtop/grammar/
compute_block.rs

1//! ComputeBlock orchestrator (GGPlot equivalent in Grammar of Graphics).
2
3use std::collections::HashMap;
4use std::time::Duration;
5
6use super::composition::CompositionMode;
7use super::context::ExecutionContext;
8use super::error::{GrammarError, GrammarResult};
9use super::policy::ExecutionPolicy;
10use super::resources::ResourceMapping;
11use super::strategy::{ExecutionStrategy, StrategyLayer};
12use super::transform::DataTransform;
13use super::workload::WorkloadSpec;
14
15/// Execution result
16#[derive(Debug, Clone)]
17pub struct ExecutionResult {
18    /// Execution time
19    pub duration: Duration,
20    /// GFLOP/s achieved
21    pub gflops: f64,
22    /// Memory bandwidth achieved (GB/s)
23    pub bandwidth_gbps: f64,
24    /// Strategy that was used
25    pub strategy_used: String,
26    /// Additional metrics
27    pub metrics: HashMap<String, f64>,
28}
29
30/// Validated ComputeBlock ready for execution
31#[derive(Debug, Clone)]
32pub struct BuiltComputeBlock {
33    pub(crate) inner: ComputeBlock,
34}
35
36impl BuiltComputeBlock {
37    /// Execute the compute block
38    pub fn execute(&self) -> GrammarResult<ExecutionResult> {
39        let start = std::time::Instant::now();
40
41        // Select strategy (in order of priority)
42        let mut strategies = self.inner.strategies.clone();
43        strategies.sort_by(|a, b| b.priority.cmp(&a.priority));
44
45        let strategy_used = if let Some(layer) = strategies.first() {
46            format!("{:?}", layer.strategy)
47        } else {
48            "Sequential".to_string()
49        };
50
51        // Simulate execution (real implementation would dispatch to backends)
52        let duration = start.elapsed();
53        let flops = self
54            .inner
55            .workload
56            .as_ref()
57            .map(|w| w.flop_count())
58            .unwrap_or(0);
59        let gflops = if duration.as_secs_f64() > 0.0 {
60            flops as f64 / duration.as_secs_f64() / 1e9
61        } else {
62            0.0
63        };
64
65        Ok(ExecutionResult {
66            duration,
67            gflops,
68            bandwidth_gbps: 0.0,
69            strategy_used,
70            metrics: HashMap::new(),
71        })
72    }
73
74    /// Get the workload spec
75    pub fn workload(&self) -> Option<&WorkloadSpec> {
76        self.inner.workload.as_ref()
77    }
78}
79
80/// ComputeBlock - the main orchestrator (analogous to GGPlot)
81#[derive(Debug, Clone, Default)]
82pub struct ComputeBlock {
83    /// Workload specification
84    pub(crate) workload: Option<WorkloadSpec>,
85    /// Resource mapping
86    pub(crate) resources: ResourceMapping,
87    /// Strategy layers (multiple, with priority)
88    pub(crate) strategies: Vec<StrategyLayer>,
89    /// Data transform
90    pub(crate) transform: DataTransform,
91    /// Execution context
92    pub(crate) context: ExecutionContext,
93    /// Composition mode
94    pub(crate) composition: CompositionMode,
95    /// Execution policy
96    pub(crate) policy: ExecutionPolicy,
97    /// Facet parameters for parameter sweep
98    pub(crate) facet_params: Option<(String, Vec<f64>)>,
99}
100
101impl ComputeBlock {
102    /// Create a new ComputeBlock builder
103    pub fn builder() -> ComputeBlockBuilder {
104        ComputeBlockBuilder::new()
105    }
106
107    /// Validate the ComputeBlock configuration
108    fn validate(&self) -> GrammarResult<()> {
109        // F701: Builder rejects incomplete spec
110        if self.workload.is_none() {
111            return Err(GrammarError::MissingWorkload);
112        }
113
114        // F711: Scale domain validation
115        // (handled in ResourceScale)
116
117        Ok(())
118    }
119
120    /// Build and validate the ComputeBlock
121    pub fn build(self) -> GrammarResult<BuiltComputeBlock> {
122        self.validate()?;
123        Ok(BuiltComputeBlock { inner: self })
124    }
125}
126
127/// Builder for ComputeBlock (fluent API)
128#[derive(Debug, Clone, Default)]
129pub struct ComputeBlockBuilder {
130    inner: ComputeBlock,
131}
132
133impl ComputeBlockBuilder {
134    /// Create a new builder
135    pub fn new() -> Self {
136        Self {
137            inner: ComputeBlock {
138                transform: DataTransform::Identity,
139                context: ExecutionContext::Cpu {
140                    affinity: None,
141                    numa_node: None,
142                },
143                composition: CompositionMode::None,
144                policy: ExecutionPolicy::default(),
145                ..Default::default()
146            },
147        }
148    }
149
150    /// Set workload specification
151    pub fn workload(mut self, workload: WorkloadSpec) -> Self {
152        self.inner.workload = Some(workload);
153        self
154    }
155
156    /// Set resource mapping
157    pub fn resources(mut self, resources: ResourceMapping) -> Self {
158        self.inner.resources = resources;
159        self
160    }
161
162    /// Add a strategy layer
163    pub fn strategy(mut self, strategy: ExecutionStrategy) -> Self {
164        self.inner.strategies.push(StrategyLayer::new(strategy));
165        self
166    }
167
168    /// Add a strategy layer with priority
169    pub fn strategy_with_priority(mut self, strategy: ExecutionStrategy, priority: i32) -> Self {
170        self.inner
171            .strategies
172            .push(StrategyLayer::new(strategy).priority(priority));
173        self
174    }
175
176    /// Set data transform
177    pub fn transform(mut self, transform: DataTransform) -> Self {
178        self.inner.transform = transform;
179        self
180    }
181
182    /// Set execution context
183    pub fn context(mut self, context: ExecutionContext) -> Self {
184        self.inner.context = context;
185        self
186    }
187
188    /// Set composition mode
189    pub fn composition(mut self, composition: CompositionMode) -> Self {
190        self.inner.composition = composition;
191        self
192    }
193
194    /// Set execution policy
195    pub fn policy(mut self, policy: ExecutionPolicy) -> Self {
196        self.inner.policy = policy;
197        self
198    }
199
200    /// Set facet parameters for parameter sweep
201    pub fn facet_by(mut self, param: impl Into<String>, values: Vec<f64>) -> Self {
202        self.inner.facet_params = Some((param.into(), values));
203        self
204    }
205
206    /// Build and validate
207    pub fn build(self) -> GrammarResult<BuiltComputeBlock> {
208        self.inner.build()
209    }
210}