Skip to content

Commit 91f9d71

Browse files
perf(profiling): use fast pointer hasher for heap-live tracker
Replace default SipHash with a simple bit-mixing hasher optimized for pointer addresses. Since pointers are already well-distributed, we use `ptr ^ (ptr >> 4)` instead of expensive cryptographic hashing. This reduces overhead in untrack_allocation() which is called on every free. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9867f1e commit 91f9d71

1 file changed

Lines changed: 48 additions & 4 deletions

File tree

profiling/src/profiling/mod.rs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use libdd_profiling::internal::Profile as InternalProfile;
3737
use log::{debug, info, trace, warn};
3838
use std::borrow::Cow;
3939
use std::collections::HashMap;
40-
use std::hash::Hash;
40+
use std::hash::{BuildHasher, Hash, Hasher};
4141
use std::num::NonZeroI64;
4242
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicU64, Ordering};
4343
use std::sync::{Arc, Barrier, OnceLock};
@@ -60,6 +60,49 @@ pub const NO_TIMESTAMP: i64 = 0;
6060
// magnitude for the capacity.
6161
const UPLOAD_CHANNEL_CAPACITY: usize = 8;
6262

63+
/// A fast, non-cryptographic hasher optimized for pointer addresses.
64+
/// Since pointers are already well-distributed and typically aligned,
65+
/// we use a simple bit mixing approach instead of expensive hashing.
66+
#[derive(Default)]
67+
struct PointerHasher(u64);
68+
69+
impl Hasher for PointerHasher {
70+
#[inline]
71+
fn write(&mut self, _bytes: &[u8]) {
72+
unreachable!("PointerHasher only supports write_usize");
73+
}
74+
75+
#[inline]
76+
fn write_usize(&mut self, ptr: usize) {
77+
// Pointers are typically 8 or 16-byte aligned, so shift right to spread
78+
// the entropy across the lower bits. XOR with shifted value for mixing.
79+
let ptr = ptr as u64;
80+
self.0 = ptr ^ (ptr >> 4);
81+
}
82+
83+
#[inline]
84+
fn finish(&self) -> u64 {
85+
self.0
86+
}
87+
}
88+
89+
/// BuildHasher that creates PointerHasher instances.
90+
/// DashMap requires Clone for its internal sharding.
91+
#[derive(Clone)]
92+
struct PointerHasherBuilder;
93+
94+
impl BuildHasher for PointerHasherBuilder {
95+
type Hasher = PointerHasher;
96+
97+
#[inline]
98+
fn build_hasher(&self) -> Self::Hasher {
99+
PointerHasher(0)
100+
}
101+
}
102+
103+
/// Type alias for the heap tracker with our fast pointer hasher
104+
type HeapTracker = DashMap<usize, LiveHeapSample, PointerHasherBuilder>;
105+
63106
/// The global profiler. Profiler gets made during the first rinit after an
64107
/// minit, and is destroyed on mshutdown.
65108
static mut PROFILER: OnceLock<Profiler> = OnceLock::new();
@@ -284,7 +327,8 @@ pub struct Profiler {
284327
/// Tracks sampled allocations for live heap profiling.
285328
/// Maps allocation pointer -> sample data for batched emission at export time.
286329
/// Wrapped in Arc to share with TimeCollector for batched sample emission.
287-
live_heap_tracker: Arc<DashMap<usize, LiveHeapSample>>,
330+
/// Uses a fast pointer hasher since addresses are already well-distributed.
331+
live_heap_tracker: Arc<HeapTracker>,
288332
}
289333

290334
struct TimeCollector {
@@ -294,7 +338,7 @@ struct TimeCollector {
294338
upload_sender: Sender<UploadMessage>,
295339
upload_period: Duration,
296340
/// Shared tracker for batched heap-live sample emission at export time.
297-
live_heap_tracker: Arc<DashMap<usize, LiveHeapSample>>,
341+
live_heap_tracker: Arc<HeapTracker>,
298342
}
299343

300344
impl TimeCollector {
@@ -788,7 +832,7 @@ impl Profiler {
788832
let interrupt_manager = Arc::new(InterruptManager::new());
789833
let (message_sender, message_receiver) = crossbeam_channel::bounded(100);
790834
let (upload_sender, upload_receiver) = crossbeam_channel::bounded(UPLOAD_CHANNEL_CAPACITY);
791-
let live_heap_tracker = Arc::new(DashMap::new());
835+
let live_heap_tracker = Arc::new(DashMap::with_hasher(PointerHasherBuilder));
792836
let time_collector = TimeCollector {
793837
fork_barrier: fork_barrier.clone(),
794838
interrupt_manager: interrupt_manager.clone(),

0 commit comments

Comments
 (0)