Skip to content

Commit 9867f1e

Browse files
refactor(profiling): simplify heap-live and sample filter code
- Use functional style (map + match) in collect_batched_heap_live_samples - Only create ProfileIndex when heap-live tracking is enabled - Replace 32 repetitive I/O profiling lines with a loop - Use filter_map in sample type filter method - Add early bail-out in free_allocation when heap-live is disabled Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1712f68 commit 9867f1e

2 files changed

Lines changed: 32 additions & 71 deletions

File tree

profiling/src/profiling/mod.rs

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,11 @@ pub enum ProfilerMessage {
231231
Wake,
232232
}
233233

234-
/// A complete sample stored for live heap tracking. Contains everything needed
235-
/// to send an identical deallocation sample (with negated heap-live-* values).
236-
#[derive(Clone, Debug)]
237234
/// Tracked allocation for batched heap-live sample emission.
238235
/// Unlike the .NET profiler which tracks CLR objects via weak handles,
239236
/// we track raw allocation pointers. Samples are emitted in batches
240237
/// at profile export time, not on each allocation/free.
238+
#[derive(Clone, Debug)]
241239
pub struct LiveHeapSample {
242240
/// The profile index (sample_types + tags) for adding to correct profile
243241
pub key: ProfileIndex,
@@ -318,16 +316,17 @@ impl TimeCollector {
318316
let tracked = entry.value();
319317

320318
// Build sample_values with only heap-live-samples and heap-live-size set
321-
let mut sample_values = vec![0i64; tracked.key.sample_types.len()];
322-
for (i, st) in tracked.key.sample_types.iter().enumerate() {
323-
if st.r#type == "heap-live-samples" {
324-
sample_values[i] = 1;
325-
} else if st.r#type == "heap-live-size" {
326-
sample_values[i] = tracked.allocation_size;
327-
}
328-
}
319+
let sample_values: Vec<i64> = tracked
320+
.key
321+
.sample_types
322+
.iter()
323+
.map(|st| match st.r#type {
324+
"heap-live-samples" => 1,
325+
"heap-live-size" => tracked.allocation_size,
326+
_ => 0,
327+
})
328+
.collect();
329329

