risingwave_common/memory/
monitored_heap.rs1use std::collections::BinaryHeap;
16use std::mem::size_of;
17
18use risingwave_common_estimate_size::EstimateSize;
19
20use crate::memory::{MemoryContext, MonitoredGlobalAlloc};
21
22pub struct MemMonitoredHeap<T> {
23 inner: BinaryHeap<T>,
24 mem_ctx: MemoryContext,
25}
26
27impl<T: Ord + EstimateSize> MemMonitoredHeap<T> {
28 pub fn new_with(mem_ctx: MemoryContext) -> Self {
29 Self {
30 inner: BinaryHeap::new(),
31 mem_ctx,
32 }
33 }
34
35 pub fn with_capacity(capacity: usize, mem_ctx: MemoryContext) -> Self {
36 let inner = BinaryHeap::with_capacity(capacity);
37 mem_ctx.add_unchecked((capacity * size_of::<T>()) as i64);
39 Self { inner, mem_ctx }
40 }
41
42 pub fn push(&mut self, item: T) {
43 let prev_cap = self.inner.capacity();
44 let item_heap = item.estimated_heap_size();
45 self.inner.push(item);
46 let new_cap = self.inner.capacity();
47 self.mem_ctx
48 .add_unchecked(((new_cap - prev_cap) * size_of::<T>() + item_heap) as i64);
49 }
50
51 pub fn pop(&mut self) -> Option<T> {
52 let prev_cap = self.inner.capacity();
53 let item = self.inner.pop();
54 let item_heap = item.as_ref().map(|i| i.estimated_heap_size()).unwrap_or(0);
55 let new_cap = self.inner.capacity();
56 self.mem_ctx
57 .add_unchecked(-(((prev_cap - new_cap) * size_of::<T>() + item_heap) as i64));
58
59 item
60 }
61
62 pub fn is_empty(&self) -> bool {
63 self.inner.is_empty()
64 }
65
66 pub fn len(&self) -> usize {
67 self.inner.len()
68 }
69
70 pub fn peek(&self) -> Option<&T> {
71 self.inner.peek()
72 }
73
74 pub fn into_sorted_vec(self) -> Vec<T, MonitoredGlobalAlloc> {
101 let old_cap = self.inner.capacity();
102 let alloc = MonitoredGlobalAlloc::with_memory_context(self.mem_ctx.clone());
103 let vec = self.inner.into_iter_sorted();
104
105 let mut ret = Vec::with_capacity_in(vec.len(), alloc);
106 ret.extend(vec);
107
108 self.mem_ctx
109 .add_unchecked(-((old_cap * size_of::<T>()) as i64));
110 ret
111 }
112
113 pub fn mem_context(&self) -> &MemoryContext {
114 &self.mem_ctx
115 }
116}
117
118impl<T> Extend<T> for MemMonitoredHeap<T>
119where
120 T: Ord + EstimateSize,
121{
122 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
123 let old_cap = self.inner.capacity();
124 let mut items_heap_size = 0usize;
125 let items = iter.into_iter();
126 self.inner.reserve_exact(items.size_hint().0);
127 for item in items {
128 items_heap_size += item.estimated_heap_size();
129 self.inner.push(item);
130 }
131
132 let new_cap = self.inner.capacity();
133
134 let diff = (new_cap - old_cap) * size_of::<T>() + items_heap_size;
135 self.mem_ctx.add_unchecked(diff as i64);
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::MemMonitoredHeap;
142 use crate::memory::MemoryContext;
143 use crate::metrics::LabelGuardedIntGauge;
144
145 #[test]
146 fn test_heap() {
147 let gauge = LabelGuardedIntGauge::test_int_gauge::<4>();
148 let mem_ctx = MemoryContext::root(gauge.clone(), u64::MAX);
149
150 let mut heap = MemMonitoredHeap::<u8>::new_with(mem_ctx);
151 assert_eq!(0, gauge.get());
152
153 heap.push(9u8);
154 heap.push(1u8);
155 assert_eq!(heap.inner.capacity() as i64, gauge.get());
156
157 heap.pop().unwrap();
158 assert_eq!(heap.inner.capacity() as i64, gauge.get());
159
160 assert!(!heap.is_empty());
161 }
162
163 #[test]
164 fn test_heap_drop() {
165 let gauge = LabelGuardedIntGauge::test_int_gauge::<4>();
166 let mem_ctx = MemoryContext::root(gauge.clone(), u64::MAX);
167
168 let vec = {
169 let mut heap = MemMonitoredHeap::<u8>::new_with(mem_ctx);
170 assert_eq!(0, gauge.get());
171
172 heap.push(9u8);
173 heap.push(1u8);
174 assert_eq!(heap.inner.capacity() as i64, gauge.get());
175
176 heap.into_sorted_vec()
177 };
178
179 assert_eq!(2, gauge.get());
180
181 drop(vec);
182
183 assert_eq!(0, gauge.get());
184 }
185}