Arena Allocation — Asignador por Bloques
Archivo: src/arena.rs (291 líneas)
¿Por qué existe?
Reemplaza los miles de Box::new(), Vec::push() y String::clone() individuales del compilador por un asignador que reserva memoria en chunks grandes y la libera toda de golpe al final. El impacto se siente principalmente en la velocidad de compilación (lexer, parser, optimizer), no en el runtime del código generado.
Arquitectura
La arena mantiene un vector de Chunks de memoria cruda. Cada chunk tiene un bump offset (Cell<usize>) que avanza por alineación con cada asignación. Al asignar, se busca el primer chunk con espacio (has_space) y, si ninguno lo tiene, se crea uno nuevo de tamaño default_chunk_size.max(size + align).
| Componente | Descripción |
|---|---|
Chunk | Bloque de memoria cruda con bump allocation: ptr, size, offset; libera en Drop |
Arena | Allocator principal: alloc_raw, alloc, alloc_slice, reset y métricas (bytes_allocated, chunk_count, memory_reserved) |
ScopedArena | Arena con push_scope/pop_scope que permite liberar memoria por nivel |
API Pública
| Función | Descripción |
|---|---|
new() / with_chunk_size(n) | Crea la arena con chunk por defecto de 64 KB o con tamaño personalizado |
alloc<T>(value) -> &mut T | Asigna un valor y retorna referencia mutable (patrón secuencial de compiladores) |
alloc_raw<T>(value) -> *mut T | Asigna y retorna raw pointer; el caller debe garantizar que la arena viva más que el puntero |
alloc_slice<T: Clone>(&[T]) -> &[T] | Asigna un slice clonando cada elemento en la arena |
reset() | Libera toda la memoria: resetea los offsets de todos los chunks (se reutilizan) y pone total_allocated en 0 |
Cómo se integra
Se usa en las fases de compilación que producen muchas estructuras temporales de vida acotada (AST, símbolos, IR intermedio). Al final de cada pasada, reset() recupera toda la memoria de una sola vez sin costo de free individual. ScopedArena permite, además, marcar niveles: push_scope guarda el total_allocated actual y pop_scope rebobina los chunks a ese punto.
alloc<T> solo funciona correctamente cuando no se mantienen referencias previas al hacer nuevas asignaciones (los chunks pueden reubicarse o crearse nuevos). Usá alloc_raw si necesitás punteros estables.pop_scope invalida todas las referencias asignadas desde el push_scope correspondiente: es el mecanismo de "liberar por nivel" que evita leaks en pasadas anidadas del compilador.