# memkit Roadmap
**Version:** Draft 1.0
**Date:** December 2025
**Status:** Planning Phase
> **Vision:** Intent-driven memory management for real-time systems. Plug it into whatever system you're using and opt-in to what you need.
---
## Table of Contents
1. [Executive Summary](#1-executive-summary)
2. [Crate Ecosystem](#2-crate-ecosystem)
3. [Core Crate: memkit](#3-core-crate-memkit)
4. [GPU Crate: memkit-gpu](#4-gpu-crate-memkit-gpu)
5. [Async Crate: memkit-async](#5-async-crate-memkit-async)
6. [Integration Crates](#6-integration-crates)
7. [Tooling: cargo-memlense](#7-tooling-cargo-memlense)
8. [API Coalescing Plan](#8-api-coalescing-plan)
9. [Naming Convention](#9-naming-convention)
10. [Type Migration Table](#10-type-migration-table)
11. [Trait Architecture](#11-trait-architecture)
12. [Module Structure](#12-module-structure)
13. [Feature Flags](#13-feature-flags)
14. [Migration Strategy](#14-migration-strategy)
15. [Timeline](#15-timeline)
16. [Open Questions](#16-open-questions)
---
## 1. Executive Summary
### What is memkit?
memkit is a complete rewrite of framealloc, restructured as a modular memory management ecosystem. The goal is to preserve all the powerful features of framealloc while eliminating the "spaghetti" internal architecture.
### Core Principles
1. **Modularity** - Each concern in its own crate
2. **Opt-in Complexity** - Simple by default, powerful when needed
3. **Zero-cost Abstractions** - Hot paths must be allocation-free and lock-free
4. **Trait-first Design** - Composition over inheritance
5. **Clear Boundaries** - No circular dependencies, clean module graph
### Crate Ecosystem Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ User Application │
└─────────────────────────────────────────────────────────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌───────────────┐
│ memkit-bevy │ │ memkit-rapier │ │ memkit-async │
│ (Bevy ECS) │ │ (Physics) │ │ (Tokio/async) │
└───────┬───────┘ └────────┬────────┘ └───────┬───────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ memkit-gpu │◄─────►│ memkit │
│ (GPU memory) │ │ (CPU core) │
└───────────────┘ └───────────────┘
│ │
└───────────┬───────────┘
│
▼
┌───────────────────────┐
│ cargo-memlense │
│ (Static analysis & │
│ diagnostics) │
└───────────────────────┘
```
---
## 2. Crate Ecosystem
### 2.1 Crate List
| `memkit` | Core CPU memory management | None | 0.12.0 |
| `memkit-gpu` | GPU memory management | memkit | 0.12.0 |
| `memkit-async` | Async/Tokio patterns | memkit | 0.12.0 |
| `memkit-bevy` | Bevy ECS integration | memkit, bevy | 0.12.0 |
| `memkit-rapier` | Rapier physics integration | memkit, rapier | 0.12.0 |
| `cargo-memlense` | Static analysis CLI tool | syn, proc-macro2 | 0.12.0 |
### 2.2 Dependency Graph
```
cargo-memlense (standalone, no runtime deps on memkit)
│
│ analyzes code using
▼
memkit-bevy ──────► memkit ◄────── memkit-rapier
│ │ │
│ │ │
▼ ▼ ▼
bevy_ecs memkit-gpu ◄────── memkit-async
│
▼
gpu-allocator (optional)
ash (optional)
```
### 2.3 Workspace Structure
```
memkit/
├── Cargo.toml # Workspace root
├── README.md
├── CHANGELOG.md
├── LICENSE-MIT
├── LICENSE-APACHE
│
├── crates/
│ ├── memkit/ # Core crate
│ │ ├── Cargo.toml
│ │ └── src/
│ │
│ ├── memkit-gpu/ # GPU crate
│ │ ├── Cargo.toml
│ │ └── src/
│ │
│ ├── memkit-async/ # Async crate
│ │ ├── Cargo.toml
│ │ └── src/
│ │
│ ├── memkit-bevy/ # Bevy integration
│ │ ├── Cargo.toml
│ │ └── src/
│ │
│ └── memkit-rapier/ # Rapier integration
│ ├── Cargo.toml
│ └── src/
│
├── tools/
│ └── cargo-memlense/ # Static analysis tool
│ ├── Cargo.toml
│ └── src/
│
├── examples/
│ ├── basic/
│ ├── game-loop/
│ ├── gpu-transfer/
│ └── bevy-integration/
│
├── benches/
│ ├── allocation.rs
│ ├── frame-cycle.rs
│ └── gpu-transfer.rs
│
└── docs/
├── architecture.md
├── migration-guide.md
└── api-reference.md
```
---
## 3. Core Crate: memkit
### 3.1 Purpose
The core crate provides CPU-side memory management:
- Frame allocators (bump allocation, per-frame reset)
- Pool allocators (fixed-size blocks)
- Heap allocators (variable-size, long-lived)
- Deferred free queues
- Handle-based allocation
- Thread-local fast paths
- Allocation tagging and intent
### 3.2 Module Structure
```
memkit/src/
├── lib.rs # Public API surface
│
├── allocator/ # Core allocator implementations
│ ├── mod.rs
│ ├── frame.rs # MkFrameAllocator - bump allocator
│ ├── pool.rs # MkPoolAllocator - slab allocator
│ ├── heap.rs # MkHeapAllocator - general purpose
│ ├── deferred.rs # MkDeferredQueue - delayed free
│ └── handle.rs # MkHandleAllocator - relocatable
│
├── container/ # Memory containers (coalesced from api/)
│ ├── mod.rs
│ ├── frame_box.rs # MkFrameBox<T>
│ ├── pool_box.rs # MkPoolBox<T>
│ ├── heap_box.rs # MkHeapBox<T>
│ ├── frame_vec.rs # MkFrameVec<T>
│ ├── frame_map.rs # MkFrameMap<K,V>
│ └── slice.rs # MkFrameSlice<T>
│
├── lifecycle/ # Frame lifecycle management (coalesced)
│ ├── mod.rs
│ ├── frame.rs # begin_frame/end_frame
│ ├── phase.rs # MkPhase - named scopes within frames
│ ├── checkpoint.rs # MkCheckpoint - save/restore points
│ └── scope.rs # MkScope - RAII guards
│
├── policy/ # Allocation policies (coalesced from retention/promotion)
│ ├── mod.rs
│ ├── retention.rs # When to keep vs free
│ ├── promotion.rs # Frame → Pool → Heap promotion
│ └── budget.rs # Memory budgets and limits
│
├── tag/ # Allocation tagging
│ ├── mod.rs
│ ├── intent.rs # MkIntent - why is this allocated?
│ ├── group.rs # MkGroup - bulk operations
│ └── registry.rs # Tag registration and lookup
│
├── transfer/ # Cross-context transfers
│ ├── mod.rs
│ ├── thread.rs # Cross-thread transfer
│ └── handle.rs # MkTransferHandle
│
├── sync/ # Synchronization primitives
│ ├── mod.rs
│ ├── barrier.rs # MkBarrier
│ └── atomic.rs # Lock-free utilities
│
├── stats/ # Runtime statistics (minimal, not diagnostics)
│ ├── mod.rs
│ └── counter.rs # Allocation counters
│
├── traits/ # Core traits
│ ├── mod.rs
│ ├── allocator.rs # MkAllocator trait
│ ├── resettable.rs # MkResettable trait
│ └── tagged.rs # MkTagged trait
│
├── config/ # Configuration
│ ├── mod.rs
│ └── builder.rs # MkConfigBuilder
│
└── error/ # Error types
├── mod.rs
└── alloc_error.rs # MkAllocError
```
### 3.3 Public API Surface
```rust
// Main entry point
pub use crate::MkAllocator;
pub use crate::MkConfig;
// Containers
pub use crate::container::{
MkFrameBox, MkPoolBox, MkHeapBox,
MkFrameVec, MkFrameMap, MkFrameSlice,
};
// Lifecycle
pub use crate::lifecycle::{MkPhase, MkCheckpoint, MkScope};
// Tagging
pub use crate::tag::{MkIntent, MkGroup};
// Transfer
pub use crate::transfer::MkTransferHandle;
// Traits
pub use crate::traits::{Allocator, Resettable, Tagged};
// Stats
pub use crate::stats::MkStats;
// Errors
pub use crate::error::MkAllocError;
```
### 3.4 Example Usage
```rust
use memkit::{MkAllocator, MkConfig, MkIntent};
fn main() {
// Create allocator with default config
let alloc = MkAllocator::new(MkConfig::default());
// Game loop
loop {
alloc.begin_frame();
// Frame-local allocation (freed at end_frame)
let positions = alloc.frame_vec::<Vec3>(1000);
// Tagged allocation for debugging
alloc.with_intent(MkIntent::Physics, || {
let contacts = alloc.frame_vec::<Contact>(100);
process_physics(&contacts);
});
// Pool allocation (returned to pool on drop)
let entity = alloc.pool_box(Entity::new());
// Heap allocation (manual lifetime)
let texture = alloc.heap_box(load_texture());
alloc.end_frame();
}
}
```
---
## 4. GPU Crate: memkit-gpu
### 4.1 Purpose
GPU-side memory management with **backend-agnostic generics**:
- Staging buffers (CPU → GPU transfer)
- Device-local buffers
- Memory pools per GPU heap type
- Transfer synchronization
- Multi-GPU support (future)
### 4.2 Design Philosophy: Backend-Agnostic Generics
**Key Principle:** User code should not need to know if it's running on Vulkan, Metal, or DX12.
memkit-gpu provides a **generic GPU memory layer** that abstracts over backends:
```rust
// User code is backend-agnostic
fn upload_mesh<G: MkGpuBackend>(gpu: &MkGpu<G>, vertices: &[Vertex]) -> MkDeviceBuffer<G> {
let staging = gpu.staging_buffer(vertices.len() * size_of::<Vertex>());
staging.write(vertices);
let device = gpu.device_buffer(staging.size());
gpu.transfer(&staging, &device);
device
}
// Backend selected at initialization only
#[cfg(target_os = "windows")]
let gpu = MkGpu::<Vulkan>::new(&instance);
#[cfg(target_os = "macos")]
let gpu = MkGpu::<Metal>::new(&device);
```
### 4.3 Generic Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ User Code (Generic) │
│ MkGpu<G>, MkDeviceBuffer<G>, MkStagingBuffer<G> │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ MkGpuBackend Trait │
│ - allocate_device() - map_memory() - submit_transfer() │
│ - allocate_staging() - unmap_memory() - wait_fence() │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Vulkan │ │ Metal │ │ DX12 │
│ (via ash) │ │ (via metal) │ │ (via windows) │
└───────────────┘ └───────────────┘ └───────────────┘
```
### 4.4 Module Structure
```
memkit-gpu/src/
├── lib.rs
│
├── generic/ # Backend-agnostic types (NEW)
│ ├── mod.rs
│ ├── gpu.rs # MkGpu<G> - main entry point
│ ├── buffer.rs # MkBuffer<G>, MkDeviceBuffer<G>, MkStagingBuffer<G>
│ ├── memory.rs # MkMemoryBlock<G>, MkMemoryType
│ ├── queue.rs # MkQueue<G> - command submission
│ └── sync.rs # MkFence<G>, MkSemaphore<G>
│
├── traits/ # Backend trait definitions
│ ├── mod.rs
│ ├── backend.rs # MkGpuBackend - main backend trait
│ ├── allocator.rs # MkGpuAllocator - allocation strategy
│ ├── mappable.rs # MkMappable - CPU-accessible memory
│ ├── transferable.rs # MkTransferable - GPU transfers
│ └── capabilities.rs # MkCapabilities - query backend features
│
├── buffer/ # High-level buffer types
│ ├── mod.rs
│ ├── staging.rs # MkStagingBuffer<G>
│ ├── device.rs # MkDeviceBuffer<G>
│ ├── uniform.rs # MkUniformBuffer<G>
│ └── storage.rs # MkStorageBuffer<G> (compute)
│
├── pool/ # GPU memory pools
│ ├── mod.rs
│ ├── device_local.rs # Device-local pool
│ ├── host_visible.rs # Host-visible pool
│ ├── unified.rs # Unified memory (if available)
│ └── strategy.rs # Pool allocation strategies
│
├── transfer/ # CPU ↔ GPU transfers
│ ├── mod.rs
│ ├── upload.rs # CPU → GPU
│ ├── download.rs # GPU → CPU
│ ├── copy.rs # GPU → GPU
│ └── batch.rs # Batched transfers
│
├── backend/ # Backend implementations
│ ├── mod.rs
│ ├── vulkan/ # Vulkan backend
│ │ ├── mod.rs
│ │ ├── instance.rs
│ │ ├── device.rs
│ │ ├── memory.rs
│ │ └── commands.rs
│ ├── metal/ # Metal backend (future)
│ │ └── mod.rs
│ ├── dx12/ # DirectX 12 backend (future)
│ │ └── mod.rs
│ └── dummy/ # No-op backend for testing
│ └── mod.rs
│
└── coord/ # Coordination (with CPU)
├── mod.rs
├── barrier.rs # MkGpuBarrier<G>
├── fence.rs # MkFence<G>
└── timeline.rs # Timeline semaphores
```
### 4.5 Core Traits
```rust
/// Main backend trait - implement this for each graphics API
pub trait MkGpuBackend: Sized + Send + Sync + 'static {
type Device;
type Buffer;
type Memory;
type Fence;
type Queue;
type Error: std::error::Error;
// Initialization
fn create(config: &MkGpuConfig) -> Result<Self, Self::Error>;
fn capabilities(&self) -> MkCapabilities;
// Memory allocation
fn allocate(
&self,
size: usize,
memory_type: MkMemoryType,
) -> Result<Self::Memory, Self::Error>;
fn deallocate(&self, memory: Self::Memory);
// Buffer operations
fn create_buffer(
&self,
size: usize,
usage: MkBufferUsage,
memory: &Self::Memory,
) -> Result<Self::Buffer, Self::Error>;
fn destroy_buffer(&self, buffer: Self::Buffer);
// Mapping
fn map(&self, memory: &Self::Memory) -> Result<*mut u8, Self::Error>;
fn unmap(&self, memory: &Self::Memory);
fn flush(&self, memory: &Self::Memory, offset: usize, size: usize);
// Transfers
fn transfer(
&self,
src: &Self::Buffer,
dst: &Self::Buffer,
size: usize,
) -> Result<Self::Fence, Self::Error>;
// Synchronization
fn wait(&self, fence: &Self::Fence) -> Result<(), Self::Error>;
fn is_complete(&self, fence: &Self::Fence) -> bool;
}
/// Memory types abstracted across backends
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MkMemoryType {
/// Fast GPU memory, not CPU accessible
DeviceLocal,
/// CPU-accessible, coherent (no flush needed)
HostCoherent,
/// CPU-accessible, cached (faster reads, needs flush)
HostCached,
/// Unified memory (AMD APU, Apple Silicon, etc.)
Unified,
}
/// Buffer usage flags
bitflags! {
pub struct MkBufferUsage: u32 {
const TRANSFER_SRC = 0x0001;
const TRANSFER_DST = 0x0002;
const UNIFORM = 0x0010;
const STORAGE = 0x0020;
const VERTEX = 0x0080;
const INDEX = 0x0040;
}
}
```
### 4.6 Public API Surface
```rust
// Generic types (backend-agnostic)
pub use crate::generic::{MkGpu, MkBuffer, MkDeviceBuffer, MkStagingBuffer};
pub use crate::generic::{MkMemoryBlock, MkMemoryType, MkBufferUsage};
pub use crate::generic::{MkFence, MkQueue};
// Traits
pub use crate::traits::{MkGpuBackend, MkGpuAllocator, MkMappable, MkCapabilities};
// Pools
pub use crate::pool::{MkDevicePool, MkHostPool, MkUnifiedPool};
// Transfer utilities
pub use crate::transfer::{MkUpload, MkDownload, MkBatchTransfer};
// Coordination
pub use crate::coord::{MkGpuBarrier, MkTimeline};
// Backends (feature-gated)
#[cfg(feature = "vulkan")]
pub use crate::backend::vulkan::Vulkan;
#[cfg(feature = "metal")]
pub use crate::backend::metal::Metal;
#[cfg(feature = "dx12")]
pub use crate::backend::dx12::Dx12;
pub use crate::backend::dummy::Dummy;
```
### 4.7 Example Usage (Backend-Agnostic)
```rust
use memkit::MkAllocator;
use memkit_gpu::{MkGpu, MkGpuBackend, MkMemoryType, MkBufferUsage};
// Generic function works with ANY backend
fn upload_mesh_data<G: MkGpuBackend>(
gpu: &MkGpu<G>,
vertices: &[Vertex],
indices: &[u32],
) -> Result<(MkDeviceBuffer<G>, MkDeviceBuffer<G>), G::Error> {
// Create staging buffers
let vertex_staging = gpu.staging_buffer(vertices)?;
let index_staging = gpu.staging_buffer(indices)?;
// Create device-local buffers
let vertex_buffer = gpu.device_buffer(
vertices.len() * size_of::<Vertex>(),
MkBufferUsage::VERTEX | MkBufferUsage::TRANSFER_DST,
)?;
let index_buffer = gpu.device_buffer(
indices.len() * size_of::<u32>(),
MkBufferUsage::INDEX | MkBufferUsage::TRANSFER_DST,
)?;
// Batch transfer (efficient)
gpu.batch_transfer()
.copy(&vertex_staging, &vertex_buffer)
.copy(&index_staging, &index_buffer)
.submit_and_wait()?;
Ok((vertex_buffer, index_buffer))
}
// Backend selected at app initialization
fn main() {
// Auto-detect best backend
let gpu = MkGpu::auto_detect().expect("No GPU backend available");
// Or explicitly choose
#[cfg(feature = "vulkan")]
let gpu = MkGpu::<Vulkan>::new(Default::default()).unwrap();
let (vbo, ibo) = upload_mesh_data(&gpu, &vertices, &indices).unwrap();
// Use buffers for rendering...
}
```
---
## 5. Async Crate: memkit-async
### 5.1 Purpose
**Custom async-aware allocators** built on memkit's architecture (not just Tokio integration):
- Native async allocator types designed for concurrent workloads
- Task-local frame allocators with proper async lifetime semantics
- Async-safe memory pools with backpressure support
- Zero-copy async channels for allocated data
- Cooperative yielding during large allocations
### 5.2 Design Philosophy: Async-Native Allocators
**Key Principle:** Async allocators understand async context and behave correctly across `.await` points.
Problems with naive async + allocators:
```rust
// PROBLEM: Frame data may be invalid after await
let data = alloc.frame_box(value);
some_async_op().await; // Frame may have reset!
use_data(&data); // Undefined behavior
```
memkit-async provides allocators that understand async:
```rust
// SOLUTION: Async-aware allocator
let data = async_alloc.async_frame_box(value).await;
some_async_op().await; // Allocator tracks task state
use_data(&data); // Safe - allocator ensures validity
```
### 5.3 Core Async Allocator Types
```rust
/// Async-aware frame allocator
/// - Tracks which task owns each allocation
/// - Prevents reset while any task has live allocations
/// - Supports cooperative frame boundaries across tasks
pub struct MkAsyncFrameAlloc { /* ... */ }
/// Async memory pool with backpressure
/// - Async acquire that yields when pool is exhausted
/// - Configurable backpressure behavior
/// - Fair scheduling across waiting tasks
pub struct MkAsyncPool<T> { /* ... */ }
/// Zero-copy async channel for allocated data
/// - Transfer ownership without copying
/// - Works with any memkit allocation type
/// - Bounded and unbounded variants
pub struct MkAsyncChannel<T> { /* ... */ }
/// Task-local allocator context
/// - Each task gets isolated allocation context
/// - Automatic cleanup on task completion
/// - Hierarchical (child tasks can inherit parent context)
pub struct MkTaskLocal { /* ... */ }
```
### 5.4 Module Structure
```
memkit-async/src/
├── lib.rs
│
├── allocator/ # Async-native allocators (NEW)
│ ├── mod.rs
│ ├── async_frame.rs # MkAsyncFrameAlloc
│ ├── async_pool.rs # MkAsyncPool<T>
│ ├── async_heap.rs # MkAsyncHeap (with async cleanup)
│ └── backpressure.rs # Backpressure policies
│
├── container/ # Async-safe containers (NEW)
│ ├── mod.rs
│ ├── async_box.rs # MkAsyncBox<T> - async Drop
│ ├── async_vec.rs # MkAsyncVec<T> - growable async
│ └── async_buffer.rs # MkAsyncBuffer - streaming
│
├── channel/ # Zero-copy channels (NEW)
│ ├── mod.rs
│ ├── bounded.rs # Bounded channel
│ ├── unbounded.rs # Unbounded channel
│ └── broadcast.rs # One-to-many broadcast
│
├── task/ # Task-local allocation
│ ├── mod.rs
│ ├── context.rs # MkTaskLocal - task context
│ ├── scope.rs # MkAsyncScope - async RAII
│ └── inherit.rs # Parent-child inheritance
│
├── transfer/ # Async cross-task transfer
│ ├── mod.rs
│ ├── handle.rs # MkAsyncTransferHandle
│ └── stream.rs # Streaming transfers
│
├── runtime/ # Runtime integration
│ ├── mod.rs
│ ├── tokio.rs # Tokio hooks
│ ├── async_std.rs # async-std hooks (future)
│ └── executor.rs # Generic executor trait
│
└── sync/ # Async synchronization
├── mod.rs
├── barrier.rs # MkAsyncBarrier
├── semaphore.rs # Allocation semaphore
└── notify.rs # Allocation notifications
```
### 5.5 Core Traits
```rust
/// Async allocator trait - extends base Allocator for async contexts
#[async_trait]
pub trait AsyncAllocator: Allocator {
/// Allocate with async backpressure
/// Returns Pending if memory not available, yields to runtime
async fn allocate_async(&self, layout: Layout) -> Result<NonNull<u8>, Self::Error>;
/// Check if allocation would block
fn would_block(&self, layout: Layout) -> bool;
/// Get task-local allocation context
fn task_context(&self) -> Option<&MkTaskLocal>;
}
/// Async-safe container trait
#[async_trait]
pub trait AsyncContainer: Sized {
type Allocator: AsyncAllocator;
/// Async drop - allows yielding during cleanup
async fn async_drop(self);
/// Transfer to another task
async fn transfer(self) -> MkAsyncTransferHandle<Self>;
}
/// Backpressure policy for async pools
pub enum MkBackpressure {
/// Block until memory available
Wait,
/// Return error immediately
Fail,
/// Wait with timeout
Timeout(Duration),
/// Evict least-recently-used
Evict,
}
```
### 5.6 Public API Surface
```rust
// Async allocators
pub use crate::allocator::{MkAsyncFrameAlloc, MkAsyncPool, MkAsyncHeap};
pub use crate::allocator::MkBackpressure;
// Async containers
pub use crate::container::{MkAsyncBox, MkAsyncVec, MkAsyncBuffer};
// Channels
pub use crate::channel::{MkAsyncChannel, MkBroadcast};
// Task-local
pub use crate::task::{MkTaskLocal, MkAsyncScope};
// Transfer
pub use crate::transfer::{MkAsyncTransferHandle, MkAsyncStream};
// Traits
pub use crate::traits::{AsyncAllocator, AsyncContainer};
// Sync primitives
pub use crate::sync::{MkAsyncBarrier, MkAllocSemaphore};
```
### 5.7 Example Usage
```rust
use memkit_async::{
MkAsyncFrameAlloc, MkAsyncPool, MkAsyncChannel,
MkTaskLocal, MkBackpressure, AsyncAllocator,
};
#[tokio::main]
async fn main() {
// Create async-aware frame allocator
let async_alloc = MkAsyncFrameAlloc::new(Default::default());
// Spawn multiple tasks that share the allocator safely
let handles: Vec<_> = (0..10).map(|i| {
let alloc = async_alloc.clone();
tokio::spawn(async move {
// Task-local context - isolated from other tasks
MkTaskLocal::enter(&alloc, async {
// This allocation is tracked per-task
let data = alloc.async_frame_box(vec![i; 1024]).await;
// Safe across await - allocator tracks task state
tokio::time::sleep(Duration::from_millis(10)).await;
// Still valid!
process(&data);
}).await
})
}).collect();
// Wait for all tasks
for h in handles {
h.await.unwrap();
}
// Async pool with backpressure
let pool: MkAsyncPool<LargeBuffer> = MkAsyncPool::new(
100, // capacity
MkBackpressure::Wait, // block if exhausted
);
// This will yield to runtime if pool is exhausted
let buffer = pool.acquire().await;
// Zero-copy channel for allocated data
let (tx, rx) = MkAsyncChannel::<MkAsyncBox<GameState>>::bounded(16);
tokio::spawn(async move {
let state = async_alloc.async_heap_box(GameState::new()).await;
tx.send(state).await.unwrap(); // Zero-copy transfer
});
let received = rx.recv().await.unwrap();
}
```
### 5.8 Async Frame Lifecycle
```rust
use memkit_async::{MkAsyncFrameAlloc, MkAsyncFrameGuard};
async fn game_loop() {
let alloc = MkAsyncFrameAlloc::new(Default::default());
loop {
// Async frame guard - waits for all tasks to release allocations
let frame = alloc.begin_frame_async().await;
// Spawn work that uses frame allocations
let physics = tokio::spawn({
let alloc = alloc.clone();
async move {
let contacts = alloc.async_frame_vec::<Contact>(1000).await;
simulate_physics(&contacts).await;
// contacts automatically tracked
}
});
let render = tokio::spawn({
let alloc = alloc.clone();
async move {
let commands = alloc.async_frame_vec::<DrawCommand>(500).await;
build_render_list(&commands).await;
}
});
// Wait for all frame work
let _ = tokio::join!(physics, render);
// Frame guard ensures all allocations are released
// before allowing reset
drop(frame); // Or: frame.end().await;
}
}
```
---
## 6. Integration Crates
### 6.1 memkit-bevy
```
memkit-bevy/src/
├── lib.rs
├── plugin.rs # MkBevyPlugin
├── resource.rs # MkAllocator as Bevy Resource
├── system.rs # Frame begin/end systems
└── component.rs # MkAllocated<T> component
```
**Usage:**
```rust
use bevy::prelude::*;
use memkit_bevy::MkBevyPlugin;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(MkBevyPlugin::default())
.add_systems(Update, my_system)
.run();
}
fn my_system(alloc: Res<MkAllocator>) {
let temp = alloc.frame_vec::<Transform>(100);
// ...
}
```
### 6.2 memkit-rapier
```
memkit-rapier/src/
├── lib.rs
├── plugin.rs # Rapier integration plugin
├── broad_phase.rs # Custom broad-phase allocator
├── narrow_phase.rs # Contact manifold allocation
└── pipeline.rs # Physics pipeline integration
```
**Usage:**
```rust
use memkit::MkAllocator;
use memkit_rapier::MkRapierPlugin;
use rapier3d::prelude::*;
fn main() {
let alloc = MkAllocator::new(Default::default());
let physics = PhysicsPipeline::new();
// Install memkit as Rapier's allocator
MkRapierPlugin::install(&alloc, &mut physics);
}
```
---
## 7. Tooling: cargo-memlense
### 7.1 Purpose
Static analysis and diagnostics tool (replaces cargo-fa):
- Detect memory intent violations at compile time
- Suggest optimizations
- Generate memory usage reports
- IDE integration (LSP)
### 7.2 Features
1. **Static Analysis**
- Cross-thread frame access detection
- Allocation lifetime analysis
- Tag consistency checking
- Budget violation prediction
2. **Diagnostics Codes**
- ML1xx: Frame lifecycle errors
- ML2xx: Threading errors
- ML3xx: GPU memory errors
- ML4xx: Performance warnings
- ML5xx: Best practice hints
3. **IDE Integration**
- LSP server mode
- Inline diagnostics
- Quick fixes
### 7.3 Module Structure
```
cargo-memlense/src/
├── main.rs # CLI entry point
│
├── cli/ # Command-line interface
│ ├── mod.rs
│ ├── args.rs
│ └── commands.rs
│
├── analysis/ # Static analysis passes
│ ├── mod.rs
│ ├── lifetime.rs # Allocation lifetime analysis
│ ├── threading.rs # Cross-thread analysis
│ ├── gpu.rs # GPU memory analysis
│ ├── budget.rs # Budget analysis
│ └── intent.rs # Intent/tag analysis
│
├── diagnostic/ # Diagnostic infrastructure
│ ├── mod.rs
│ ├── code.rs # Diagnostic codes (ML1xx, etc.)
│ ├── emit.rs # Diagnostic emission
│ ├── severity.rs # Error/Warning/Hint
│ └── suggestion.rs # Fix suggestions
│
├── lsp/ # Language Server Protocol
│ ├── mod.rs
│ ├── server.rs
│ └── handlers.rs
│
├── report/ # Report generation
│ ├── mod.rs
│ ├── json.rs
│ ├── html.rs
│ └── summary.rs
│
└── explain/ # Diagnostic explanations
├── mod.rs
└── database.rs # Explanation text database
```
### 7.4 Usage
```bash
# Basic analysis
cargo memlense check
# With specific checks
cargo memlense check --threading --gpu
# Generate report
cargo memlense report --format html --output report.html
# LSP server mode (for IDEs)
cargo memlense lsp
# Explain a diagnostic
cargo memlense explain ML201
```
---
## 8. Future: Realtime Memory (memkit-realtime)
> **Status:** Planned for post-1.0 release. Provisions made in core architecture.
### 8.1 Vision
memkit-realtime will provide **hard realtime memory guarantees** - something framealloc never attempted. This is for systems where allocation latency must be bounded and predictable:
- Audio processing (< 1ms latency budgets)
- Robotics control loops
- Medical devices
- Industrial automation
- VR/AR with strict frame timing
### 8.2 What Makes Realtime Different
**Standard allocators (including current memkit):**
- Best-effort performance
- May occasionally spike
- No worst-case guarantees
- Acceptable for games (occasional frame drop OK)
**Realtime allocators:**
- Bounded worst-case latency (WCET)
- No unbounded loops
- No system calls in hot path
- Provable timing properties
- Zero allocation failures after initialization
### 8.3 Planned Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ memkit-realtime │
├─────────────────────────────────────────────────────────────────┤
│ MkRealtimeAllocator │
│ - Pre-allocated memory pools │
│ - O(1) allocation/deallocation │
│ - No locks (lock-free or wait-free) │
│ - Bounded fragmentation │
├─────────────────────────────────────────────────────────────────┤
│ MkRealtimeConfig │
│ - WCET bounds specification │
│ - Memory budget (pre-allocated) │
│ - Failure policy (panic vs fallback) │
├─────────────────────────────────────────────────────────────────┤
│ MkRealtimeAnalyzer (compile-time) │
│ - Static WCET analysis │
│ - Allocation pattern verification │
│ - Memory bound proofs │
└─────────────────────────────────────────────────────────────────┘
```
### 8.4 Core Types (Planned)
```rust
/// Realtime allocator with bounded timing
pub struct MkRealtimeAllocator {
// All memory pre-allocated at construction
pools: [MkRealtimePool; N],
// O(1) free list per size class
free_lists: [AtomicPtr<Node>; N],
// Configuration
config: MkRealtimeConfig,
}
/// Realtime configuration
pub struct MkRealtimeConfig {
/// Maximum allocation size supported
pub max_allocation: usize,
/// Total memory budget (pre-allocated)
pub memory_budget: usize,
/// Worst-case allocation time bound
pub wcet_alloc: Duration,
/// Worst-case deallocation time bound
pub wcet_dealloc: Duration,
/// What to do if allocation would exceed budget
pub overflow_policy: MkOverflowPolicy,
}
/// Overflow handling
pub enum MkOverflowPolicy {
/// Panic immediately (fail-fast)
Panic,
/// Return error (caller handles)
Error,
/// Use fallback allocator (breaks RT guarantees)
Fallback(Box<dyn Allocator>),
}
/// Realtime-safe container (no dynamic growth)
pub struct MkRealtimeVec<T, const N: usize> {
data: [MaybeUninit<T>; N],
len: usize,
}
/// Realtime pool with O(1) operations
pub struct MkRealtimePool<T> {
slots: Box<[MaybeUninit<T>]>,
free_head: AtomicUsize,
// Bitmap for O(1) free slot finding
free_bitmap: AtomicU64,
}
```
### 8.5 Guarantees
| Allocation time | O(1) worst-case |
| Deallocation time | O(1) worst-case |
| Memory overhead | ≤ 2x requested size |
| Fragmentation | Bounded (size-class based) |
| Thread safety | Lock-free (wait-free optional) |
| Failure mode | Deterministic |
### 8.6 Provisions in Core memkit
To enable realtime support later, the core memkit architecture includes:
1. **Trait-based allocation**
```rust
pub trait Allocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<u8>, Self::Error>;
}
```
2. **No hidden allocations**
- All memkit types document their allocation behavior
- No surprise heap allocations in hot paths
3. **Configurable policies**
- Failure handling is configurable, not hardcoded
- Allows strict realtime policies
4. **Size-class awareness**
- Internal pool structures use size classes
- Compatible with realtime pool strategies
5. **Timing hooks**
```rust
pub trait TimingHooks {
fn on_alloc_start(&self, layout: Layout);
fn on_alloc_end(&self, layout: Layout, duration: Duration);
}
```
### 8.7 Module Structure (Planned)
```
memkit-realtime/src/
├── lib.rs
│
├── allocator/ # Realtime allocators
│ ├── mod.rs
│ ├── pool.rs # MkRealtimePool - O(1) pool
│ ├── slab.rs # MkRealtimeSlab - fixed-size
│ ├── bump.rs # MkRealtimeBump - linear, no free
│ └── tlsf.rs # TLSF allocator (O(1) general purpose)
│
├── container/ # Realtime containers
│ ├── mod.rs
│ ├── vec.rs # MkRealtimeVec<T, N> - fixed capacity
│ ├── ring.rs # MkRealtimeRing<T, N> - ring buffer
│ ├── queue.rs # MkRealtimeQueue<T, N> - SPSC/MPMC
│ └── map.rs # MkRealtimeMap<K, V, N> - bounded map
│
├── sync/ # Lock-free primitives
│ ├── mod.rs
│ ├── atomic_pool.rs # Wait-free pool operations
│ └── hazard.rs # Hazard pointers for safe reclamation
│
├── analysis/ # Static analysis
│ ├── mod.rs
│ ├── wcet.rs # WCET estimation
│ └── verify.rs # Allocation pattern verification
│
└── config/ # Configuration
├── mod.rs
└── builder.rs # MkRealtimeConfig builder
```
### 8.8 Timeline
| Provisions | v0.12.0 | Core traits designed for RT compatibility |
| Research | v0.14.0 | WCET analysis, TLSF implementation |
| Alpha | v0.16.0 | Basic MkRealtimeAllocator |
| Beta | v0.18.0 | Full container suite |
| Stable | v1.2.0 | Production-ready realtime support |
### 8.9 Use Case Example (Future)
```rust
use memkit_realtime::{MkRealtimeAllocator, MkRealtimeConfig, MkRealtimeVec};
// Audio callback - must complete in < 1ms
fn audio_callback(alloc: &MkRealtimeAllocator, samples: &mut [f32]) {
// O(1) allocation, guaranteed
let mut buffer: MkRealtimeVec<f32, 1024> = alloc.realtime_vec();
// Process audio
for (i, sample) in samples.iter_mut().enumerate() {
buffer.push(process(*sample)); // O(1), no realloc
}
// Apply effects
apply_reverb(&mut buffer);
// Copy back
samples.copy_from_slice(&buffer);
// O(1) deallocation on drop
}
fn main() {
let config = MkRealtimeConfig::builder()
.memory_budget(64 * 1024 * 1024) // 64MB pre-allocated
.wcet_alloc(Duration::from_micros(10))
.wcet_dealloc(Duration::from_micros(10))
.overflow_policy(MkOverflowPolicy::Panic)
.build();
let alloc = MkRealtimeAllocator::new(config);
// Run audio thread with realtime allocator
audio_thread::run(move |samples| {
audio_callback(&alloc, samples);
});
}
```
---
## 9. API Coalescing Plan
This section details which current framealloc APIs should be merged, split, or removed.
### 9.1 Coalesce: Lifecycle Management
**Current (scattered):**
- `api/scope.rs` - FrameGuard, FrameScope
- `api/phases.rs` - PhaseGuard, PhaseTracker
- `api/checkpoint.rs` - Checkpoint, SpeculativeResult
- `api/lifecycle.rs` - LifecycleCallbacks, LifecycleEvent
**New (unified):**
```
lifecycle/
├── frame.rs # begin_frame/end_frame + callbacks
├── phase.rs # MkPhase (named scopes within frames)
├── checkpoint.rs # MkCheckpoint (save/restore)
└── scope.rs # MkScope (RAII guards)
```
**Rationale:** All frame lifecycle concerns in one module. Callbacks become part of frame management, not separate.
### 9.2 Coalesce: Policy Management
**Current (scattered):**
- `api/retention.rs` - RetentionPolicy, RetentionRegistry, ImportanceLevel
- `api/promotion.rs` - PromotionConfig, FrameUsageSummary
- `api/thread_budget.rs` - ThreadBudgetConfig, ThreadBudget
- `core/budget.rs` - BudgetTracker
**New (unified):**
```
policy/
├── retention.rs # When to keep allocations
├── promotion.rs # Frame → Pool → Heap promotion rules
└── budget.rs # All budget/limit logic (thread + global)
```
**Rationale:** These are all "policy decisions" about memory. Unify under one concept.
### 9.3 Coalesce: Container Types
**Current (scattered):**
- `api/wrappers.rs` - FrameBox, PoolBox, HeapBox, FrameSlice
- `api/frame_collections.rs` - FrameVec, FrameMap, FrameString
- `api/scratch.rs` - ScratchPool, ScratchHandle
**New (unified):**
```
container/
├── box.rs # MkFrameBox, MkPoolBox, MkHeapBox
├── vec.rs # MkFrameVec
├── map.rs # MkFrameMap
├── slice.rs # MkFrameSlice
└── scratch.rs # MkScratchPool (cross-frame reusable)
```
**Rationale:** All "things that hold allocations" in one place.
### 9.4 Coalesce: Tagging & Groups
**Current (scattered):**
- `api/tag.rs` - AllocationTag, AllocationIntent
- `api/tagged.rs` - TagGuard, TagStack
- `api/groups.rs` - AllocationGroup, GroupHandle
**New (unified):**
```
tag/
├── intent.rs # MkIntent - why allocated
├── group.rs # MkGroup - bulk operations
└── registry.rs # Tag registration
```
**Rationale:** Tags and groups are related concepts for allocation attribution.
### 9.5 Coalesce: Transfer & Sync
**Current (scattered):**
- `api/transfer.rs` - TransferHandle, TransferId
- `api/barrier.rs` - CpuGpuBarrier, BarrierBuilder
- `sync/` - various sync primitives
- `api/deferred_control.rs` - DeferredController, DeferredConfig
**New (unified):**
```
transfer/
├── thread.rs # Cross-thread transfer
└── handle.rs # MkTransferHandle
sync/
├── barrier.rs # MkBarrier (CPU-side)
└── deferred.rs # Deferred queue control
```
**Rationale:** Transfer is about moving data. Sync is about coordination. Split clearly.
### 9.6 Remove or Deprecate
| `handles.rs` (root) | **Remove** | Re-export of allocators::handles, not needed |
| `streaming.rs` (root) | **Merge** | Fold into allocator/handle.rs |
| `api/allocator_impl.rs` | **Merge** | Fold into main allocator |
| `api/stats.rs` | **Simplify** | Minimal stats only, rest to cargo-memlense |
| `api/snapshot.rs` | **Move** | To cargo-memlense (diagnostics concern) |
| `diagnostics/` (all) | **Move** | To cargo-memlense entirely |
### 9.7 Coalescing Summary Table
| `api/scope.rs`, `api/phases.rs`, `api/checkpoint.rs`, `api/lifecycle.rs` | 4 | `lifecycle/` | Unified frame lifecycle |
| `api/retention.rs`, `api/promotion.rs`, `api/thread_budget.rs`, `core/budget.rs` | 4 | `policy/` | Unified policy management |
| `api/wrappers.rs`, `api/frame_collections.rs`, `api/scratch.rs` | 3 | `container/` | Unified containers |
| `api/tag.rs`, `api/tagged.rs`, `api/groups.rs` | 3 | `tag/` | Unified tagging |
| `api/transfer.rs`, `api/barrier.rs`, `api/deferred_control.rs` | 3 | `transfer/` + `sync/` | Split by concern |
| `allocators/*` | 7 | `allocator/` | Rename only |
| `diagnostics/*` | 10 | `cargo-memlense` | Move to tool |
| `api/snapshot.rs` | 1 | `cargo-memlense` | Diagnostics concern |
**Result:** From ~40 scattered files to ~20 organized modules.
---
## 9. Naming Convention
### 9.1 Type Prefix: `Mk`
All public types use the `Mk` prefix:
```rust
// Allocator
MkAllocator // Main entry point (was SmartAlloc)
MkConfig // Configuration (was AllocConfig)
// Containers
MkFrameBox<T> // Frame-allocated box (was FrameBox)
MkPoolBox<T> // Pool-allocated box (was PoolBox)
MkHeapBox<T> // Heap-allocated box (was HeapBox)
MkFrameVec<T> // Frame-allocated vector (was FrameVec)
MkFrameMap<K,V> // Frame-allocated map (was FrameMap)
MkFrameSlice<T> // Frame-allocated slice (was FrameSlice)
// Lifecycle
MkPhase // Named scope within frame (was PhaseGuard)
MkCheckpoint // Save/restore point (was Checkpoint)
MkScope // RAII guard (was FrameGuard)
// Policy
MkBudget // Memory budget (was BudgetTracker)
MkRetention // Retention policy (was RetentionPolicy)
MkPromotion // Promotion config (was PromotionConfig)
// Tagging
MkIntent // Allocation intent (was AllocationIntent)
MkGroup // Allocation group (was AllocationGroup)
MkTag // Allocation tag (was AllocationTag)
// Transfer
MkTransferHandle // Cross-thread transfer (was TransferHandle)
MkBarrier // Synchronization barrier (was CpuGpuBarrier)
// Stats
MkStats // Runtime statistics (was AllocStats)
// Errors
MkAllocError // Allocation error (was various)
MkBudgetExceeded // Budget exceeded (new)
MkTransferError // Transfer failed (new)
```
### 9.2 Trait Naming
Traits do NOT use the `Mk` prefix (Rust convention):
```rust
trait Allocator // Core allocation trait
trait Resettable // Can be reset (frame allocators)
trait Tagged // Supports tagging
trait Transferable // Can be transferred across contexts
trait GpuAllocator // GPU allocation trait (in memkit-gpu)
trait Mappable // GPU memory that can be mapped
```
### 9.3 Module Naming
Modules use lowercase, no prefix:
```rust
use memkit::allocator; // Not mk_allocator
use memkit::container; // Not mk_container
use memkit::lifecycle; // Not mk_lifecycle
```
### 9.4 Function Naming
Methods use snake_case, descriptive names:
```rust
impl MkAllocator {
fn new(config: MkConfig) -> Self;
fn begin_frame(&self);
fn end_frame(&self);
fn frame_box<T>(&self, value: T) -> MkFrameBox<T>;
fn frame_vec<T>(&self, capacity: usize) -> MkFrameVec<T>;
fn pool_box<T>(&self, value: T) -> MkPoolBox<T>;
fn heap_box<T>(&self, value: T) -> MkHeapBox<T>;
fn with_intent<F, R>(&self, intent: MkIntent, f: F) -> R;
fn with_phase<F, R>(&self, name: &str, f: F) -> R;
fn checkpoint(&self) -> MkCheckpoint;
fn stats(&self) -> MkStats;
}
```
---
## 10. Type Migration Table
Complete mapping from framealloc to memkit:
| `SmartAlloc` | `MkAllocator` | Main entry point |
| `AllocConfig` | `MkConfig` | Configuration |
| `AllocStats` | `MkStats` | Statistics |
| `FrameBox<T>` | `MkFrameBox<T>` | Frame-allocated box |
| `PoolBox<T>` | `MkPoolBox<T>` | Pool-allocated box |
| `HeapBox<T>` | `MkHeapBox<T>` | Heap-allocated box |
| `FrameSlice<T>` | `MkFrameSlice<T>` | Frame-allocated slice |
| `FrameVec<T>` | `MkFrameVec<T>` | Frame-allocated vector |
| `FrameMap<K,V>` | `MkFrameMap<K,V>` | Frame-allocated map |
| `FrameGuard` | `MkScope` | RAII guard |
| `FrameScope` | `MkScope` | Same, unified |
| `PhaseGuard` | `MkPhase` | Named scope |
| `PhaseTracker` | (internal) | No longer public |
| `Checkpoint` | `MkCheckpoint` | Save/restore |
| `SpeculativeResult<T>` | `MkCheckpoint::Result<T>` | Associated type |
| `AllocationTag` | `MkTag` | Tag type |
| `AllocationIntent` | `MkIntent` | Intent enum |
| `AllocationGroup` | `MkGroup` | Group type |
| `GroupHandle` | `MkGroup::Handle` | Associated type |
| `TagGuard` | `MkTag::Guard` | Associated type |
| `TagStack` | (internal) | No longer public |
| `TransferHandle` | `MkTransferHandle` | Cross-thread transfer |
| `TransferId` | `MkTransferHandle::Id` | Associated type |
| `CpuGpuBarrier` | `MkBarrier` | Sync barrier |
| `BarrierBuilder` | `MkBarrier::Builder` | Associated type |
| `RetentionPolicy` | `MkRetention` | Policy |
| `ImportanceLevel` | `MkRetention::Importance` | Associated type |
| `PromotionConfig` | `MkPromotion` | Policy |
| `ThreadBudgetConfig` | `MkBudget::Config` | Associated type |
| `BudgetTracker` | `MkBudget` | Budget tracking |
| `ScratchPool` | `MkScratchPool` | Cross-frame pool |
| `ScratchHandle` | `MkScratchPool::Handle` | Associated type |
| `DeferredController` | (internal) | No longer public |
| `DeferredConfig` | `MkConfig::Deferred` | Part of config |
| `LifecycleCallbacks` | `MkAllocator::on_*` | Methods, not struct |
| `HandleAllocator` | `MkHandleAllocator` | Handle-based |
| `StreamingAllocator` | (merged) | Into MkHandleAllocator |
---
## 11. Trait Architecture
### 11.1 Core Traits (memkit)
```rust
/// Core allocation trait - all allocators implement this
pub trait Allocator {
type Error;
fn allocate(&self, layout: Layout) -> Result<NonNull<u8>, Self::Error>;
fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
/// Optional: allocate zeroed memory
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<u8>, Self::Error> {
let ptr = self.allocate(layout)?;
unsafe { ptr.as_ptr().write_bytes(0, layout.size()); }
Ok(ptr)
}
}
/// Resettable allocators (frame allocators)
pub trait Resettable: Allocator {
fn reset(&self);
fn bytes_used(&self) -> usize;
fn bytes_capacity(&self) -> usize;
}
/// Tagged allocation support
pub trait Tagged: Allocator {
fn current_tag(&self) -> Option<MkTag>;
fn with_tag<F, R>(&self, tag: MkTag, f: F) -> R
where
F: FnOnce() -> R;
}
/// Intent-aware allocation
pub trait Intentful: Allocator {
fn current_intent(&self) -> MkIntent;
fn with_intent<F, R>(&self, intent: MkIntent, f: F) -> R
where
F: FnOnce() -> R;
}
/// Cross-context transfer support
pub trait Transferable {
type Handle;
fn prepare_transfer(&self) -> Self::Handle;
fn complete_transfer(handle: Self::Handle) -> Self;
}
```
### 11.2 GPU Traits (memkit-gpu)
```rust
/// GPU memory allocator
pub trait GpuAllocator {
type Buffer;
type Error;
fn allocate_device(&self, size: usize) -> Result<Self::Buffer, Self::Error>;
fn allocate_staging(&self, size: usize) -> Result<Self::Buffer, Self::Error>;
fn deallocate(&self, buffer: Self::Buffer);
}
/// Mappable GPU memory
pub trait Mappable {
fn map(&self) -> *mut u8;
fn unmap(&self);
fn is_mapped(&self) -> bool;
}
/// GPU memory transfer
pub trait GpuTransfer {
type Fence;
fn transfer(&self, src: &impl Mappable, dst: &impl GpuAllocator::Buffer) -> Self::Fence;
fn wait(&self, fence: Self::Fence);
}
```
### 11.3 Trait Hierarchy
```
Allocator (base)
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
Resettable Tagged Intentful
│ │ │
└──────────────┼──────────────┘
│
▼
MkAllocator
(implements all)
```
---
## 12. Module Structure
### 12.1 Complete memkit Module Tree
```
memkit/
├── lib.rs
│ ├── pub mod allocator
│ ├── pub mod container
│ ├── pub mod lifecycle
│ ├── pub mod policy
│ ├── pub mod tag
│ ├── pub mod transfer
│ ├── pub mod sync
│ ├── pub mod stats
│ ├── pub mod traits
│ ├── pub mod config
│ └── pub mod error
│
├── allocator/
│ ├── mod.rs # pub use frame::MkFrameAllocator, etc.
│ ├── frame.rs # Bump allocator, reset per frame
│ ├── pool.rs # Slab allocator, fixed-size blocks
│ ├── heap.rs # General-purpose, variable-size
│ ├── deferred.rs # Delayed deallocation queue
│ └── handle.rs # Handle-based with relocation
│
├── container/
│ ├── mod.rs # pub use box::*, vec::*, etc.
│ ├── box.rs # MkFrameBox, MkPoolBox, MkHeapBox
│ ├── vec.rs # MkFrameVec
│ ├── map.rs # MkFrameMap
│ ├── slice.rs # MkFrameSlice
│ └── scratch.rs # MkScratchPool
│
├── lifecycle/
│ ├── mod.rs # pub use frame::*, phase::*, etc.
│ ├── frame.rs # begin_frame, end_frame, callbacks
│ ├── phase.rs # MkPhase - named scopes
│ ├── checkpoint.rs # MkCheckpoint - save/restore
│ └── scope.rs # MkScope - RAII guard
│
├── policy/
│ ├── mod.rs # pub use retention::*, budget::*, etc.
│ ├── retention.rs # MkRetention - keep vs free decisions
│ ├── promotion.rs # MkPromotion - tier promotion rules
│ └── budget.rs # MkBudget - memory limits
│
├── tag/
│ ├── mod.rs # pub use intent::*, group::*, etc.
│ ├── intent.rs # MkIntent enum
│ ├── group.rs # MkGroup for bulk ops
│ └── registry.rs # Tag registration
│
├── transfer/
│ ├── mod.rs # pub use thread::*, handle::*
│ ├── thread.rs # Cross-thread transfer logic
│ └── handle.rs # MkTransferHandle
│
├── sync/
│ ├── mod.rs # pub use barrier::*, etc.
│ ├── barrier.rs # MkBarrier
│ ├── deferred.rs # Deferred queue control
│ └── atomic.rs # Lock-free utilities
│
├── stats/
│ ├── mod.rs # pub use counter::*
│ └── counter.rs # MkStats, basic counters
│
├── traits/
│ ├── mod.rs # pub use allocator::*, etc.
│ ├── allocator.rs # Allocator trait
│ ├── resettable.rs # Resettable trait
│ ├── tagged.rs # Tagged trait
│ └── intentful.rs # Intentful trait
│
├── config/
│ ├── mod.rs # pub use builder::*
│ └── builder.rs # MkConfig, MkConfigBuilder
│
└── error/
├── mod.rs # pub use alloc_error::*
└── alloc_error.rs # MkAllocError enum
```
### 12.2 Import Examples
```rust
// Minimal import
use memkit::MkAllocator;
// Full import
use memkit::{
MkAllocator, MkConfig,
container::{MkFrameBox, MkFrameVec},
lifecycle::{MkPhase, MkCheckpoint},
tag::MkIntent,
};
// Trait import
use memkit::traits::{Allocator, Resettable, Tagged};
```
---
## 13. Feature Flags
### 13.1 memkit Features
```toml
[features]
default = []
# Debug features
debug = ["backtrace"] # Enable debug info, backtraces
poisoning = [] # Memory poisoning on free
# Performance features
parking_lot = ["dep:parking_lot"] # Use parking_lot for locks
prefetch = [] # Enable prefetch hints
# Nightly features
nightly = [] # Nightly-only optimizations
allocator_api = ["nightly"] # std::alloc::Allocator impl
```
### 13.2 memkit-gpu Features
```toml
[features]
default = []
# Backends
vulkan = ["dep:ash", "dep:gpu-allocator"]
metal = ["dep:metal"] # Future
dx12 = ["dep:windows"] # Future
dummy = [] # No-op backend for testing
# Validation
validation = [] # Enable GPU validation layers
```
### 13.3 memkit-async Features
```toml
[features]
default = ["tokio"]
tokio = ["dep:tokio"]
async-std = ["dep:async-std"] # Future alternative
```
### 13.4 memkit-bevy Features
```toml
[features]
default = []
# Bevy version compatibility
bevy_0_14 = ["dep:bevy@0.14"]
bevy_0_15 = ["dep:bevy@0.15"]
```
---
## 14. Migration Strategy
### 14.1 Phase 1: Parallel Development (Weeks 1-4)
1. Create `memkit` workspace alongside framealloc
2. Implement core `memkit` crate
3. Write comprehensive tests
4. **Do not modify framealloc**
### 14.2 Phase 2: Feature Parity (Weeks 5-8)
1. Implement all memkit-* crates
2. Implement cargo-memlense
3. Create migration guide document
4. Create automated migration tool (optional)
### 14.3 Phase 3: Beta Release (Weeks 9-10)
1. Release memkit 0.12.0-beta.1
2. Announce to existing framealloc users
3. Gather feedback
4. Fix issues
### 14.4 Phase 4: Stable Release (Weeks 11-12)
1. Release memkit 0.12.0
2. Update framealloc to re-export memkit:
```rust
#[deprecated(since = "0.12.0", note = "Use memkit instead")]
pub use memkit::MkAllocator as SmartAlloc;
```
3. Document migration path
### 14.5 Phase 5: Legacy Support (Ongoing)
1. framealloc 0.12.x: Re-exports + deprecation warnings
2. framealloc 0.13.0: Minimal shim, strong deprecation
3. framealloc 1.0.0: Archive, point to memkit
### 14.6 Migration Code Example
```rust
// Before (framealloc)
use framealloc::{SmartAlloc, AllocConfig, FrameBox, AllocationIntent};
let alloc = SmartAlloc::new(AllocConfig::default());
alloc.begin_frame();
let data: FrameBox<[f32; 100]> = alloc.frame_box([0.0; 100]);
alloc.with_intent(AllocationIntent::Physics, || { /* ... */ });
alloc.end_frame();
// After (memkit)
use memkit::{MkAllocator, MkConfig, MkFrameBox, MkIntent};
let alloc = MkAllocator::new(MkConfig::default());
alloc.begin_frame();
let data: MkFrameBox<[f32; 100]> = alloc.frame_box([0.0; 100]);
alloc.with_intent(MkIntent::Physics, || { /* ... */ });
alloc.end_frame();
```
---
## 15. Timeline
### 15.1 Gantt Chart (Text)
```
Week: 1 2 3 4 5 6 7 8 9 10 11 12
├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤
Phase 1: ████████████████
(Core memkit development)
Phase 2: ████████████████████████
(All crates + cargo-memlense)
Phase 3: ████████
(Beta release + feedback)
Phase 4: ████████
(Stable release)
```
### 15.2 Milestone Dates (Relative)
| M1: Core Complete | Week 4 | `memkit` crate compiles, tests pass |
| M2: GPU Complete | Week 6 | `memkit-gpu` with Vulkan backend |
| M3: Async Complete | Week 7 | `memkit-async` with Tokio support |
| M4: Integrations | Week 8 | `memkit-bevy`, `memkit-rapier` |
| M5: Tooling | Week 8 | `cargo-memlense` MVP |
| M6: Beta | Week 10 | 0.12.0-beta.1 published |
| M7: Stable | Week 12 | 0.12.0 published |
### 15.3 Resource Requirements
- **Primary developer**: Full-time on memkit
- **Testing**: Automated CI + manual game integration tests
- **Documentation**: Inline docs + migration guide + examples
- **Benchmarks**: Maintain parity with framealloc performance
---
## 16. Open Questions
### 16.1 Resolved
| Crate name | `memkit` |
| Tooling name | `cargo-memlense` |
| Naming convention | `Mk` prefix for types |
| Crate structure | Hybrid (core + plugins) |
| Diagnostics location | `cargo-memlense` |
| Rapier integration | Separate `memkit-rapier` crate |
### 16.2 Still Open
| GitHub organization | `memkit-rs` org vs personal repo | Affects branding |
| Documentation hosting | docs.rs vs custom | docs.rs is standard |
| Logo/branding | Need design | Optional but nice |
| Benchmark suite | Criterion vs custom | Criterion is standard |
| MSRV (Minimum Rust Version) | 1.70? 1.75? | Affects features |
| WebGPU support | Future `memkit-webgpu` | Not in initial release |
---
## Appendix A: Current framealloc File Mapping
For reference, mapping every current file to its memkit destination:
```
framealloc/src/ → memkit/crates/memkit/src/
├── lib.rs → lib.rs (rewritten)
├── handles.rs → (removed, was re-export)
├── streaming.rs → (removed, merged into allocator/handle.rs)
│
├── allocators/ → allocator/
│ ├── mod.rs → mod.rs
│ ├── frame.rs → frame.rs
│ ├── heap.rs → heap.rs
│ ├── slab.rs → pool.rs (renamed)
│ ├── deferred.rs → deferred.rs
│ ├── handles.rs → handle.rs
│ └── streaming.rs → (merged into handle.rs)
│
├── api/ → (split across modules)
│ ├── mod.rs → (removed)
│ ├── alloc.rs → lib.rs (MkAllocator)
│ ├── allocator_impl.rs → (merged into lib.rs)
│ ├── config.rs → config/builder.rs
│ ├── stats.rs → stats/counter.rs
│ ├── scope.rs → lifecycle/scope.rs
│ ├── phases.rs → lifecycle/phase.rs
│ ├── checkpoint.rs → lifecycle/checkpoint.rs
│ ├── lifecycle.rs → lifecycle/frame.rs
│ ├── wrappers.rs → container/box.rs
│ ├── frame_collections.rs → container/vec.rs, container/map.rs
│ ├── scratch.rs → container/scratch.rs
│ ├── tag.rs → tag/intent.rs
│ ├── tagged.rs → tag/registry.rs
│ ├── groups.rs → tag/group.rs
│ ├── retention.rs → policy/retention.rs
│ ├── promotion.rs → policy/promotion.rs
│ ├── thread_budget.rs → policy/budget.rs
│ ├── transfer.rs → transfer/handle.rs
│ ├── barrier.rs → sync/barrier.rs
│ ├── deferred_control.rs → sync/deferred.rs
│ └── snapshot.rs → cargo-memlense (moved)
│
├── core/ → (merged)
│ ├── mod.rs → (removed)
│ └── budget.rs → policy/budget.rs (merged)
│
├── sync/ → sync/
│ ├── mod.rs → mod.rs
│ └── ... → atomic.rs
│
├── util/ → (internal, not public)
│ └── ... → (kept as internal utils)
│
├── diagnostics/ → cargo-memlense/src/
│ ├── mod.rs → (moved)
│ ├── behavior.rs → analysis/behavior.rs
│ ├── context.rs → diagnostic/context.rs
│ ├── emit.rs → diagnostic/emit.rs
│ ├── hooks.rs → (removed, not needed)
│ ├── kind.rs → diagnostic/code.rs
│ ├── macros.rs → (removed)
│ ├── snapshot.rs → report/snapshot.rs
│ ├── strict.rs → (removed)
│ └── tracy.rs → (optional, separate)
│
├── gpu/ → memkit-gpu/src/
│ ├── mod.rs → lib.rs
│ ├── traits.rs → traits/
│ ├── dummy.rs → backend/dummy.rs
│ └── vulkan/ → backend/vulkan/
│
├── coordinator/ → memkit-gpu/src/coord/
│ └── ... → (merged)
│
├── bevy/ → memkit-bevy/src/
│ └── ... → (restructured)
│
├── tokio/ → memkit-async/src/
│ └── ... → (restructured)
│
├── rapier/ → memkit-rapier/src/
│ └── ... → (restructured)
│
└── debug/ → (feature-gated in memkit)
└── ... → (simplified)
```
---
## Appendix B: Diagnostic Codes
New diagnostic codes for cargo-memlense:
### ML1xx: Frame Lifecycle
| ML101 | Error | `begin_frame` called without `end_frame` |
| ML102 | Error | `end_frame` called without `begin_frame` |
| ML103 | Warning | Frame allocation used after `end_frame` |
| ML104 | Warning | Checkpoint not restored or discarded |
| ML105 | Hint | Consider using phase for this scope |
### ML2xx: Threading
| ML201 | Error | Frame data sent to another thread without transfer |
| ML202 | Error | Transfer handle used after completion |
| ML203 | Warning | Potential data race on shared allocation |
| ML204 | Warning | Cross-thread allocation without explicit policy |
| ML205 | Hint | Consider thread-local allocator |
### ML3xx: GPU Memory
| ML301 | Error | Staging buffer not freed before frame end |
| ML302 | Error | GPU buffer accessed without synchronization |
| ML303 | Error | Device-local memory mapped for CPU access |
| ML304 | Warning | Transfer without barrier |
| ML305 | Warning | Unnecessary staging buffer copy |
### ML4xx: Performance
| ML401 | Warning | Allocation in hot loop |
| ML402 | Warning | Frame allocator reset with live references |
| ML403 | Warning | Pool fragmentation detected |
| ML404 | Hint | Allocation could use frame tier |
| ML405 | Hint | Consider pre-allocating |
### ML5xx: Best Practices
| ML501 | Hint | Missing allocation intent |
| ML502 | Hint | Inconsistent tagging |
| ML503 | Hint | Budget not configured |
| ML504 | Hint | Consider using scratch pool |
| ML505 | Hint | Unused allocation group |
---
## Appendix C: Benchmarks to Maintain
Performance targets (must match or exceed framealloc):
| Frame alloc (1KB) | 15ns | ≤15ns |
| Frame alloc (64KB) | 18ns | ≤18ns |
| Frame reset (10K allocs) | 50ns | ≤50ns |
| Pool alloc/free | 25ns | ≤25ns |
| Heap alloc (1KB) | 100ns | ≤100ns |
| Cross-thread transfer | 200ns | ≤200ns |
| Tag lookup | 5ns | ≤5ns |
| Phase enter/exit | 10ns | ≤10ns |
---
*End of Document*