Skip to content

Commit c9a2ce4

Browse files
committed
readme
1 parent c846832 commit c9a2ce4

File tree

3 files changed

+205
-2
lines changed

3 files changed

+205
-2
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,3 @@ Cargo.lock
1212

1313
# MSVC Windows builds of rustc generate these, which store debugging information
1414
*.pdb
15-

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "atomalloc"
33
version = "0.1.0"
44
edition = "2021"
5-
description = "atomalloc is an asynchronous, atomic, and lock-free memory allocator."
5+
description = "atomalloc is an asynchronous, atomic, and lock-free memory allocator written in pure safe Rust"
66
license = "MPL-2.0"
77
readme = "README.md"
88
repository = "https://github.com/ovnanova/atomalloc"

README.md

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# ⚛️ AtomAlloc ⚛️
2+
3+
[![Build](https://github.com/ovnanova/atomalloc/actions/workflows/rust.yml/badge.svg)](https://github.com/ovnanova/atomalloc/actions/workflows/rust.yml) [![Rust](https://img.shields.io/badge/rust-stable-orange.svg)](https://www.rust-lang.org)
4+
5+
An experimental async-first memory allocator exploring atomic & lock-free allocation patterns in Rust.
6+
7+
> **Research Implementation**: This is an experimental allocator focused on exploring async memory management patterns. It is NOT optimized for production use and currently exhibits higher overhead than traditional allocators.
8+
9+
## Overview
10+
11+
AtomAlloc investigates the intersection of async Rust, atomic operations, and memory management by implementing a fully lock-free, task-aware allocator. While performance is not yet competitive with production allocators, it provides insights into async allocation patterns and challenges.
12+
13+
## Design Goals vs Reality
14+
15+
### ✅ Successfully Achieved
16+
- 🛡️ **Zero Unsafe Code**: Fully safe Rust implementation
17+
- 🔓 **Lock-Free Design**: Atomic operations throughout
18+
- 💾 **Task Awareness**: Generations and task-local caching
19+
- 🔄 **Async Interface**: Full async/await support
20+
21+
### ❌ Current Limitations
22+
- **Performance**: slower than system allocator for common cases
23+
- **Memory Overhead**: higher memory usage due to atomic metadata
24+
- **Cache Efficiency**: poor cache locality from atomic operations
25+
- **Cold Start**: initial allocation overhead from block initialization
26+
- **Compatibility**: incompatible with GlobalAlloc trait or the unstable allocator_api feature
27+
28+
## Usage
29+
30+
If you'd like to experiment with this allocator, download this repo.
31+
32+
Basic usage:
33+
34+
```rust
35+
// Create allocator instance
36+
let alloc = AtomAlloc::new().await;
37+
38+
// Allocation
39+
let layout = Layout::new::<[u8; 1024]>();
40+
let block = alloc.allocate(layout).await?;
41+
42+
// Write data
43+
block.write(0, &[1, 2, 3, 4]).await?;
44+
45+
// Read data
46+
let data = block.read(0, 4).await?;
47+
48+
// Deallocation
49+
alloc.deallocate(block).await;
50+
51+
// Get allocation stats
52+
let stats = alloc.stats().await;
53+
println!("Cache hit rate: {}%",
54+
stats.cache_hits as f64 / (stats.cache_hits + stats.cache_misses) as f64 * 100.0);
55+
```
56+
57+
## Configuration
58+
59+
The allocator can be configured via `AtomAllocConfig`:
60+
61+
```rust
62+
let config = AtomAllocConfig {
63+
max_memory: 1024 * 1024 * 1024, // 1GB
64+
max_block_size: 64 * 1024, // 64KB
65+
min_block_size: 64, // 64B
66+
alignment: 16,
67+
cache_ttl: Duration::from_secs(300),
68+
max_caches: 1000,
69+
initial_pool_size: 1024 * 1024, // 1MB
70+
zero_on_dealloc: true,
71+
};
72+
73+
let alloc = AtomAlloc::with_config(config).await;
74+
```
75+
76+
## Technical Architecture
77+
78+
### Core Components
79+
80+
```rust
81+
pub struct AtomAlloc {
82+
pool: Arc<MemoryPool>,
83+
cache: Arc<BlockCache>,
84+
block_manager: Arc<BlockManager>,
85+
stats: Arc<AtomAllocStats>,
86+
config: Arc<AtomAllocConfig>,
87+
}
88+
89+
pub struct Block {
90+
state: AtomicU64, // Packs generation + flags
91+
size: AtomicUsize, // Block size
92+
data: Box<[AtomicU8]>, // Actual memory storage
93+
}
94+
```
95+
96+
### Memory Model
97+
98+
Allocation follows a three-tier hierarchy:
99+
1. Block Cache with Hot/Cold Queues
100+
2. Size Class Pool with Power-of-2 Classes
101+
3. Global Memory Pool
102+
103+
Each level uses atomic operations and lock-free data structures for synchronization.
104+
105+
### Generation Safety
106+
107+
```rust
108+
impl BlockManager {
109+
pub async fn verify_generation(&self, block: &Pin<Arc<Block>>) -> Result<(), AtomAllocError> {
110+
let block_gen = block.generation();
111+
let current_gen = self.current_generation.load(Ordering::Acquire);
112+
113+
if block_gen > current_gen {
114+
return Err(AtomAllocError::BlockError(BlockError::InvalidGeneration {
115+
block: block_gen,
116+
expected: current_gen,
117+
}));
118+
}
119+
Ok(())
120+
}
121+
}
122+
```
123+
124+
Memory safety is enforced through a generation system that tracks block validity.
125+
126+
## Critical Implementation Challenges
127+
128+
### 1. Cache Efficiency
129+
130+
The block cache implements a hot/cold queue system to balance reuse and memory pressure:
131+
132+
```rust
133+
pub struct BlockCache {
134+
manager: Arc<BlockManager>,
135+
pool: Arc<MemoryPool>,
136+
size_classes: Vec<Arc<SizeClass>>,
137+
stats: Arc<AtomAllocStats>,
138+
}
139+
```
140+
141+
### 2. Memory Ordering
142+
143+
Ensuring correct ordering without locks requires careful atomic operations:
144+
145+
```rust
146+
pub struct Block {
147+
pub async fn write(&self, offset: usize, data: &[u8]) -> Result<(), BlockError> {
148+
let size = self.size.load(Ordering::Acquire);
149+
// Atomic writes with proper ordering
150+
self.state.fetch_and(!ZEROED_FLAG, Ordering::Release);
151+
Ok(())
152+
}
153+
}
154+
```
155+
156+
### 3. Zero-on-Free Overhead
157+
158+
Memory zeroing for security has performance implications:
159+
160+
```rust
161+
async fn zero_block(&self, block: &Pin<Arc<Block>>) {
162+
if self.config.zero_on_dealloc {
163+
block.clear().await;
164+
}
165+
}
166+
```
167+
168+
## Further Improvements
169+
170+
Current areas of investigation:
171+
1. Cache-friendly atomic operations
172+
2. Improved size class distribution
173+
3. Memory coalescing techniques
174+
4. Alternative cache hierarchies
175+
5. Reduce generation verification overhead
176+
177+
## Contributing
178+
179+
This is an experiment that I would like to improve further over time.
180+
181+
I welcome contributions that would:
182+
- Explore new async allocation patterns
183+
- Improve performance characteristics
184+
185+
## License
186+
187+
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](LICENSE)
188+
189+
## Why This Exists
190+
191+
This allocator serves as an investigation into several questions:
192+
- Can we build a fully async-first allocator?
193+
- What are the real costs of lock-free memory management?
194+
- How do we handle task isolation efficiently?
195+
- What patterns emerge in async memory usage?
196+
197+
While the current implementation is not performance-competitive, I think it offers some insights on where to go next.
198+
199+
## Final Disclaimer
200+
201+
This was built as an exploration of the boundaries of async Rust. Don't use this in production unless you enjoy debugging memory allocation patterns more than having a functional application.
202+
203+
---
204+
*Last updated: October 26, 2024*

0 commit comments

Comments
 (0)