perf: optimize DNS query hot path (#15)
* perf: optimize hot path — RwLock, inline filtering, pre-allocated strings - Mutex → RwLock for cache, blocklist, and overrides (concurrent read access) - Make cache.lookup() and overrides.lookup() take &self (read-only) - Eliminate 3 Vec allocations per DnsPacket::write() via inline filtering - Pre-allocate domain strings with capacity 64 in parse path - Add criterion micro-benchmarks (hot_path + throughput) - Add bench README documenting both benchmark suites Measured improvement: ~14% faster parsing, ~9% pipeline throughput, round-trip cached 733ns → 698ns (~2.3M queries/sec). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: simplify benchmark code after review - Remove redundant DnsHeader::new() (already set by DnsPacket::new()) - Remove unused DnsHeader import - Change simulate_cached_pipeline to take &DnsCache (lookup is &self now) - Remove unnecessary mut on cache in cache_lookup_miss bench Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #15.
This commit is contained in:
18
src/cache.rs
18
src/cache.rs
@@ -19,7 +19,6 @@ pub struct DnsCache {
|
||||
max_entries: usize,
|
||||
min_ttl: u32,
|
||||
max_ttl: u32,
|
||||
query_count: u64,
|
||||
}
|
||||
|
||||
impl DnsCache {
|
||||
@@ -30,29 +29,16 @@ impl DnsCache {
|
||||
max_entries,
|
||||
min_ttl,
|
||||
max_ttl,
|
||||
query_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&mut self, domain: &str, qtype: QueryType) -> Option<DnsPacket> {
|
||||
self.query_count += 1;
|
||||
|
||||
if self.query_count.is_multiple_of(1000) {
|
||||
self.evict_expired();
|
||||
}
|
||||
|
||||
/// Read-only lookup — expired entries are left in place (cleaned up on insert).
|
||||
pub fn lookup(&self, domain: &str, qtype: QueryType) -> Option<DnsPacket> {
|
||||
let type_map = self.entries.get(domain)?;
|
||||
let entry = type_map.get(&qtype)?;
|
||||
|
||||
let elapsed = entry.inserted_at.elapsed();
|
||||
if elapsed >= entry.ttl {
|
||||
// Expired: remove this entry
|
||||
let type_map = self.entries.get_mut(domain).unwrap();
|
||||
type_map.remove(&qtype);
|
||||
self.entry_count -= 1;
|
||||
if type_map.is_empty() {
|
||||
self.entries.remove(domain);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user