734 lines
20 KiB
Plaintext
734 lines
20 KiB
Plaintext
// Moved from docs/src/internal/PHYSICS_CONTROL_SYSTEM_DESIGN.adoc to docs/working/scratch/src/internal/PHYSICS_CONTROL_SYSTEM_DESIGN.adoc on 2026-06-16 (docs reorg Phase 2)
|
||
= Physics Control System: Observables → Scheduling & Memory Management
|
||
:toc: left
|
||
:toclevels: 2
|
||
xref:../README.adoc[← Back to Documentation Index]
|
||
|
||
== Executive Summary
|
||
|
||
The physics engine provides **observability** (what is the system doing?). Scheduling and memory management provide **control** (what should the system do differently?).
|
||
|
||
This document describes the integration architecture:
|
||
|
||
```
|
||
PHYSICS ENGINE
|
||
(Observability)
|
||
/ | \
|
||
/ | \
|
||
entropy | temp | latency
|
||
/slope | trend | p99
|
||
/ | |
|
||
↓ ↓ ↓
|
||
|
||
┌──────────────┬──────────────┬──────────────┐
|
||
│ SCHEDULING │ FEEDBACK │ MEMORY │
|
||
│ SYSTEM │ CONTROL │ MANAGEMENT │
|
||
│ (Execution) │ LOOPS │ (Placement) │
|
||
└──────────────┴──────────────┴──────────────┘
|
||
│ │
|
||
├─ Word priority ├─ Cache tier
|
||
├─ Core affinity ├─ Block placement
|
||
├─ Preemption hints ├─ GC triggers
|
||
├─ Latency SLO └─ Eviction policy
|
||
└─ Throttling
|
||
```
|
||
|
||
---
|
||
|
||
== Part 1: SCHEDULING SYSTEM
|
||
|
||
=== Current State
|
||
|
||
StarForth runs on top of the OS scheduler (L4Re or Linux):
|
||
|
||
- VM executes on single thread (mostly)
|
||
- Cannot directly control word execution order within VM
|
||
- Can only hint via OS priority (`setpriority`, `sched_setparam`)
|
||
- Return stack drives nesting order (not under our control)
|
||
|
||
=== What Physics Can Control in Scheduling
|
||
|
||
==== A. Direct Controls (OS Level)
|
||
|
||
===== Per-Word Priority
|
||
|
||
[source,c]
|
||
----
|
||
// In physics_metadata.c or new sched_hints.c
|
||
typedef struct {
|
||
int os_priority; // nice level or sched_priority
|
||
int scheduling_class; // SCHED_OTHER, SCHED_FIFO, SCHED_RR
|
||
int cpu_affinity_mask; // which cores allowed
|
||
} sched_hint_t;
|
||
|
||
// Physics engine adjusts based on:
|
||
word->sched_hint.os_priority = (word->temperature_q8 > 0x8000) ? 5 : 10;
|
||
// (hotter = lower nice = higher priority)
|
||
----
|
||
|
||
===== CPU Affinity
|
||
|
||
[source,c]
|
||
----
|
||
// Pin hot words to specific cores
|
||
word->sched_hint.cpu_affinity_mask |= (1 << preferred_core);
|
||
// Keep cache-sensitive words on same core
|
||
// Separate noisy/interfering words to different cores
|
||
----
|
||
|
||
===== Preemption Hints
|
||
|
||
[source,c]
|
||
----
|
||
// For L4Re: hint scheduler whether word is preemptible
|
||
// For Linux: adjust time quantum request
|
||
word->sched_hint.is_preemptible = (word->entropy_slope > 10000) ? 0 : 1;
|
||
// Hot, rapidly-growing words = non-preemptible (batching)
|
||
// Cold, stable words = preemptible (interactive)
|
||
----
|
||
|
||
==== B. Indirect Controls (VM Scheduler Layer)
|
||
|
||
Even without OS control, we can implement a **VM-level scheduler** that respects physics metrics:
|
||
|
||
===== Word Execution Order Within Colon Definitions
|
||
|
||
[source,c]
|
||
----
|
||
// In execute_colon_word(), instead of strict FIFO on return stack:
|
||
// Get next word to execute
|
||
DictEntry *w = (DictEntry *)(uintptr_t)(*ip);
|
||
|
||
// Check: is this word in a hot critical path?
|
||
if (w->physics.temperature_q8 > 0xC000) {
|
||
// Boost execution frequency (call more often)
|
||
// Or: execute on preferred core if available
|
||
// Or: batch with related words
|
||
execute_with_affinity(w, preferred_core);
|
||
} else if (w->physics.temperature_q8 < 0x2000) {
|
||
// Cold word: can defer or batch with others
|
||
// Or: execute during idle time
|
||
defer_or_batch(w);
|
||
}
|
||
----
|
||
|
||
===== Adaptive Batching
|
||
|
||
[source,c]
|
||
----
|
||
// Physics observes: certain words always called together
|
||
// Scheduling can batch them to minimize cache misses
|
||
// Example: IF/THEN should batch together
|
||
|
||
batch_id = learned_batch_group[w->word_id];
|
||
execute_batch(batch_id); // Execute entire group before context switch
|
||
----
|
||
|
||
==== C. Latency SLO Enforcement
|
||
|
||
===== Monitor per-word latency against SLO
|
||
|
||
[source,c]
|
||
----
|
||
// For each word execution:
|
||
uint64_t start = sf_monotonic_ns();
|
||
w->func(vm);
|
||
uint64_t elapsed = sf_monotonic_ns() - start;
|
||
|
||
// Track percentiles
|
||
physics_metadata_record_latency(w, elapsed);
|
||
|
||
// Check SLO
|
||
if (elapsed > w->sched_hint.latency_limit_ns) {
|
||
// Violation! Adjust knobs:
|
||
w->sched_hint.os_priority--; // boost priority
|
||
w->sched_hint.stack_depth_limit--; // prevent deep recursion
|
||
w->sched_hint.batch_timeout_ms--; // flush batches faster
|
||
log_slo_violation(w, elapsed);
|
||
}
|
||
----
|
||
|
||
---
|
||
|
||
== Part 2: MEMORY MANAGEMENT SYSTEM
|
||
|
||
=== Current State
|
||
|
||
StarForth has:
|
||
|
||
- Single 5 MB dictionary heap (static allocation, no fragmentation)
|
||
- Block storage (1024 blocks × 1024 bytes)
|
||
- Two stacks (data + return, 1024 cells each)
|
||
- No explicit memory manager (implicit in dictionary.c)
|
||
|
||
=== What Physics Can Control in Memory
|
||
|
||
==== A. Word Placement (Cache Tiers)
|
||
|
||
===== Memory Hierarchy Abstraction
|
||
|
||
----
|
||
CPU
|
||
/ | \
|
||
/ | \
|
||
L1i/L1d L2 L3
|
||
| | |
|
||
└──────┴───┴────────┐
|
||
Memory │
|
||
│
|
||
Block Storage (Disk)
|
||
----
|
||
|
||
===== Physics-Aware Placement Strategy
|
||
|
||
[source,c]
|
||
----
|
||
typedef enum {
|
||
MEM_TIER_L1, // On-chip cache (fastest, smallest)
|
||
MEM_TIER_L2, // L2 cache
|
||
MEM_TIER_L3, // L3/unified cache
|
||
MEM_TIER_DRAM, // Main memory
|
||
MEM_TIER_BLOCK, // Block storage (persistent)
|
||
} mem_tier_t;
|
||
|
||
// Physics metrics drive placement:
|
||
mem_tier_t choose_tier(DictEntry *word) {
|
||
if (word->physics.temperature_q8 > 0xE000) {
|
||
return MEM_TIER_L1; // Super hot: cache it
|
||
} else if (word->physics.temperature_q8 > 0x8000) {
|
||
return MEM_TIER_L2; // Hot: keep in L2
|
||
} else if (word->physics.temperature_q8 > 0x4000) {
|
||
return MEM_TIER_L3; // Warm: L3 is ok
|
||
} else if (word->physics.entropy_slope > 0) {
|
||
return MEM_TIER_DRAM; // Growing entropy: main memory
|
||
} else {
|
||
return MEM_TIER_BLOCK; // Cold: evict to block storage
|
||
}
|
||
}
|
||
----
|
||
|
||
===== Cache Coloring / Conflict Avoidance
|
||
|
||
[source,c]
|
||
----
|
||
// If two words often execute together (learned from call graph),
|
||
// place them to minimize cache conflicts
|
||
|
||
struct {
|
||
DictEntry *word_a;
|
||
DictEntry *word_b;
|
||
int coexecution_count; // how often do they run together?
|
||
} word_pair_stats[];
|
||
|
||
// Physics detects: word_a + word_b always run together
|
||
// Memory management: place them in different cache sets
|
||
// (L3 cache set assignment based on entropy + call pattern)
|
||
----
|
||
|
||
==== B. Garbage Collection & Eviction
|
||
|
||
===== Trigger GC Based on Physics
|
||
|
||
[source,c]
|
||
----
|
||
// Current: trigger GC when dict_end > threshold
|
||
// Physics-aware: trigger based on memory pressure + workload phase
|
||
|
||
void maybe_trigger_gc(void) {
|
||
// Get host snapshot
|
||
physics_host_snapshot_t snapshot;
|
||
physics_host_snapshot(&snapshot);
|
||
|
||
// Detect memory pressure
|
||
int memory_pressure = snapshot.psi_mem_some_avg10_milli;
|
||
|
||
// Count cold words
|
||
int cold_word_count = 0;
|
||
DictEntry *entry = vm->dictionary;
|
||
while (entry) {
|
||
// Not executed recently AND low temperature
|
||
uint64_t age = sf_monotonic_ns() - entry->physics.last_active_ns;
|
||
if (age > 10_000_000_000 && // 10 seconds old
|
||
entry->physics.temperature_q8 < 0x1000) {
|
||
cold_word_count++;
|
||
}
|
||
entry = entry->link;
|
||
}
|
||
|
||
// Trigger if:
|
||
// 1. Memory pressure high (> 50%)
|
||
// 2. OR dict_end > 80% full
|
||
// 3. AND we have cold words to evict
|
||
if ((memory_pressure > 50 || dict_end > DICT_SIZE * 0.8) &&
|
||
cold_word_count > 0) {
|
||
physics_guided_gc(cold_word_count);
|
||
}
|
||
}
|
||
----
|
||
|
||
===== Eviction Policy: LRU with Temperature Weighting
|
||
|
||
[source,c]
|
||
----
|
||
// Instead of pure LRU, use:
|
||
// eviction_score = (age_in_ticks * inverse_temperature_q8)
|
||
// Higher score = more likely to evict
|
||
|
||
DictEntry *choose_eviction_candidate(void) {
|
||
DictEntry *best = NULL;
|
||
uint64_t best_score = 0;
|
||
|
||
for (DictEntry *e = vm->dictionary; e; e = e->link) {
|
||
uint64_t age = sf_monotonic_ns() - e->physics.last_active_ns;
|
||
uint16_t invtemp = 0xFFFF - e->physics.temperature_q8;
|
||
|
||
uint64_t score = (age / 1_000_000) * (invtemp + 1);
|
||
// age in ms * inverse temperature
|
||
|
||
if (score > best_score) {
|
||
best_score = score;
|
||
best = e;
|
||
}
|
||
}
|
||
|
||
return best;
|
||
}
|
||
----
|
||
|
||
==== C. Block Storage Management
|
||
|
||
===== Which Words Migrate to Disk?
|
||
|
||
[source,c]
|
||
----
|
||
// Physics observes: cold words, low error rate, not in critical path
|
||
// Memory management: spill to block storage
|
||
|
||
void maybe_spill_to_disk(DictEntry *word) {
|
||
// Candidate for spilling if:
|
||
// 1. Cold (temperature < 0x2000)
|
||
// 2. Hasn't run recently (age > 5 min)
|
||
// 3. Not in any SLO-critical call path
|
||
// 4. Memory pressure > 70%
|
||
|
||
if (word->physics.temperature_q8 > 0x2000) return; // Too hot
|
||
|
||
uint64_t age = sf_monotonic_ns() - word->physics.last_active_ns;
|
||
if (age < 300_000_000_000) return; // Too recent (5 min)
|
||
|
||
if (is_in_critical_path(word)) return; // Don't touch critical code
|
||
|
||
physics_host_snapshot_t snap;
|
||
physics_host_snapshot(&snap);
|
||
if (snap.cgroup_memory_current_bytes / snap.memory_total < 0.7) return;
|
||
|
||
// All checks passed: spill to block storage
|
||
spill_word_to_block(word);
|
||
word->flags |= WORD_SPILLED;
|
||
}
|
||
----
|
||
|
||
===== Lazy Loading from Disk
|
||
|
||
[source,c]
|
||
----
|
||
// When word is needed but spilled:
|
||
void execute_potentially_spilled_word(DictEntry *word) {
|
||
if (word->flags & WORD_SPILLED) {
|
||
// Load from block storage
|
||
load_word_from_block(word);
|
||
word->flags &= ~WORD_SPILLED;
|
||
|
||
// Update physics (it was accessed)
|
||
physics_metadata_touch(word, word->entropy, sf_monotonic_ns());
|
||
}
|
||
|
||
// Now execute normally
|
||
if (word->func) {
|
||
word->func(vm);
|
||
}
|
||
}
|
||
----
|
||
|
||
---
|
||
|
||
== Part 3: INTEGRATION POINTS
|
||
|
||
=== Data Flow: Physics → Scheduling → Memory
|
||
|
||
[source]
|
||
----
|
||
PHYSICS ENGINE OBSERVES:
|
||
├─ entropy↑ (word getting hot)
|
||
├─ temperature_q8↑ (thermal state)
|
||
├─ last_active_ns (recent execution)
|
||
├─ latency_ns (execution cost)
|
||
├─ call_graph (who calls whom)
|
||
├─ stack_depth (nesting complexity)
|
||
├─ error_rate (reliability)
|
||
└─ memory_pressure (from host snapshot)
|
||
|
||
│
|
||
↓ (feedback control)
|
||
|
||
SCHEDULING SYSTEM DECIDES:
|
||
├─ execution_priority (OS nice level)
|
||
├─ cpu_affinity (core pinning)
|
||
├─ batch_group (group related words)
|
||
├─ preemption_hint (when to yield)
|
||
├─ latency_slo (per-word guarantee)
|
||
├─ throttle_enable (limit execution)
|
||
└─ stack_limit (prevent overflow)
|
||
|
||
│
|
||
↓ (actual execution)
|
||
|
||
WORD EXECUTION:
|
||
├─ Respects scheduling hints
|
||
├─ Generates new metrics
|
||
├─ Updates physics state
|
||
└─ Triggers memory decisions
|
||
|
||
│
|
||
↓ (feedback to memory)
|
||
|
||
MEMORY MANAGEMENT DECIDES:
|
||
├─ cache_tier (where to place)
|
||
├─ block_placement (cache coloring)
|
||
├─ gc_trigger (when to clean)
|
||
├─ eviction_policy (what to remove)
|
||
├─ spill_to_disk (what to archive)
|
||
└─ prefetch_hint (what to preload)
|
||
|
||
│
|
||
↓ (placement affects execution)
|
||
|
||
NEXT ITERATION:
|
||
(New metrics → new decisions)
|
||
----
|
||
|
||
=== Control Surface: APIs Between Components
|
||
|
||
===== Physics → Scheduling Interface
|
||
|
||
[source,c]
|
||
----
|
||
// In include/sched_control.h
|
||
typedef struct {
|
||
int priority; // OS priority hint
|
||
int cpu_mask; // Affinity mask
|
||
int batch_group; // Batch ID
|
||
int is_preemptible; // True = ok to interrupt
|
||
uint32_t latency_limit_ns; // SLO threshold
|
||
uint32_t stack_limit; // Recursion cap
|
||
} sched_control_t;
|
||
|
||
// Called by physics engine when knobs adjust
|
||
void sched_update_word(DictEntry *word, const sched_control_t *ctl);
|
||
|
||
// Called by word executor to report state
|
||
void sched_report_execution(DictEntry *word, uint64_t latency_ns);
|
||
----
|
||
|
||
===== Physics → Memory Interface
|
||
|
||
[source,c]
|
||
----
|
||
// In include/mem_control.h
|
||
typedef enum {
|
||
MEM_TIER_L1, MEM_TIER_L2, MEM_TIER_L3,
|
||
MEM_TIER_DRAM, MEM_TIER_BLOCK
|
||
} mem_tier_t;
|
||
|
||
typedef struct {
|
||
mem_tier_t preferred_tier; // Cache tier hint
|
||
int cache_set_prefer; // Conflict avoidance
|
||
uint32_t spill_threshold_age; // How old before spill?
|
||
int can_evict; // Ok to GC this word?
|
||
uint32_t eviction_priority; // Lower = evict first
|
||
} mem_control_t;
|
||
|
||
// Called by physics engine when memory knobs adjust
|
||
void mem_update_word_placement(DictEntry *word, const mem_control_t *ctl);
|
||
|
||
// Called by memory manager to report state
|
||
void mem_report_pressure(uint64_t bytes_used, uint64_t memory_limit);
|
||
----
|
||
|
||
===== Scheduling ↔ Memory Coordination
|
||
|
||
[source,c]
|
||
----
|
||
// When scheduler decides to batch words together,
|
||
// memory manager should try to cache-color them
|
||
|
||
void sched_batch_words(DictEntry **words, int count) {
|
||
// Tell memory manager about this batch
|
||
mem_notify_batch_group(words, count);
|
||
|
||
// Memory can now place them strategically
|
||
// (different cache sets, same L3, etc.)
|
||
}
|
||
|
||
// When memory manager evicts a word,
|
||
// tell scheduler about the cost
|
||
|
||
void mem_evict_word(DictEntry *word) {
|
||
if (sched_is_critical(word)) {
|
||
// Don't evict critical-path words without warning
|
||
log_warning("Evicting critical word: %s", word->name);
|
||
sched_disable_fast_path(word);
|
||
}
|
||
|
||
// Actually evict
|
||
spill_to_block(word);
|
||
}
|
||
----
|
||
|
||
---
|
||
|
||
== Part 4: IMPLEMENTATION PHASES
|
||
|
||
=== Phase 1: Instrumentation (Current)
|
||
|
||
- ✅ Physics engine observes metrics
|
||
- ✅ Metrics published to analytics heap
|
||
- ⏳ Scheduling system reads metrics (advisory only)
|
||
- ⏳ Memory manager reads metrics (advisory only)
|
||
|
||
=== Phase 2: Weak Control (Next 2-3 weeks)
|
||
|
||
- ✅ Physics engine observes metrics
|
||
- ⏳ Scheduling system adjusts OS priority hints
|
||
- ⏳ Memory manager implements cache-tier awareness
|
||
- ⏳ No enforcement yet (advisory)
|
||
|
||
=== Phase 3: Strong Control (Weeks 3-6)
|
||
|
||
- ✅ Physics engine drives decisions
|
||
- ⏳ Scheduling system enforces latency SLO
|
||
- ⏳ Memory manager enforces gc/eviction
|
||
- ⏳ Closed-loop feedback working
|
||
|
||
=== Phase 4: ML Integration (Weeks 6+)
|
||
|
||
- ⏳ ML model predicts latency, memory, scheduling
|
||
- ⏳ Governance approves knob adjustments
|
||
- ⏳ Fully adaptive physics-driven system
|
||
|
||
---
|
||
|
||
== Part 5: CONCRETE EXAMPLES
|
||
|
||
=== Example 1: Hot Word Going Critical
|
||
|
||
**Scenario**: I/O word suddenly becomes hot during file operations
|
||
|
||
[source]
|
||
----
|
||
Timeline:
|
||
|
||
T=0s:
|
||
PHYSICS: temperature_q8(BLOCK_READ) = 0x8000 (warm)
|
||
ACTION: Normal operation
|
||
|
||
T=5s:
|
||
PHYSICS: temperature_q8(BLOCK_READ) = 0xC000 (hot)
|
||
entropy_slope(BLOCK_READ) = 1000/sec (rapidly increasing)
|
||
ACTION: Alert scheduling + memory systems
|
||
|
||
T=5.5s:
|
||
SCHEDULING REACTS:
|
||
- Boost priority: os_priority = -5 (above normal)
|
||
- Enable batching: batch_group = IO_BATCH_1
|
||
- Cap recursion: stack_limit = 100 (prevent deep nesting)
|
||
- Set SLO: latency_limit = 10000ns (must meet)
|
||
|
||
MEMORY REACTS:
|
||
- Cache tier: move to MEM_TIER_L2
|
||
- Prefetch: load related words (BLOCK_WRITE, BUFFER, UPDATE)
|
||
- No eviction: mark as protected
|
||
|
||
T=6s:
|
||
EXECUTION:
|
||
- BLOCK_READ runs with high priority
|
||
- Batched with related IO words
|
||
- Monitored against 10μs SLO
|
||
- Cached in L2 for speed
|
||
|
||
T=6.5s:
|
||
MONITORING:
|
||
- Latency: p99 = 8500ns ✅ (within SLO)
|
||
- Temperature: stabilizing at 0xD000
|
||
- Call pattern: periodic (file scan loop)
|
||
|
||
ACTION: Continue monitoring, may fine-tune batch size
|
||
|
||
T=10s:
|
||
PHYSICS: temperature stabilizing, pattern learned
|
||
SCHEDULING: Can now be more aggressive (predict latency)
|
||
MEMORY: Knows this is sustained hot (don't evict)
|
||
----
|
||
|
||
=== Example 2: Cold Word Emergency Eviction
|
||
|
||
**Scenario**: Memory pressure high, need to evict unused words
|
||
|
||
[source]
|
||
----
|
||
Timeline:
|
||
|
||
T=0s:
|
||
MEMORY: dict_end = 1.9 MB (95% full)
|
||
HOST SNAPSHOT: cgroup memory_current = 480MB / 500MB = 96%
|
||
ACTION: Trigger garbage collection
|
||
|
||
T=0.5s:
|
||
PHYSICS: Scan all words for eviction candidates
|
||
- Word X: temperature=0x0200, age=2_000_000_000_000ns (33 min old)
|
||
- Word Y: temperature=0x1000, age=600_000_000_000ns (10 min old)
|
||
- Word Z: temperature=0x8000, age=100_000_000_000ns (very recent hot)
|
||
|
||
SCORES (age_ms * inverse_temp):
|
||
- Word X: (2,000,000) * (0xFF00) = 18.4 billion ✓ EVICT FIRST
|
||
- Word Y: (600,000) * (0xF000) = 5.6 billion
|
||
- Word Z: (100,000) * (0x7FFF) = 737 million (don't touch)
|
||
|
||
T=1s:
|
||
EVICTION DECISION:
|
||
- Evict Word X (coldest, oldest)
|
||
- Word Y is backup if more space needed
|
||
- Word Z is protected (hot + critical path)
|
||
|
||
T=1.5s:
|
||
SPILL TO DISK:
|
||
- Save Word X to block storage
|
||
- Mark as WORD_SPILLED
|
||
- Reclaim ~4KB of dictionary space
|
||
|
||
T=2s:
|
||
MEMORY UPDATE:
|
||
- dict_end = 1.896 MB (94.8% full)
|
||
- Pressure satisfied
|
||
- Resume normal operations
|
||
|
||
T=1_000s (later):
|
||
WORD X NEEDED AGAIN:
|
||
- User calls Word X
|
||
- Executor detects WORD_SPILLED flag
|
||
- Load from block storage (~50μs)
|
||
- Execute normally
|
||
- Physics re-learns its pattern
|
||
----
|
||
|
||
---
|
||
|
||
== Part 6: CRITICAL DESIGN DECISIONS
|
||
|
||
=== Decision 1: Scheduling Granularity
|
||
|
||
**Option A: Per-word priority (fine-grained)**
|
||
- Each word gets own OS priority
|
||
- Problem: OS scheduler might thrash (too many priority levels)
|
||
- Cost: More OS context switches
|
||
|
||
**Option B: Word batches (coarse-grained)**
|
||
- Group words into 5-10 priority classes
|
||
- Batch execution (atomic from OS perspective)
|
||
- Benefit: Fewer context switches, deterministic batching
|
||
- Drawback: Less fine-grained control
|
||
|
||
**Option C: Hybrid (initial fine → batch)**
|
||
- Phase 2: Per-word priority
|
||
- Phase 3: Learn optimal batches, switch to batch-based
|
||
- Best of both worlds if well-tuned
|
||
|
||
**Recommendation**: Hybrid (start fine, optimize to batch)
|
||
|
||
---
|
||
|
||
=== Decision 2: Memory Placement Accuracy
|
||
|
||
**Option A: Static placement**
|
||
- At word creation time, decide tier based on size/type
|
||
- Doesn't adapt to runtime behavior
|
||
- Fast, predictable
|
||
|
||
**Option B: Dynamic migration**
|
||
- Move words between tiers as temperature changes
|
||
- Could cause cache thrashing (moving too often)
|
||
- Complex to implement correctly
|
||
|
||
**Option C: Hints + OS support**
|
||
- Provide hints to OS (madvise, NUMA policies)
|
||
- Let OS handle actual placement
|
||
- Less control but more portable
|
||
|
||
**Option D: Logical tiers (no actual movement)**
|
||
- "Tier" is just a label/hint
|
||
- Affects batching, scheduling, but not memory layout
|
||
- Simpler, still effective
|
||
|
||
**Recommendation**: Option D (logical tiers as scheduling hints)
|
||
|
||
---
|
||
|
||
=== Decision 3: GC vs. Spilling
|
||
|
||
**Option A: Always GC (compact)**
|
||
- Forget cold words completely
|
||
- Reclaim space immediately
|
||
- Problem: User can't redefine forgotten words
|
||
|
||
**Option B: Always spill (archive)**
|
||
- Keep definitions in block storage
|
||
- Can reload on demand
|
||
- Problem: Extra latency when needed
|
||
|
||
**Option C: Policy-based (governance)**
|
||
- Governance decides: GC or spill?
|
||
- Per-word metadata: can_forget? can_spill?
|
||
- Most flexible, most complex
|
||
|
||
**Recommendation**: Option C (governance-driven)
|
||
|
||
---
|
||
|
||
== Part 7: OPEN QUESTIONS
|
||
|
||
1. **Scheduling Model**:
|
||
- Should VM implement its own scheduler? Or rely entirely on OS?
|
||
- How strictly enforce latency SLOs within FORTH?
|
||
|
||
2. **Memory Constraints**:
|
||
- Can we physically move words in dictionary? (would break pointers)
|
||
- Or only logical placement hints?
|
||
|
||
3. **Block Storage Role**:
|
||
- Is block storage for persistence? Or active memory overflow?
|
||
- How quickly should spilled words be reloadable?
|
||
|
||
4. **L4Re Specifics**:
|
||
- L4Re has IPC + capabilities
|
||
- Should scheduling decisions affect IPC batch deadlines?
|
||
- Should memory decisions affect capability delegation?
|
||
|
||
5. **Governance Integration**:
|
||
- Who decides eviction policy? (system? user? governance?)
|
||
- Who approves spilling to disk?
|
||
- Who sets per-word SLOs?
|
||
|
||
6. **Measurement Overhead**:
|
||
- Current physics metrics: ~200-300ns per event
|
||
- Will scheduling+memory control add 100ns more?
|
||
- Is that acceptable for your latency targets?
|
||
|
||
---
|
||
|
||
== References
|
||
|
||
- Physics Implementation Status: xref:./PHYSICS_IMPLEMENTATION_STATUS.adoc[PHYSICS_IMPLEMENTATION_STATUS.adoc]
|
||
- Physics Cone of Influence: xref:./PHYSICS_CONE_OF_INFLUENCE_DESIGN.adoc[PHYSICS_CONE_OF_INFLUENCE_DESIGN.adoc]
|
||
- Platform Time Abstraction: `include/platform_time.h` (reference implementation)
|
||
- Physics Metadata: `src/physics_metadata.c`, `include/physics_metadata.h`
|
||
- Physics Runtime: `src/physics_runtime.c`, `include/physics_runtime.h` |