Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FAQ

General

Is PulseMap a HashMap replacement?

No. PulseMap is a bounded cache with automatic eviction. Use it when:

  • You need fixed memory usage
  • You can tolerate entries being evicted
  • You want in-process caching without Redis

Use HashMap when you need to keep every entry forever.

What happens when PulseMap is full?

The least-useful entry in the target bucket is evicted (LFU+LRU hybrid). This is automatic and costs zero additional cache misses. Check eviction_count() to monitor.

Can I turn off eviction?

Not directly, but you can minimize it:

  1. Use with_auto_resize(n) — the map doubles when 75% full
  2. Start with a large initial size
  3. Monitor eviction_count() — if it’s 0, you’re fine

What’s the maximum key/value size?

  • Inline mode: key ≤ 6 bytes, value ≤ 7 bytes (fastest, zero allocation)
  • Slab mode: unlimited size (heap allocated)

Both modes are transparent — PulseMap automatically chooses the optimal storage.


Performance

Why is PulseMap faster than HashMap?

Three reasons:

  1. Cache efficiency: 1 cache line per lookup (vs 2-3 for HashMap)
  2. No pointer chasing: Inline mode stores data directly in the bucket
  3. H2 fingerprint: 99.2% of non-matches rejected without key comparison

When is PulseMap slower?

  • Iteration — PulseMap doesn’t maintain insertion order
  • Very large values — Slab allocation adds overhead
  • 99%+ fill rate — Every insert causes an eviction

How does it compare to moka?

Single-thread: moka is significantly slower (161ms vs 6.1ms for 100K inserts). moka uses background maintenance threads and heavy synchronization.

Multi-thread (4T): ShardedPulseMap is 6.5–12x faster than moka across all concurrent workloads.

moka’s strength is its W-TinyLFU eviction policy (better hit rates on skewed workloads). PulseMap wins on raw throughput.


Memory

Does PulseMap leak memory?

No (since v0.6.0). Slab entries are returned to a free list on eviction/removal.

  • Rust: Drop chains through SlabPool + free list
  • C FFI: User must call pulse_map_free() (documented)

How much memory does PulseMap use?

Memory = num_buckets × 64 bytes + slab_overhead

For inline-only workloads (small KV pairs): exactly num_buckets × 64 bytes.

Can I use PulseMap in no_std?

Yes! Disable the std feature:

pulse_map = { version = "0.6", default-features = false }

Core data structures (MetaWord, Slot, Bucket) work without allocator.


Concurrency

Is PulseMap thread-safe?

  • ConcurrentPulseMap — fully thread-safe, single-lock architecture
  • ShardedPulseMap — fully thread-safe, 16-shard architecture (recommended for 3+ threads)
  • TypedPulseMap and PulseMapRaw — single-threaded only

Can I set different TTLs for different keys?

Yes! Since v0.6.1, use insert_ttl(key, value, ttl):

#![allow(unused)]
fn main() {
cache.set_ttl(500);                              // global default
cache.insert_ttl(b"session", b"data", 50);       // expires after 50
cache.insert_ttl(b"config", b"val", u32::MAX);   // never expires
}

Can I use PulseMap with async/await?

Yes! ConcurrentPulseMap and ShardedPulseMap methods are non-blocking (spinlock, not mutex):

#![allow(unused)]
fn main() {
async fn handler(cache: &ShardedPulseMap<String, String>) {
    // Safe to call from async context — won't block the executor
    cache.insert("key".to_string(), "val".to_string());
}
}

What happens during resize?

  • ConcurrentPulseMap: Stop-the-world (exclusive write lock). ~1ms per 10K entries.
  • ShardedPulseMap: resize_all() rehashes one shard at a time — other shards remain operational.

FFI

Is the C API thread-safe?

Yes! The C API wraps ConcurrentPulseMap internally. You can call pulse_map_insert() from multiple threads simultaneously.