1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Copyright (c) 2026 Nicholas Vermeulen
// SPDX-License-Identifier: AGPL-3.0-or-later
//! arena.rs — memory pooling for the evaluator (Phase 3.3, activated).
//!
//! This file used to hold a dormant mark/sweep arena sketch. That design
//! was a mismatch for Rusty's memory model and was replaced, not activated:
//! every `Value` is `Rc`-based, so reclamation already happens the moment a
//! refcount hits zero — a tracing collector has nothing to collect — and
//! the sketch's `mark()` had no value→slot mapping, so it could never know
//! which slots were live (see git history for the original).
//!
//! What the evaluator actually pays for is *allocation churn*, and that is
//! what this module pools: every function call and `let` builds an
//! `EnvFrame` around a hash map whose table is allocated and dropped
//! moments later. Dropped frames hand their cleared map (allocation and
//! capacity intact) back here, and frame construction takes one out again —
//! the table allocation is paid once and recycled, not once per call.
//!
//! Thread-local because `Value`/`Env` are `Rc`-based (single-threaded by
//! design). `try_with` everywhere: during thread teardown the pool may
//! already be destroyed while environments are still dropping.
//!
//! Shipped alongside two sibling churn fixes measured in the same pass
//! (docs/ROADMAP.md 3.3): `Expr::List` became `Rc<Vec<Expr>>` (cloning a
//! function body per call was a deep copy with a String allocation per
//! symbol; now it's a refcount bump) and frame maps use FxHash instead of
//! SipHash. Together: ~4× on call/let/actor-heavy workloads.
use crateVarMap;
use RefCell;
thread_local!
/// Keep at most this many maps; beyond it, dropped maps just deallocate.
const FRAME_POOL_CAP: usize = 1024;
/// Take a recycled (empty, capacity-preserving) frame map, or a fresh one.
/// Return a frame's map to the pool. The caller must pass it *already
/// cleared*: clearing drops the contained values, which can recursively
/// drop other `EnvFrame`s that also borrow the pool.
// ── Small-frame vec pool (0.54.0 hybrid frames) ─────────────────────────
// Same discipline as the map pool, for the inline (String, Value) vecs
// that back `Slots::Small`: cleared before recycling, thread-local,
// capped. See env.rs `Slots` for why most frames never touch a map.
type SmallVec = ;
thread_local!
/// Take a recycled (empty, capacity-preserving) small vec, or a fresh one.
/// Return a small vec to the pool. Caller passes it *already cleared* —
/// clearing drops values, which can recursively drop other EnvFrames.