molframe_core/parallel/policy.rs
1//! Whether a reduction may reassociate.
2//!
3//! Determinism is not free of consequence but it is nearly free of cost: it
4//! holds one partial per block live until the merge, and it fixes the order
5//! those partials combine in. What it buys is that a result does not change
6//! when a caller changes the thread count, which is what makes a recorded
7//! result reproducible by someone who has only the record.
8//!
9//! A caller who would rather have work stealing says so, and the result says
10//! so too. A speedup bought by silently changing the answer is not a speedup.
11
12use std::fmt;
13
14/// Whether a parallel reduction must combine partials in block order.
15#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
16pub enum ReductionPolicy {
17 /// Partials combine in ascending block index, whatever order they finish in.
18 ///
19 /// Output is bit-identical at any worker count.
20 #[default]
21 Deterministic,
22 /// Partials combine as they complete.
23 ///
24 /// Permits work stealing, and reassociates floating-point sums. A result
25 /// produced under this policy MUST record it in its provenance.
26 Fast,
27}
28
29impl ReductionPolicy {
30 /// Returns true when combination order is fixed by block index.
31 #[must_use]
32 pub const fn is_deterministic(self) -> bool {
33 matches!(self, Self::Deterministic)
34 }
35
36 /// The name recorded in provenance.
37 #[must_use]
38 pub const fn name(self) -> &'static str {
39 match self {
40 Self::Deterministic => "deterministic",
41 Self::Fast => "fast",
42 }
43 }
44}
45
46impl fmt::Display for ReductionPolicy {
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 formatter.write_str(self.name())
49 }
50}
51
52#[cfg(test)]
53#[path = "policy_tests.rs"]
54mod tests;