Files
LithosAnanake/docs/working/architecture/03-architecture/heartbeat-system/architecture.md
T

337 lines
9.7 KiB
Markdown

<!-- Moved from docs/03-architecture/heartbeat-system/architecture.md to docs/working/architecture/03-architecture/heartbeat-system/architecture.md on 2026-06-16 (docs reorg Phase 2) -->
# VM Heartbeat Architecture: Centralized Time-Based Tuning
## The Insight
Loop #5 (window tuning) and Loop #3 (heat decay validation) are **both time-driven events** that should be coordinated via a central **vm_tick()** heartbeat rather than scattered through execution paths.
**Future-proof benefit:** This heartbeat can evolve into a background thread for system observability without touching core VM logic.
---
## Architecture: Heartbeat Abstraction
### 1. Core Heartbeat Function
**File:** `include/vm.h` (add to VM struct)
```c
typedef struct {
uint64_t tick_count; /* Total heartbeat ticks since VM init */
uint64_t last_window_tune_tick; /* Last tick we tuned window */
uint64_t last_slope_tune_tick; /* Last tick we validated decay slope */
int heartbeat_enabled; /* 1 = heartbeat active, 0 = disabled */
} HeartbeatState;
struct VM {
// ... existing fields ...
HeartbeatState heartbeat;
};
```
### 2. Heartbeat Dispatcher
**File:** `src/vm.c` (new function)
```c
/**
* Central heartbeat: aggregates all time-driven tuning operations.
* Called periodically (every N executions) to maintain system observability.
*
* Design: Can be called from:
* - Main execution loop (now): synchronous, zero latency impact
* - Background thread (future): async, decoupled from VM execution
*/
void vm_tick(VM *vm)
{
if (!vm || !vm->heartbeat.heartbeat_enabled)
return;
vm->heartbeat.tick_count++;
/* Plugin 1: Window Tuning (Loop #5) */
if (ENABLE_PIPELINING && (vm->heartbeat.tick_count - vm->heartbeat.last_window_tune_tick) >= WINDOW_TUNING_FREQUENCY)
{
vm_tick_window_tuner(vm);
vm->heartbeat.last_window_tune_tick = vm->heartbeat.tick_count;
}
/* Plugin 2: Heat Decay Slope Validation (Loop #3) */
if ((vm->heartbeat.tick_count - vm->heartbeat.last_slope_tune_tick) >= SLOPE_VALIDATION_FREQUENCY)
{
vm_tick_slope_validator(vm);
vm->heartbeat.last_slope_tune_tick = vm->heartbeat.tick_count;
}
/* Plugin 3: System State Monitoring (Future) */
// vm_tick_system_monitor(vm);
/* Plugin 4: Formal Verification State Update (Future) */
// vm_tick_formal_state_sync(vm);
}
```
### 3. Window Tuner Plugin
**File:** `src/physics_pipelining_metrics.c`
```c
/**
* Window tuning plugin for heartbeat.
* Checks if effective_window_size is optimal based on prefetch accuracy.
*/
void vm_tick_window_tuner(VM *vm)
{
if (!vm || !vm->rolling_window.is_warm || !ENABLE_PIPELINING)
return;
RollingWindowOfTruth *window = &vm->rolling_window;
PipelineGlobalMetrics *metrics = &vm->pipeline_metrics;
/* Calculate current prefetch accuracy */
if (metrics->prefetch_attempts == 0)
return; /* Not enough data yet */
double current_accuracy = (double)metrics->prefetch_hits / (double)metrics->prefetch_attempts;
/* Binary chop suggests next window size */
uint32_t suggested_size = loop_5_binary_chop_suggest_window(
metrics,
window->effective_window_size,
ADAPTIVE_MIN_WINDOW_SIZE,
ROLLING_WINDOW_SIZE
);
/* Apply if different */
if (suggested_size != window->effective_window_size)
{
log_message(LOG_INFO,
"HEARTBEAT[window]: %u → %u (accuracy %.2f%%, %lu/%lu prefetch hits)",
window->effective_window_size,
suggested_size,
current_accuracy * 100.0,
metrics->prefetch_hits,
metrics->prefetch_attempts);
window->effective_window_size = suggested_size;
}
/* Record for next iteration */
metrics->last_checked_window_size = window->effective_window_size;
metrics->last_checked_accuracy = current_accuracy;
metrics->window_tuning_checks++;
}
```
### 4. Slope Validator Plugin
**File:** `src/physics_metadata.c`
```c
/**
* Heat decay slope validation plugin for heartbeat.
* Validates that linear decay is actually helping (Loop #3 closure).
*
* Periodically measures:
* - Are words actually decaying correctly?
* - Is cache pollution from stale words decreasing?
* - Should we try a different decay function (exponential, log, etc.)?
*/
void vm_tick_slope_validator(VM *vm)
{
if (!vm)
return;
/* Collect snapshot of current state */
uint64_t hot_word_count = 0;
uint64_t stale_word_count = 0;
uint64_t total_heat = 0;
/* Scan dictionary and categorize words by heat level */
for (DictEntry *e = vm->latest; e != NULL; e = e->link)
{
if (e->execution_heat > HOTWORDS_EXECUTION_HEAT_THRESHOLD)
hot_word_count++;
else if (e->execution_heat > 0 && e->execution_heat < 10)
stale_word_count++;
total_heat += e->execution_heat;
}
double avg_heat = (vm->latest) ? (double)total_heat / DICTIONARY_SIZE : 0.0;
double stale_ratio = (double)stale_word_count / (double)DICTIONARY_SIZE;
log_message(LOG_INFO,
"HEARTBEAT[slope]: decay validation - hot_words=%lu, stale_ratio=%.2f%%, avg_heat=%.1f",
hot_word_count,
stale_ratio * 100.0,
avg_heat);
/* Phase 2: Compare decay effectiveness over time */
/* Could measure:
* - Is stale_ratio decreasing? (decay is working)
* - Is hot_word_count stable? (decay not over-aggressive)
* - Should we try exponential instead of linear?
*/
}
```
---
## Integration into Execution Path
### Option A: Synchronous (Current - Minimal Latency Impact)
**In `execute_colon_word()` or `vm_interpret_word()`:**
```c
/* Every N executions, run heartbeat operations */
if (++vm->execution_count % HEARTBEAT_FREQUENCY == 0)
{
vm_tick(vm);
}
```
**Pros:**
- No threading complexity
- Tight integration with execution
- Works immediately
**Cons:**
- Adds latency to VM (tuning operations happen in hot path)
- Must keep operations lightweight
### Option B: Background Thread (Future - Decoupled)
```c
/* Spawn heartbeat thread at VM init */
void vm_init(VM *vm)
{
// ... existing init ...
#ifdef USE_HEARTBEAT_THREAD
pthread_create(&vm->heartbeat_thread, NULL, heartbeat_loop, (void*)vm);
#endif
}
/* Background thread heartbeat loop */
void *heartbeat_loop(void *arg)
{
VM *vm = (VM *)arg;
while (vm->heartbeat.heartbeat_enabled)
{
sleep(HEARTBEAT_INTERVAL_MS / 1000.0); /* e.g., sleep 100ms */
vm_tick(vm);
}
return NULL;
}
```
**Pros:**
- Decoupled from execution hot path
- Tuning doesn't add latency to user workload
- System observability without user performance impact
**Cons:**
- Threading complexity (locks, atomics needed)
- Requires careful synchronization with VM state
---
## Configuration Knobs
```makefile
# Heartbeat frequencies (in execution ticks)
HEARTBEAT_FREQUENCY=256 # Tick VM every 256 executions
WINDOW_TUNING_FREQUENCY=1000 # Tune window every 1000 ticks
SLOPE_VALIDATION_FREQUENCY=5000 # Validate decay every 5000 ticks
# Heartbeat mode
HEARTBEAT_THREAD_ENABLED=0 # 1 = background thread, 0 = synchronous
HEARTBEAT_THREAD_INTERVAL_MS=100 # If threaded: wake up every 100ms
# Validation thresholds
WINDOW_TUNING_ACCURACY_THRESHOLD=1 # Min % improvement to continue shrinking
SLOPE_VALIDATION_PRINT_INTERVAL=10 # Log every Nth validation
```
---
## Benefits of Heartbeat Architecture
### Now (Phase 1)
1. **Centralized tuning**: All time-driven operations coordinated in one place
2. **Maintainability**: Easy to add/remove tuning plugins
3. **Observability**: Logging shows exactly when/why tuning happens
4. **Testability**: Can mock heartbeat in unit tests
### Future (Phase 2)
1. **Background thread capability**: Move tuning out of hot path
2. **System monitoring**: Add CPU, memory, cache stats collection
3. **Formal verification**: Keep Isabelle model in sync with actual state
4. **Adaptive scheduling**: Adjust tuning frequency based on workload phase
---
## Loop #3 & #5 Integration
### Loop #3 (Heat Decay): Now VALIDATED
```c
void vm_tick_slope_validator(VM *vm)
{
/* Measure actual decay effectiveness:
* - Are stale words actually disappearing?
* - Is cache pollution decreasing?
* - Could exponential decay work better?
*/
}
```
**Questions answered:**
- Does linear decay help? (Measure stale_word_ratio trend)
- Is slope optimal? (Try different DECAY_RATE values in DoE)
- Could we use exponential? (A/B test: DECAY_FUNCTION=LINEAR vs EXPONENTIAL)
### Loop #5 (Window Tuning): Now ARCHITECTED
```c
void vm_tick_window_tuner(VM *vm)
{
/* Measure window effectiveness:
* - Is smaller window more predictable?
* - Did we lose important patterns?
* - Where's the sweet spot?
*/
}
```
**Benefits:**
- Both tuners can cooperate: window size affects prefetch accuracy
- Slope validator can inform window tuning decisions
- Centralized observability of both optimizations
---
## Sketch Summary
**Heartbeat is a dispatch mechanism that:**
1. Centralizes all time-driven VM operations
2. Provides clean plugin architecture for tuning functions
3. Works synchronously now, extensible to threading later
4. Enables Loop #3 validation AND Loop #5 tuning simultaneously
5. Future-proofs system observability
**Code locations:**
- `include/vm.h`: Add HeartbeatState struct + prototype vm_tick()
- `src/vm.c`: vm_tick() dispatcher, integration point
- `src/physics_pipelining_metrics.c`: vm_tick_window_tuner()
- `src/physics_metadata.c`: vm_tick_slope_validator()
**Effort:** ~200 LOC across 3-4 files, but CLEAN architecture
---
**Does this heartbeat abstraction align with your vision?**