192 lines
9.3 KiB
TeX
192 lines
9.3 KiB
TeX
%% SCRAP: architecture/03-architecture/physics-engine/feedback-loops
|
|
%% SOURCE: docs/working/architecture/03-architecture/physics-engine/feedback-loops.md
|
|
%% STATUS: CURRENT
|
|
%% FITS: dev-guide/ch-physics
|
|
%% EDITORIAL: lifted — prose rewritten to press voice
|
|
|
|
\section{The Seven Feedback Loops}
|
|
|
|
StarForth's adaptive runtime is governed by seven configurable feedback loops.
|
|
Each loop pairs a positive (accumulation) signal that drives the runtime toward
|
|
optimization against a negative (stabilization) signal that prevents runaway
|
|
growth and holds the system at equilibrium. Every loop can be toggled
|
|
independently through a Makefile flag of the form \texttt{ENABLE\_LOOP\_N\_*},
|
|
and the loops are designed to compose cumulatively so that Design of Experiments
|
|
(DoE) campaigns can measure each increment in isolation.
|
|
|
|
The loops borrow thermodynamic language as a modeling convenience. Using
|
|
execution frequency as a proxy for thermal energy, the runtime tracks
|
|
``heat'' per word, lets it ``decay'' over time, and reasons about ``stability''
|
|
in statistical terms. These are descriptive tools, not claims of physical
|
|
thermodynamics.
|
|
|
|
\begin{table}[ht]
|
|
\centering
|
|
\small
|
|
\begin{tabular}{llll}
|
|
\toprule
|
|
Loop & Name & Accumulation signal & Stabilization signal \\
|
|
\midrule
|
|
\#1 & Execution Heat & word execution increments heat & Loop \#3 decay; cold words demoted \\
|
|
\#2 & Rolling Window & every execution recorded to buffer & wrap overwrite; adaptive shrinking \\
|
|
\#3 & Linear Decay & slope starts at $1/3$ & decay reduces heat; slope adjusts $\pm 5\%$ \\
|
|
\#4 & Pipelining & word$\rightarrow$word transitions recorded & counts age out; miss-rate feedback \\
|
|
\#5 & Window Inference & window grows with diversity & Levene's test shrinks to minimum \\
|
|
\#6 & Decay Inference & regression extracts decay rate & ANOVA early-exit; fit-quality bound \\
|
|
\#7 & Adaptive Heartrate & tick counter increments & inference cadence falls when stable \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\caption{The seven feedback loops, with their accumulation and stabilization mechanisms.}
|
|
\end{table}
|
|
|
|
\subsection{Loop \#1 --- Execution Heat Tracking}
|
|
|
|
Loop \#1 counts word usage to identify hot execution paths. Every execution
|
|
increments \texttt{DictEntry.execution\_heat}, and words crossing the promotion
|
|
threshold migrate into the \texttt{hotwords\_cache} for constant-time lookup.
|
|
Stabilization comes from Loop \#3, which decays heat over time, and from cache
|
|
demotion: words falling below \texttt{HEAT\_CACHE\_DEMOTION\_THRESHOLD}~(10)
|
|
are evicted. The \texttt{PHYSICS-RESET-STATS} word clears all heat counters.
|
|
Build flag: \texttt{ENABLE\_LOOP\_1\_HEAT\_TRACKING}.
|
|
|
|
\subsection{Loop \#2 --- Rolling Window History}
|
|
|
|
Loop \#2 captures the execution sequence in a circular buffer
|
|
(\texttt{execution\_history}) to seed metrics deterministically. The window
|
|
grows to \texttt{ROLLING\_WINDOW\_SIZE}~(default 4096) before wrapping and
|
|
becomes ``warm'' after 1024 executions, the point at which it holds
|
|
representative data. Stabilization is twofold: oldest entries are overwritten
|
|
on wrap, and adaptive shrinking reduces \texttt{effective\_window\_size}
|
|
whenever pattern-diversity growth falls below
|
|
\texttt{ADAPTIVE\_GROWTH\_THRESHOLD}~(1\%). The check runs every
|
|
\texttt{ADAPTIVE\_CHECK\_FREQUENCY}~(256) executions, retains
|
|
\texttt{ADAPTIVE\_SHRINK\_RATE}~(75\%) of the window per cycle, and never
|
|
shrinks below \texttt{ADAPTIVE\_MIN\_WINDOW\_SIZE}~(256). Build flag:
|
|
\texttt{ENABLE\_LOOP\_2\_ROLLING\_WINDOW}.
|
|
|
|
\subsection{Loop \#3 --- Linear Decay}
|
|
|
|
Loop \#3 ages words to prevent unbounded heat accumulation; it is primarily a
|
|
negative-feedback loop. Heat decays at \texttt{DECAY\_RATE\_PER\_US\_Q16}
|
|
($1/65536$ heat per microsecond), giving a half-life of roughly six to seven
|
|
seconds for a 100-heat word. The decay slope, stored as a \Qtype{} value
|
|
(\texttt{decay\_slope\_q48}, initialized to $1/3$), self-adjusts by $\pm 5\%$
|
|
against the hot-to-stale word ratio: an excess of hot words increases the slope,
|
|
an excess of stale words decreases it. A minimum interval
|
|
\texttt{DECAY\_MIN\_INTERVAL}~($1\,\mu$s) guards against over-decay. Build flag:
|
|
\texttt{ENABLE\_LOOP\_3\_LINEAR\_DECAY}.
|
|
|
|
\subsection{Loop \#4 --- Pipelining Metrics}
|
|
|
|
Loop \#4 tracks word-to-word transitions to enable speculative prefetch.
|
|
\texttt{WordTransitionMetrics} records transitions per word and builds context
|
|
windows of depth \texttt{TRANSITION\_WINDOW\_SIZE}~(default 2) for prediction,
|
|
while \texttt{prefetch\_attempts} and \texttt{prefetch\_hits} track accuracy.
|
|
Transition counts age out over time, and a miss-rate signal drives a binary-chop
|
|
search over window size through \texttt{PipelineGlobalMetrics.suggested\_next\_size}.
|
|
Build flag: \texttt{ENABLE\_LOOP\_4\_PIPELINING\_METRICS}.
|
|
|
|
\subsection{Loop \#5 --- Window Width Inference}
|
|
|
|
Loop \#5 finds the optimal rolling-window size through statistical testing.
|
|
The \texttt{effective\_window\_size} expands toward \texttt{ROLLING\_WINDOW\_SIZE}
|
|
when pattern diversity rises above threshold. For stabilization it applies
|
|
Levene's test for equality of variance: the heat trajectory is split into $K$
|
|
disjoint chunks, each chunk's variance is computed independently, and the test
|
|
statistic $W$ is compared against the critical value ($W \approx 6.5$ at
|
|
$\alpha = 0.05$). When $W \le$ critical, the variances are stable and the window
|
|
has reached its minimum sufficient size, preventing over-capture of redundant
|
|
pattern data. Build flag: \texttt{ENABLE\_LOOP\_5\_WINDOW\_INFERENCE}.
|
|
|
|
\subsection{Loop \#6 --- Decay Slope Inference}
|
|
|
|
Loop \#6 extracts the optimal decay rate by exponential regression over the heat
|
|
trajectory. Fitting the log-linear model
|
|
|
|
\begin{equation}
|
|
\ln(\mathrm{heat}[t]) = \ln(h_0) - \mathrm{slope} \cdot t
|
|
\end{equation}
|
|
|
|
yields a closed-form \texttt{adaptive\_decay\_slope}. An ANOVA early-exit guards
|
|
the cost: if \texttt{has\_variance\_stabilized()} reports that variance changed
|
|
by less than 5\% since the last inference, the computation is skipped and the
|
|
cached result reused, saving an estimated 5--10k CPU cycles. The slope is
|
|
bounded by fit quality (\texttt{slope\_fit\_quality\_q48}), and
|
|
\texttt{inference\_outputs\_validate()} rejects invalid slopes (zero or greater
|
|
than 100). Build flag: \texttt{ENABLE\_LOOP\_6\_DECAY\_INFERENCE}.
|
|
|
|
\subsection{Loop \#7 --- Adaptive Heartrate}
|
|
|
|
Loop \#7 adjusts tick frequency to balance responsiveness against inference cost.
|
|
\texttt{tick\_count} increments every heartbeat; the heartbeat thread wakes at
|
|
\texttt{HEARTBEAT\_TICK\_NS}~(1\,ms default) and samples hot-word count, total
|
|
heat, and window width. When the system is stable, full inference runs less
|
|
often, gated by \texttt{HEARTBEAT\_INFERENCE\_FREQUENCY}~(5000 ticks): expensive
|
|
inference triggers a slower tick cadence. The background \texttt{HeartbeatWorker}
|
|
thread can be disabled entirely with \texttt{HEARTBEAT\_THREAD\_ENABLED=0}.
|
|
Build flag: \texttt{ENABLE\_LOOP\_7\_ADAPTIVE\_HEARTRATE}.
|
|
|
|
\subsection{Principal Stabilizers}
|
|
|
|
Five mechanisms carry the bulk of the system's negative feedback:
|
|
|
|
\begin{itemize}
|
|
\item \textbf{Heat decay} (Loop \#3) --- the primary stabilizer, preventing
|
|
unbounded heat accumulation.
|
|
\item \textbf{Window shrinking} (Loops \#2 and \#5) --- a diversity plateau
|
|
triggers size reduction via Levene's test.
|
|
\item \textbf{ANOVA early-exit} (Loop \#6) --- variance stability below 5\%
|
|
change skips expensive inference.
|
|
\item \textbf{Cache demotion} (Loop \#1) --- words below the demotion
|
|
threshold leave the hot-words cache.
|
|
\item \textbf{Slope reversal} (Loop \#3) --- an inverted hot-to-stale ratio
|
|
adjusts the decay slope by $\pm 5\%$.
|
|
\end{itemize}
|
|
|
|
\subsection{Configuration}
|
|
|
|
Any loop can be disabled at build time. Setting all seven flags to zero yields
|
|
the baseline configuration used as the DoE reference point.
|
|
|
|
\begin{lstlisting}[language=bash]
|
|
# Disable a specific loop
|
|
make ENABLE_LOOP_3_LINEAR_DECAY=0
|
|
|
|
# Baseline build (all loops off)
|
|
make ENABLE_LOOP_1_HEAT_TRACKING=0 \
|
|
ENABLE_LOOP_2_ROLLING_WINDOW=0 \
|
|
ENABLE_LOOP_3_LINEAR_DECAY=0 \
|
|
ENABLE_LOOP_4_PIPELINING_METRICS=0 \
|
|
ENABLE_LOOP_5_WINDOW_INFERENCE=0 \
|
|
ENABLE_LOOP_6_DECAY_INFERENCE=0 \
|
|
ENABLE_LOOP_7_ADAPTIVE_HEARTRATE=0
|
|
\end{lstlisting}
|
|
|
|
\begin{table}[ht]
|
|
\centering
|
|
\small
|
|
\begin{tabular}{lll}
|
|
\toprule
|
|
Knob & Default & Description \\
|
|
\midrule
|
|
\texttt{ROLLING\_WINDOW\_SIZE} & 4096 & Initial/maximum window size \\
|
|
\texttt{ADAPTIVE\_SHRINK\_RATE} & 75 & \% retained when shrinking \\
|
|
\texttt{ADAPTIVE\_MIN\_WINDOW\_SIZE} & 256 & Floor for window shrinking \\
|
|
\texttt{ADAPTIVE\_CHECK\_FREQUENCY} & 256 & Executions between diversity checks \\
|
|
\texttt{ADAPTIVE\_GROWTH\_THRESHOLD} & 1 & Growth rate (\%) signalling saturation \\
|
|
\texttt{DECAY\_RATE\_PER\_US\_Q16} & 1 & Heat decay per microsecond (\Qtype{}) \\
|
|
\texttt{HEARTBEAT\_INFERENCE\_FREQUENCY} & 5000 & Ticks between full inference runs \\
|
|
\texttt{TRANSITION\_WINDOW\_SIZE} & 2 & Context depth for pipelining prediction \\
|
|
\bottomrule
|
|
\end{tabular}
|
|
\caption{Tuning knobs for the feedback loops.}
|
|
\end{table}
|
|
|
|
The implementation is distributed across
|
|
\texttt{src/rolling\_window\_of\_truth.c} (Loop \#2),
|
|
\texttt{src/dictionary\_heat\_optimization.c} (Loops \#1 and \#3),
|
|
\texttt{src/inference\_engine.c} (unified Loops \#5 and \#6),
|
|
\texttt{src/physics\_pipelining\_metrics.c} (Loop \#4), and the \texttt{vm\_tick()}
|
|
heartbeat dispatcher in \texttt{src/vm.c} (Loop \#7). Knob definitions live in
|
|
\texttt{include/rolling\_window\_knobs.h}.
|