12 KiB
12 KiB
StarForth Design of Experiments (DoE) Metrics Schema
Version: 1.0 Date: 2025-11-19 Target: R statistical analysis (ready for tidyverse, ggplot2, lm models)
CSV Format
Each DoE run produces a single CSV row with the following structure:
timestamp,configuration,run_number,<35 metrics from C API>
Metadata Columns (3)
| Column | Type | Description |
|---|---|---|
timestamp |
ISO 8601 | When the run started (YYYY-MM-DDTHH:MM:SS) |
configuration |
string | Build configuration (e.g., "A_BASELINE__HB_ON") |
run_number |
integer | Sequential run number within configuration (1-N) |
Metrics Columns (35)
All metrics are extracted directly from the VM at the end of each test run via metrics_from_vm().
Metrics Breakdown by Category
1. Cache Metrics (8 columns)
| Column | Type | Range | Description |
|---|---|---|---|
total_lookups |
uint32_t | 0+ | Total dictionary lookups during run |
cache_hits |
uint64_t | 0+ | Cache hits (if ENABLE_HOTWORDS_CACHE=1) |
cache_hit_percent |
double | 0-100 | Hit percentage (formula: 100 * hits / total_lookups) |
bucket_hits |
uint64_t | 0+ | Bucket search hits (after cache miss) |
bucket_hit_percent |
double | 0-100 | Bucket hit percentage (formula: 100 * hits / total_lookups) |
cache_hit_latency_ns |
int64_t | 0+ | Average cache hit latency (nanoseconds) |
cache_hit_stddev_ns |
int64_t | 0+ | Cache hit latency standard deviation |
bucket_search_latency_ns |
int64_t | 0+ | Average bucket search latency (ns) |
bucket_search_stddev_ns |
int64_t | 0+ | Bucket search latency standard deviation |
Notes:
- All latencies converted from internal Q48.16 fixed-point to nanoseconds
- If
ENABLE_HOTWORDS_CACHE=0, cache_hits and cache_hit_percent will be 0
2. Pipelining Metrics (3 columns)
| Column | Type | Range | Description |
|---|---|---|---|
context_predictions_total |
uint64_t | 0+ | Total prefetch predictions made |
context_correct |
uint64_t | 0+ | Successful predictions |
context_accuracy_percent |
double | 0-100 | Prediction accuracy (formula: 100 * correct / total) |
Notes:
- Only populated if
ENABLE_PIPELINING=1 - Measures Loop #4 (word transition prediction) effectiveness
- Formula:
context_accuracy_percent = (context_correct / context_predictions_total) * 100
3. Physics & Adaptive Tuning Metrics (8 columns)
| Column | Type | Range | Description |
|---|---|---|---|
rolling_window_width |
uint32_t | 256-4096 | Current effective rolling window size |
decay_slope |
double | 0-1.0 | Exponential decay slope (converted from Q48.16) |
hot_word_count |
uint64_t | 0+ | Words above heat threshold (>HOTWORDS_EXECUTION_HEAT_THRESHOLD) |
stale_word_ratio |
double | 0-1.0 | Ratio of stale words (execution_heat < 10) to total words |
avg_word_heat |
double | 0+ | Average execution_heat across all dictionary entries |
prefetch_accuracy_percent |
double | 0-100 | Speculative prefetch success rate |
prefetch_attempts |
uint64_t | 0+ | Total prefetch attempts made |
prefetch_hits |
uint64_t | 0+ | Successful prefetch hits |
Notes:
rolling_window_width= running window size for pattern recognition (adaptive)decay_slope= Q48.16 → double conversion (slope = q48_value / 65536)hot_word_count+stale_word_ratiomeasure Heartbeat Loop #3 effectiveness (decay)prefetch_*metrics measure Loop #5 (window tuning) effectiveness
4. Window Tuning Metrics (2 columns)
| Column | Type | Range | Description |
|---|---|---|---|
window_tuning_checks |
uint64_t | 0+ | Number of adaptive window size adjustments |
final_effective_window_size |
uint32_t | 256+ | Final window size after all adjustments |
Notes:
- Indicates how many times the adaptive shrinking logic ran
- Shows result of adaptive tuning (window may have shrunk from initial size)
5. Performance Metrics (3 columns)
| Column | Type | Range | Description |
|---|---|---|---|
vm_workload_duration_ns_q48 |
int64_t | 0+ | VM workload execution time (Q48.16 nanoseconds) |
cpu_temp_delta_c_q48 |
int64_t | -100 to +100 | CPU temperature change (Q48.16 degrees Celsius) |
cpu_freq_delta_mhz_q48 |
int64_t | -4000 to +4000 | CPU frequency change (Q48.16 MHz) |
Notes:
- All three values are in Q48.16 fixed-point format
- To convert:
double_value = q48_value / 65536.0 - Temperature/frequency deltas indicate system thermal behavior during test
- Extracted from
/sys/class/thermal/and/sys/devices/system/cpu/cpufreq/
6. Configuration/Tuning Knobs (5 columns)
| Column | Type | Range | Description |
|---|---|---|---|
decay_rate_q16 |
uint32_t | 1+ | Execution heat decay rate (Q16 fixed-point) |
decay_min_interval_ns |
uint32_t | 1+ | Minimum decay interval (nanoseconds) |
rolling_window_size |
uint32_t | 256-8192 | Initial rolling window size (Makefile knob #7) |
adaptive_shrink_rate |
uint32_t | 50-95 | Shrinking percentage (Makefile knob #8) |
heat_cache_demotion_threshold |
uint32_t | 1-100 | Cache demotion threshold |
Notes:
- These are Makefile-controlled tuning knobs
- Captured to enable reproducibility analysis
decay_rate_q16is in Q16 fixed-point (divide by 65536 for decimal)
7. Feature Flags (2 columns)
| Column | Type | Values | Description |
|---|---|---|---|
enable_hotwords_cache |
int | 0, 1 | Whether physics-driven cache enabled |
enable_pipelining |
int | 0, 1 | Whether word transition prediction enabled |
Notes:
0= disabled,1= enabled- Used to identify which optimization was active
- Crucial for factorial DoE analysis (A, B, C, AB combinations)
CSV Example Row
2025-11-19T10:30:45,A_B_C_FULL__HB_ON,1,5420,2341,43.21,234,4.32,125,85,340,120,1200,1050,87.50,2048,0.0015,45,0.12,38.5,87.50,1200,1050,32,512,15000000000,0,-50,1,1000,4096,75,10,1,1
Breakdown:
timestamp: 2025-11-19T10:30:45 (when run started)configuration: A_B_C_FULL__HB_ON (all optimizations + threaded heartbeat)run_number: 1 (first run of this config)total_lookups: 5420 (dictionary lookups)cache_hits: 2341 (43.21% hit rate)bucket_hits: 234 (4.32% after cache miss)- ... (30+ more metrics)
enable_hotwords_cache: 1 (yes)enable_pipelining: 1 (yes)
R Analysis Template
Load Data
library(tidyverse)
doe_data <- read.csv("experiment_results.csv") %>%
mutate(
configuration = as.factor(configuration),
timestamp = as.POSIXct(timestamp),
# Convert Q48.16 values to decimal
vm_workload_duration_ms = vm_workload_duration_ns_q48 / 65536 / 1e6,
cpu_temp_delta_c = cpu_temp_delta_c_q48 / 65536,
cpu_freq_delta_mhz = cpu_freq_delta_mhz_q48 / 65536,
decay_rate = decay_rate_q16 / 65536
)
Exploratory Analysis
# Summarize by configuration
doe_data %>%
group_by(configuration) %>%
summarize(
n_runs = n(),
mean_cache_hit_pct = mean(cache_hit_percent),
sd_cache_hit_pct = sd(cache_hit_percent),
mean_pipeline_accuracy = mean(context_accuracy_percent),
sd_pipeline_accuracy = sd(context_accuracy_percent),
mean_workload_ms = mean(vm_workload_duration_ms),
sd_workload_ms = sd(vm_workload_duration_ms)
)
Factorial Analysis (2^2 DoE)
# Model: metrics ~ enable_hotwords_cache * enable_pipelining
lm_cache <- lm(cache_hit_percent ~ as.factor(enable_hotwords_cache), data = doe_data)
lm_pipeline <- lm(context_accuracy_percent ~ as.factor(enable_pipelining), data = doe_data)
lm_interaction <- lm(vm_workload_duration_ms ~
as.factor(enable_hotwords_cache) *
as.factor(enable_pipelining),
data = doe_data)
summary(lm_interaction) # Show main effects and interaction
Visualization
# Cache performance by configuration
ggplot(doe_data, aes(x = configuration, y = cache_hit_percent, fill = configuration)) +
geom_boxplot() +
facet_wrap(~enable_pipelining, labeller = labeller(enable_pipelining = c("0" = "Pipeline OFF", "1" = "Pipeline ON"))) +
theme_minimal() +
labs(title = "Cache Hit Rate by Configuration",
x = "Configuration",
y = "Cache Hit %")
# Performance comparison
ggplot(doe_data, aes(x = configuration, y = vm_workload_duration_ms, color = as.factor(enable_hotwords_cache))) +
geom_jitter(width = 0.2, alpha = 0.6) +
geom_boxplot(alpha = 0.1) +
theme_minimal() +
labs(title = "Workload Duration by Config",
color = "Cache Enabled")
Statistical Validation
Before Analysis
- Check for missing values:
sum(is.na(doe_data)) - Verify factor balance: Each config should have equal runs
- Check for outliers: Use
boxplot()on continuous metrics - Normality test:
shapiro.test()on residuals for ANOVA
Assumptions
- Independence: Runs are randomized (shell script does this)
- Normality: Metrics approximate normal distribution (CLT helps)
- Homogeneity: Variance similar across groups (Levene's test:
leveneTest())
Power Analysis
- Minimum runs per config: 30 (per DoE design in shell script)
- Confidence level: 95% (standard)
- Effect size detectable: ~5% change in metrics
Troubleshooting
Metrics All Zeros or Constant
- Cache metrics zero:
ENABLE_HOTWORDS_CACHEwas 0 during build - Pipeline metrics zero:
ENABLE_PIPELININGwas 0 during build - All zero: VM didn't run properly (check run logs)
CSV Parsing Issues in R
# If timestamp won't parse:
doe_data$timestamp <- strptime(doe_data$timestamp, "%Y-%m-%dT%H:%M:%S")
# If too many columns:
# Check that CSV header matches metrics_write_csv_row() output
# Run: head -1 experiment_results.csv | tr ',' '\n' | nl
Unexpected Variance
- Check heartbeat enabled/disabled (HB_ON vs HB_OFF in configuration)
- Verify CPU was not throttling (
cpu_freq_delta_mhz_q48should be small) - Check test matrix was properly randomized (
cat test_matrix.txt)
Future Enhancements
12 Math Optimizations (Planned)
As we implement stack batching, dictionary heat-aware lookup, tail-call optimization, etc., each will add new metrics columns:
| Optimization | New Metrics |
|---|---|
| Stack Operation Batching | batch_operations_applied, batch_reduction_percent |
| Heat-Aware Dictionary Lookup | lookup_reorder_events, lookup_cache_line_hits |
| Tail-Call Optimization | tail_calls_detected, frame_overhead_saved_ns |
| Decay Slope Adaptation | per_word_decay_fitted, decay_convergence_iterations |
| Control Flow Prediction | branch_prediction_accuracy_percent |
| Literal Inlining | literals_inlined, bytes_saved_percent |
| Loop Unrolling | loops_unrolled, unroll_factor_applied |
| Arithmetic Strength Reduction | strength_reductions_applied |
| Colon Definition Inlining | definitions_inlined, inline_frame_overhead_saved |
| Memory Access Prediction | prefetch_predictions_made, prefetch_cache_line_hits |
| Execution Heat Clustering | cluster_count, cluster_coherence_score |
| Adaptive Decay Rate | decay_rate_adjustments_made, convergence_rate_ms |
Each new metric will be:
- Added to
DoeMetricsstruct - Populated in
metrics_from_vm()or extraction helper - Included in CSV header and
metrics_write_csv_row() - Documented in this file
Version History
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2025-11-19 | Initial schema: 35 metrics, 2^2 factorial DoE support |
| TBD | TBD | Add metrics for 12 math optimizations |
Generated for: StarshipOS / StarForth Physics-Driven VM Contact: Robert A. James (Captain Bob) License: See ../LICENSE