nx_perf_pathology.nx source
↩ module page · 219 lines · 9601 B
1// nx_perf_pathology.nx -- B1 first stone of NISHI_PERFORMANCE_
2// DOCTRINE_ROADMAP.md.
3//
4// Sealed-enum taxonomy of performance pathologies + universal
5// classifier that takes a measured value, a reference band, and
6// auxiliary signals to classify the measurement into one of the
7// pathology classes. Composes ETG (E1 attestation, E2 tournament,
8// E2.5 Byzantine) -- a pathology classification becomes an
9// NxEtgEntry's `measurement_value` so downstream tournament +
10// agreement can operate on pathology classes the same way they
11// operate on raw measurement values.
12//
13// Composes:
14// [[NISHI_PERFORMANCE_DOCTRINE_ROADMAP]] B1 (this is the first stone)
15// nx_etg.nx (E1 attestation; classifier output becomes Entry data)
16// [[feedback-hardware-agnostic-is-robustness]] (cross-arch parity
17// required before "shipped")
18// [[feedback-conductor-heterogeneous-compute-no-second-class-
19// resources]] (P10 second-class pathology is the conductor
20// cardinal's failure mode)
21// [[feedback-reclamation-doctrine-captain-moroni]] (P11 paywall
22// pathology)
23// [[feedback-physical-truth-over-published-docs-silicon-empirical-
24// gamification]] (vendor-claim references vs empirical peak)
25// [[feedback-no-strawman-perf-comparisons]] (classifier requires
26// paired reference; never aspirational)
27// [[feedback-no-false-ok-substrate-honesty-audit]] (INCONCLUSIVE
28// verdict when inputs insufficient; never silent)
29
30// nx_safety_envelope:
31// intended_use: "performance-pathology classification:
32// measured value + reference + signals ->
33// NX_PERF_PATHOLOGY_* sealed enum; composes
34// nx_etg.nx attestation"
35// sil_target: SIL2
36// evidence: [kat_taxonomy_complete,
37// kat_optimal_band_classification,
38// kat_pathology_signature_detection]
39// hazard_register: [bug-tape-pathology-aliased-bits,
40// bug-tape-band-boundary-misclassification,
41// bug-tape-signal-missing-defaults-wrong]
42// verdict: NOT_YET_EVALUATED
43
44import "nx_syscalls.nx"
45
46// ===== Pathology sealed enum ======================================
47//
48// Numbers reserved (no renumbering); future audits replay historical
49// pathology records.
50
51const NX_PERF_PATH_NONE: i64 = 0
52const NX_PERF_PATH_OPTIMAL: i64 = 1 // P0 -- at empirical peak
53const NX_PERF_PATH_BOTTLENECK_CPU: i64 = 2 // P1
54const NX_PERF_PATH_BOTTLENECK_MEM_BW: i64 = 3 // P2
55const NX_PERF_PATH_BOTTLENECK_MEM_LAT: i64 = 4 // P3
56const NX_PERF_PATH_BOTTLENECK_IO: i64 = 5 // P4
57const NX_PERF_PATH_BLEEDING_LEAKAGE: i64 = 6 // P5
58const NX_PERF_PATH_TOO_MANY_STEPS: i64 = 7 // P6
59const NX_PERF_PATH_TOO_FEW_STEPS: i64 = 8 // P7
60const NX_PERF_PATH_LATENCY_SPIKE: i64 = 9 // P8 -- tail latency
61const NX_PERF_PATH_THROUGHPUT_CLIFF: i64 = 10 // P9
62const NX_PERF_PATH_SECOND_CLASS_RESOURCE: i64 = 11 // P10 -- CUDA pattern
63const NX_PERF_PATH_VENDOR_PAYWALL_HONORED: i64 = 12 // P11
64const NX_PERF_PATH_SINGLE_ARCH_OVERFIT: i64 = 13 // P12
65const NX_PERF_PATH_INCONCLUSIVE: i64 = 14 // insufficient signals
66const NX_PERF_PATH_N: i64 = 15
67
68func nx_perf_path_is_valid(p: i64) -> i64 {
69 if p < 0 { return 0 }
70 if p >= NX_PERF_PATH_N { return 0 }
71 return 1
72}
73
74func nx_perf_path_name(p: i64) -> *u8 {
75 if p == NX_PERF_PATH_NONE { return "NONE" }
76 if p == NX_PERF_PATH_OPTIMAL { return "OPTIMAL" }
77 if p == NX_PERF_PATH_BOTTLENECK_CPU { return "BOTTLENECK_CPU" }
78 if p == NX_PERF_PATH_BOTTLENECK_MEM_BW { return "BOTTLENECK_MEM_BW" }
79 if p == NX_PERF_PATH_BOTTLENECK_MEM_LAT { return "BOTTLENECK_MEM_LAT" }
80 if p == NX_PERF_PATH_BOTTLENECK_IO { return "BOTTLENECK_IO" }
81 if p == NX_PERF_PATH_BLEEDING_LEAKAGE { return "BLEEDING_LEAKAGE" }
82 if p == NX_PERF_PATH_TOO_MANY_STEPS { return "TOO_MANY_STEPS" }
83 if p == NX_PERF_PATH_TOO_FEW_STEPS { return "TOO_FEW_STEPS" }
84 if p == NX_PERF_PATH_LATENCY_SPIKE { return "LATENCY_SPIKE" }
85 if p == NX_PERF_PATH_THROUGHPUT_CLIFF { return "THROUGHPUT_CLIFF" }
86 if p == NX_PERF_PATH_SECOND_CLASS_RESOURCE { return "SECOND_CLASS_RESOURCE" }
87 if p == NX_PERF_PATH_VENDOR_PAYWALL_HONORED { return "VENDOR_PAYWALL_HONORED" }
88 if p == NX_PERF_PATH_SINGLE_ARCH_OVERFIT { return "SINGLE_ARCH_OVERFIT" }
89 if p == NX_PERF_PATH_INCONCLUSIVE { return "INCONCLUSIVE" }
90 return "UNKNOWN"
91}
92
93// Does this pathology indicate the substrate is leaving silicon
94// or budget on the table? Returns 1 if so, 0 otherwise.
95// OPTIMAL = 0 (peak utilization). All other named pathologies
96// imply waste of some kind. INCONCLUSIVE = 0 (no determination).
97func nx_perf_path_is_waste(p: i64) -> i64 {
98 if p == NX_PERF_PATH_OPTIMAL { return 0 }
99 if p == NX_PERF_PATH_NONE { return 0 }
100 if p == NX_PERF_PATH_INCONCLUSIVE { return 0 }
101 return 1
102}
103
104// ===== Resource signal struct =====================================
105//
106// Per-classification snapshot of resource utilization at measurement
107// time. Used by the classifier to distinguish bottleneck classes.
108// Each field is a unitless percentage in 0..100 (or -1 = "no signal").
109
110struct NxPerfSignals {
111 cpu_pct: i64, // 0..100; -1 if unsignaled
112 mem_bw_pct: i64, // % of empirical RAM bandwidth ceiling
113 mem_lat_pct: i64, // % of RAM latency ceiling (higher = more pointer-chase)
114 io_pct: i64, // disk+network utilization %
115 gpu_pct: i64, // GPU utilization %
116 npu_pct: i64, // NPU utilization %
117 tail_p99_over_p50: i64, // p99/p50 ratio * 100 (200 = 2x; 1000 = 10x)
118 growth_pct_per_min: i64, // resource-footprint growth rate (% per minute)
119 isa_overfit_ratio: i64, // cross-arch perf-ratio * 100 (100 = parity; 1000 = 10x worse)
120 paywall_responsive: i64, // 1 if ETG probe found PAYWALLED_BUT_RESPONSIVE
121 second_class_idle_count: i64, // count of resources < 10% util while one > 90%
122}
123
124// Initialize signals to "no determination" (all -1, counts 0).
125func nx_perf_signals_init(s: *NxPerfSignals) {
126 s.cpu_pct = -1
127 s.mem_bw_pct = -1
128 s.mem_lat_pct = -1
129 s.io_pct = -1
130 s.gpu_pct = -1
131 s.npu_pct = -1
132 s.tail_p99_over_p50 = -1
133 s.growth_pct_per_min = -1
134 s.isa_overfit_ratio = -1
135 s.paywall_responsive = 0
136 s.second_class_idle_count = 0
137}
138
139// ===== Universal classifier =======================================
140//
141// Given a NxPerfSignals snapshot + an "is the measurement within
142// optimal-band" boolean, classify the pathology. Decision tree
143// favors the strongest signal; ties broken by listed order.
144//
145// Pathology priority (highest to lowest) on tie:
146// 1. SINGLE_ARCH_OVERFIT (cross-arch failure trumps all)
147// 2. VENDOR_PAYWALL_HONORED (reclamation cardinal)
148// 3. SECOND_CLASS_RESOURCE (conductor cardinal)
149// 4. BLEEDING_LEAKAGE (growth_pct_per_min > 0)
150// 5. LATENCY_SPIKE (tail_p99_over_p50 >= 1000 = 10x)
151// 6. THROUGHPUT_CLIFF (encoded externally; not in signals)
152// 7. BOTTLENECK_* (CPU / MEM_BW / MEM_LAT / IO)
153// 8. TOO_MANY_STEPS / TOO_FEW_STEPS (encoded via in_optimal_band)
154// 9. OPTIMAL
155//
156// in_optimal_band: caller computes whether the bench's measured
157// value sits in the (lower, upper) empirical-peak band per the ETG
158// reference. 1 = inside; 0 = outside (slower than band) -- the
159// caller is responsible for the band per workload.
160//
161// Returns one of NX_PERF_PATH_*.
162
163func nx_perf_classify(s: *NxPerfSignals, in_optimal_band: i64) -> i64 {
164 // 1. Cross-arch parity check (highest priority -- robustness gate)
165 if s.isa_overfit_ratio > 200 {
166 return NX_PERF_PATH_SINGLE_ARCH_OVERFIT
167 }
168
169 // 2. Paywall honored (reclamation cardinal)
170 if s.paywall_responsive == 1 {
171 return NX_PERF_PATH_VENDOR_PAYWALL_HONORED
172 }
173
174 // 3. Second-class resource (conductor cardinal)
175 if s.second_class_idle_count >= 2 {
176 return NX_PERF_PATH_SECOND_CLASS_RESOURCE
177 }
178
179 // 4. Bleeding leakage
180 if s.growth_pct_per_min > 0 {
181 return NX_PERF_PATH_BLEEDING_LEAKAGE
182 }
183
184 // 5. Latency spike
185 if s.tail_p99_over_p50 >= 1000 {
186 return NX_PERF_PATH_LATENCY_SPIKE
187 }
188
189 // 6. Bottleneck classification (one resource saturated >= 90%
190 // while measurement misses the optimal band)
191 if in_optimal_band == 0 {
192 if s.cpu_pct >= 90 {
193 return NX_PERF_PATH_BOTTLENECK_CPU
194 }
195 if s.mem_bw_pct >= 90 {
196 return NX_PERF_PATH_BOTTLENECK_MEM_BW
197 }
198 if s.mem_lat_pct >= 90 {
199 return NX_PERF_PATH_BOTTLENECK_MEM_LAT
200 }
201 if s.io_pct >= 90 {
202 return NX_PERF_PATH_BOTTLENECK_IO
203 }
204 // Out of optimal band with no resource saturated -- algorithmic
205 // pathology. Distinguish TOO_FEW_STEPS (slow algorithm) from
206 // TOO_MANY_STEPS (slow due to redundant work) requires a
207 // tournament comparison (B3 rung). For B1 the classifier
208 // returns TOO_FEW_STEPS as the safer default; B3 refines.
209 return NX_PERF_PATH_TOO_FEW_STEPS
210 }
211
212 // 7. In optimal band + no waste signals: OPTIMAL
213 if in_optimal_band == 1 {
214 return NX_PERF_PATH_OPTIMAL
215 }
216
217 // 8. Inputs insufficient
218 return NX_PERF_PATH_INCONCLUSIVE
219}