330-
// Create the sample message
331330
let message = SampleMessage {
332331
key: tracked.key.clone(),
333332
value: SampleData {
@@ -1160,14 +1159,12 @@ impl Profiler {
11601159

11611160
// Track allocation for heap live profiling if enabled.
11621161
// Samples will be emitted in batch at profile export time.
1163-
// Fetch sample_types and tags once, reuse for both tracking and sending.
1164-
let sample_types = self.sample_types_filter.sample_types();
1165-
let tags = TAGS.with_borrow(Arc::clone);
1166-
let key = ProfileIndex { sample_types, tags };
1167-
11681162
if self.is_heap_live_enabled() {
11691163
let tracked = LiveHeapSample {
1170-
key: key.clone(),
1164+
key: ProfileIndex {
1165+
sample_types: self.sample_types_filter.sample_types(),
1166+
tags: TAGS.with_borrow(Arc::clone),
1167+
},
11711168
frames: frames.clone(),
11721169
labels: labels.clone(),
11731170
allocation_size: alloc_size,
@@ -1181,16 +1178,7 @@ impl Profiler {
11811178
}
11821179
}
11831180

1184-
let sample_values = self.sample_types_filter.filter(sample_values);
1185-
let message = SampleMessage {
1186-
key,
1187-
value: SampleData {
1188-
frames,
1189-
labels,
1190-
sample_values,
1191-
timestamp,
1192-
},
1193-
};
1181+
let message = self.prepare_sample_message(frames, sample_values, labels, timestamp);
11941182

11951183
match self.message_sender.try_send(ProfilerMessage::Sample(message)) {
11961184
Ok(_) => trace!(
@@ -1210,14 +1198,19 @@ impl Profiler {
12101198
/// Called when memory is freed. Removes the allocation from tracking. The next profile export
12111199
/// will not include this allocation in the heap-live samples.
12121200
pub fn free_allocation(&self, ptr: *mut std::ffi::c_void) {
1201+
// Bail out early if heap-live tracking is disabled to avoid DashMap lookup overhead
1202+
if !self.is_heap_live_enabled() {
1203+
return;
1204+
}
1205+
12131206
if let Some(sample) = self.untrack_allocation(ptr as usize) {
12141207
trace!(
12151208
"Untracked freed allocation at {:#x} ({} bytes)",
12161209
ptr as usize,
12171210
sample.allocation_size
12181211
);
12191212
}
1220-
// If not tracked, nothing to do (wasn't sampled or tracking disabled)
1213+
// If not tracked, nothing to do (wasn't sampled)
12211214
}
12221215

12231216
/// Collect a stack sample with exception.

profiling/src/profiling/sample_type_filter.rs

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -78,38 +78,11 @@ impl SampleTypeFilter {
7878

7979
#[cfg(feature = "io_profiling")]
8080
if system_settings.profiling_io_enabled {
81-
sample_types.push(SAMPLE_TYPES[9]);
82-
sample_types_mask[9] = true;
83-
sample_types.push(SAMPLE_TYPES[10]);
84-
sample_types_mask[10] = true;
85-
sample_types.push(SAMPLE_TYPES[11]);
86-
sample_types_mask[11] = true;
87-
sample_types.push(SAMPLE_TYPES[12]);
88-
sample_types_mask[12] = true;
89-
sample_types.push(SAMPLE_TYPES[13]);
90-
sample_types_mask[13] = true;
91-
sample_types.push(SAMPLE_TYPES[14]);
92-
sample_types_mask[14] = true;
93-
sample_types.push(SAMPLE_TYPES[15]);
94-
sample_types_mask[15] = true;
95-
sample_types.push(SAMPLE_TYPES[16]);
96-
sample_types_mask[16] = true;
97-
sample_types.push(SAMPLE_TYPES[17]);
98-
sample_types_mask[17] = true;
99-
sample_types.push(SAMPLE_TYPES[18]);
100-
sample_types_mask[18] = true;
101-
sample_types.push(SAMPLE_TYPES[19]);
102-
sample_types_mask[19] = true;
103-
sample_types.push(SAMPLE_TYPES[20]);
104-
sample_types_mask[20] = true;
105-
sample_types.push(SAMPLE_TYPES[21]);
106-
sample_types_mask[21] = true;
107-
sample_types.push(SAMPLE_TYPES[22]);
108-
sample_types_mask[22] = true;
109-
sample_types.push(SAMPLE_TYPES[23]);
110-
sample_types_mask[23] = true;
111-
sample_types.push(SAMPLE_TYPES[24]);
112-
sample_types_mask[24] = true;
81+
// I/O sample types are at indices 9..=24
82+
for i in 9..=24 {
83+
sample_types.push(SAMPLE_TYPES[i]);
84+
sample_types_mask[i] = true;
85+
}
11386
}
11487
}
11588

@@ -124,9 +97,6 @@ impl SampleTypeFilter {
12497
}
12598

12699
pub fn filter(&self, sample_values: SampleValues) -> Vec<i64> {
127-
let mut output = Vec::new();
128-
output.reserve_exact(self.sample_types.len());
129-
130100
// Lay this out in the same order as SampleValues.
131101
// Allows us to slice the SampleValues as if they were an array.
132102
let values: [i64; MAX_SAMPLE_TYPES] = [
@@ -157,13 +127,11 @@ impl SampleTypeFilter {
157127
sample_values.file_write_size_samples,
158128
];
159129

160-
for (value, enabled) in values.into_iter().zip(self.sample_types_mask.iter()) {
161-
if *enabled {
162-
output.push(value);
163-
}
164-
}
165-
166-
output
130+
values
131+
.into_iter()
132+
.zip(self.sample_types_mask.iter())
133+
.filter_map(|(value, &enabled)| enabled.then_some(value))
134+
.collect()
167135
}
168136
}
169137

0 commit comments

Comments
 (0)