Overview
framealloc is a deterministic, frame-based memory allocation library for Rust game engines and real-time applications. It provides predictable performance through explicit lifetimes and scales seamlessly from single-threaded to multi-threaded workloads.
Not a general-purpose allocator replacement. Purpose-built for game engines, renderers, simulations, and real-time systems.
Key Capabilities
| Capability | Description |
|---|---|
| Frame Arenas | Lock-free bump allocation, reset per frame |
| Object Pools | O(1) reuse for small, frequent allocations |
| Thread Coordination | Explicit transfers, barriers, per-thread budgets |
| Static Analysis | cargo fa catches memory mistakes at build time |
| Runtime Diagnostics | Behavior filter detects pattern violations |
Features
Core Allocation
use ;
let alloc = new;
loop
Frame Retention & Promotion
// Keep frame data beyond frame boundary
let navmesh = alloc.;
// Get promoted allocations at frame end
let promotions = alloc.end_frame_with_promotions;
Thread Coordination (v0.6.0)
// Explicit cross-thread transfers
let handle = alloc.frame_box_for_transfer;
worker_channel.send;
// Receiver: let data = handle.receive();
// Frame barriers for deterministic sync
let barrier = new;
barrier.signal_frame_complete;
barrier.wait_all;
// Per-thread budgets
alloc.set_thread_frame_budget;
IDE Integration (v0.7.0)
// Enable snapshot emission for fa-insight
let snapshot_config = default
.with_directory
.with_max_snapshots;
let emitter = new;
// In your frame loop
alloc.end_frame;
let snapshot = build_snapshot;
emitter.maybe_emit; // Checks for request file
fa-insight — VS Code extension for framealloc-aware development:
- Inline diagnostics from
cargo fa - Memory inspector sidebar panel
- Real-time snapshot visualization
- Tag hierarchy and budget tracking
- CodeLens — Memory usage shown above functions (v0.2.0)
- Trend Graphs — Sparklines showing memory over time (v0.2.0)
- Budget Alerts — Toast warnings at 80%+ usage (v0.2.0)
Install: Search "FA Insight" in VS Code Marketplace or code --install-extension fa-insight-0.2.0.vsix.
Tokio Integration (v0.8.0)
Safe async/await patterns with the hybrid model:
use ;
// Main thread: frame allocations OK
alloc.begin_frame;
let scratch = alloc.;
// Async tasks: use TaskAlloc (pool-backed, auto-cleanup)
let alloc_clone = alloc.clone;
spawn;
alloc.end_frame; // Frame reset, async tasks unaffected
Key principle: Frame allocations stay on main thread, async tasks use pool/heap.
Enable with:
= { = "0.9", = ["tokio"] }
See docs/Tokio-Frame.md for the full async safety guide.
Performance Optimizations (v0.9.0)
Batch Allocations
Benchmark results (1000 allocations of 64-byte items):
- Individual
malloc: 12,450 ns - Individual
frame_alloc: 8,920 ns (1.4x faster than malloc) frame_alloc_batch: 64 ns (139x faster than individual frame_alloc, 194x faster than malloc)
Batch allocation eliminates per-call overhead and boundary checking.
// Instead of:
for _ in 0..1000
// Use batch allocation:
let items = alloc.;
// SAFETY: Index is within bounds (0..1000)
unsafe
Safety requirements:
- Indices must be within
0..count - Must initialize before reading (use
std::ptr::write) - Pointers invalid after
end_frame() - Not
SendorSync- don't pass to other threads
When to Use Batch Allocations
Use batch APIs when:
- Allocating >100 items in a tight loop
- Performance profiling shows allocation overhead
- Item count is known upfront
- Safety requirements are acceptable
Stick with individual APIs when:
- Allocating <10 items (overhead not significant)
- Count unknown or variable
- Need automatic Drop handling
- Prototyping (optimize later)
Minimal Mode
Disable all statistics for maximum performance (66% improvement in batch scenarios):
# Development (keep diagnostics)
= "0.9"
# Production (maximum performance)
= { = "0.9", = ["minimal"] }
Minimal mode disables:
- Allocation counting and statistics
- Tag tracking overhead
- Behavior filter instrumentation
- Debug assertions
Cache Prefetch (x86_64 Only)
Enable hardware prefetch hints for better performance in alloc-then-write patterns:
= { = "0.9", = ["prefetch"] }
What it does:
Emits PREFETCHW instructions to bring cache lines into L1 in exclusive state before writing, reducing cache miss latency.
Benchmark impact:
- Write-heavy: 10-15% faster allocation+initialization
- Read-heavy: negligible impact
- Memory-bound: up to 25% improvement
Specialized Batch Sizes
For known small counts, use specialized methods with zero overhead:
// Allocate exactly 2 items (common for pairs, vec2)
let = alloc.;
a.x = 1.0;
b.x = 2.0;
// Allocate exactly 4 items (common for quads, matrix rows)
let = alloc.;
// Allocate exactly 8 items (cache line optimization)
let items = alloc.;
Performance characteristics:
- Compiled to single bump pointer increment
- No bounds checking (count is compile-time constant)
- No loop overhead
- Often inlined completely
Runtime Behavior Filter
// Opt-in runtime detection of allocation pattern issues
alloc.enable_behavior_filter;
// After running...
let report = alloc.behavior_report;
for issue in &report.issues
Rapier Physics Integration (v0.10.0)
Frame-aware wrappers for Rapier physics engine v0.31, enabling high-performance bulk allocations:
use ;
use ;
use ;
let alloc = new;
let mut physics = new;
alloc.begin_frame;
// Create bodies using frame allocation for temporary data
let body = physics.insert_body;
// Step physics with frame-allocated contact buffers
let events = physics.step_with_events;
// Process collision events (valid until end_frame)
for contact in events.contacts
// Ray casting with frame-allocated results
use Ray;
use Vector2;
use QueryFilter;
let ray = new;
let hits = physics.cast_ray;
for hit in hits
alloc.end_frame; // All physics data automatically freed
Features:
- Frame-allocated contact and proximity events
- Bulk allocation for query results using
frame_alloc_batch - Updated for Rapier v0.31 API (BroadPhaseBvh, QueryFilter changes)
- Automatic cleanup at frame boundaries
- Support for both 2D and 3D physics
Performance:
- Contact buffers: 139x faster than individual allocations
- Query results: Single bulk allocation per query
- Zero manual memory management
API Changes in v0.31:
BroadPhaserenamed toBroadPhaseBvhQueryFiltermoved fromgeometrytopipelinemodulePhysicsPipeline::stepsignature updated (removedNoneparameter)- Ray casting now uses
as_query_pipelinemethod
Enable with:
= { = "0.10", = ["rapier"] }
Static Analysis
cargo-fa is a companion tool that detects memory intent violations before runtime.
Installation
Usage
# Check specific categories
# Run all checks
# CI integration
Filtering
Subcommands
Diagnostic Categories
| Range | Category | Examples |
|---|---|---|
| FA2xx | Threading | Cross-thread access, barrier mismatch, budget missing |
| FA3xx | Budgets | Unbounded allocation loops |
| FA6xx | Lifetime | Frame escape, hot loops, missing boundaries |
| FA7xx | Async | Allocation across await, closure capture |
| FA8xx | Architecture | Tag mismatch, unknown tags |
Quick Start
Basic Usage
use ;
Bevy Integration
use *;
use SmartAllocPlugin;
Cargo Features
| Feature | Description |
|---|---|
bevy |
Bevy ECS plugin integration |
parking_lot |
Faster mutex implementation |
debug |
Memory poisoning, allocation backtraces |
tracy |
Tracy profiler integration |
nightly |
std::alloc::Allocator trait support |
diagnostics |
Enhanced runtime diagnostics |
memory_filter |
Behavior filter for pattern detection |
Performance
Allocation priority minimizes latency:
- Frame arena — Bump pointer increment, no synchronization
- Thread-local pools — Free list pop, no contention
- Global pool refill — Mutex-protected, batched
- System heap — Fallback for oversized allocations
In typical game workloads, 90%+ of allocations hit the frame arena path.
Documentation
| Resource | Description |
|---|---|
| API Docs | Generated API documentation |
| TECHNICAL.md | Architecture and implementation details |
| CHANGELOG.md | Version history and migration guides |
License
Licensed under either of:
at your option.