Skip to content

Commit 7ccf775

Browse files
zzylolclaude
andcommitted
Add window_to_ids exact-lookup index to MutableEpoch: O(M) → O(m)
MutableEpoch now maintains a secondary index: window_to_ids: HashMap<TimestampRange, Vec<(MetricID, Arc<Agg>)>> On insert: Arc::clone into window_to_ids (refcount bump only, no data copy) + Vec::push — O(1) amortized, no change to insert complexity. exact_query: HashMap lookup O(1) + iterate m entries O(m), no raw scan. Previously O(M) linear scan of all raw entries. range_query_into: unchanged linear scan of raw (O(M), bounded). seal(): unchanged — window_to_ids dropped, raw is sorted in-place. remove_windows: also removes from window_to_ids. Memory: one extra Arc pointer (8 bytes) per inserted entry — cheap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c2392a2 commit 7ccf775

1 file changed

Lines changed: 23 additions & 13 deletions

File tree

  • asap-query-engine/src/stores/simple_map_store

‎asap-query-engine/src/stores/simple_map_store/common.rs‎

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ pub struct MutableEpoch {
6161
pub raw: Vec<(TimestampRange, MetricID, Arc<dyn AggregateCore>)>,
6262
/// Distinct windows for rotation threshold — O(1) insert, O(1) len.
6363
windows: HashSet<TimestampRange>,
64+
/// Exact-lookup index: window → (MetricID, Arc<Agg>) pairs.
65+
/// Maintained O(1) on insert (Arc clone is a refcount bump, not a data copy).
66+
/// Allows exact_query to return in O(m) without scanning raw.
67+
#[allow(clippy::type_complexity)]
68+
window_to_ids: HashMap<TimestampRange, Vec<(MetricID, Arc<dyn AggregateCore>)>>,
6469
/// Epoch time bounds for O(1) skip check, updated incrementally on insert.
6570
min_start: Option<u64>,
6671
max_end: Option<u64>,
@@ -71,6 +76,7 @@ impl MutableEpoch {
7176
Self {
7277
raw: Vec::new(),
7378
windows: HashSet::new(),
79+
window_to_ids: HashMap::new(),
7480
min_start: None,
7581
max_end: None,
7682
}
@@ -89,13 +95,17 @@ impl MutableEpoch {
8995
}
9096
}
9197

92-
/// O(1) amortized: Vec push + HashSet insert + two scalar comparisons.
98+
/// O(1) amortized: Vec push + HashSet insert + HashMap entry + two scalar comparisons.
9399
pub fn insert(
94100
&mut self,
95101
metric_id: MetricID,
96102
range: TimestampRange,
97103
agg: Arc<dyn AggregateCore>,
98104
) {
105+
self.window_to_ids
106+
.entry(range)
107+
.or_default()
108+
.push((metric_id, Arc::clone(&agg)));
99109
self.raw.push((range, metric_id, agg));
100110
self.windows.insert(range);
101111
self.min_start = Some(self.min_start.map_or(range.0, |m| m.min(range.0)));
@@ -136,29 +146,29 @@ impl MutableEpoch {
136146
}
137147
}
138148

139-
/// Linear scan for exact window match — O(M), bounded.
149+
/// O(m) exact match via window_to_ids index — no raw scan needed.
150+
/// m = number of (MetricID, agg) pairs stored for this window.
140151
pub fn exact_query(
141152
&self,
142153
range: TimestampRange,
143154
) -> Option<Vec<(MetricID, Arc<dyn AggregateCore>)>> {
144-
let mut out = Vec::new();
145-
for (tr, metric_id, agg) in &self.raw {
146-
if *tr == range {
147-
out.push((*metric_id, Arc::clone(agg)));
148-
}
149-
}
150-
if out.is_empty() {
151-
None
152-
} else {
153-
Some(out)
154-
}
155+
let entries = self.window_to_ids.get(&range)?;
156+
Some(
157+
entries
158+
.iter()
159+
.map(|(metric_id, agg)| (*metric_id, Arc::clone(agg)))
160+
.collect(),
161+
)
155162
}
156163

157164
/// Remove specific windows (ReadBased cleanup).
158165
pub fn remove_windows(&mut self, windows: &[TimestampRange]) {
159166
let window_set: HashSet<TimestampRange> = windows.iter().copied().collect();
160167
self.raw.retain(|(tr, _, _)| !window_set.contains(tr));
161168
self.windows.retain(|tr| !window_set.contains(tr));
169+
for window in windows {
170+
self.window_to_ids.remove(window);
171+
}
162172
// Recompute bounds (cleanup is rare, linear scan is fine).
163173
self.min_start = self.raw.iter().map(|(tr, _, _)| tr.0).min();
164174
self.max_end = self.raw.iter().map(|(tr, _, _)| tr.1).max();

0 commit comments

Comments
 (0)