code wiki / _hdl_build / nx_hmac_alloc_ab.nx

nx_hmac_alloc_ab.nx source

↩ module page · 119 lines · 5232 B

1// nx_hmac_alloc_ab.nx -- WITHIN-SUBJECT A/B: does hoisting HMAC's scratch change anything MEASURABLE? 2// 3// ⚠THIS EXISTS BECAUSE MY PREVIOUS ATTEMPT WAS INVALID. I compared `nx_pbkdf2_sha1` (fixed) against 4// `pbkdf2_sha1` (untouched) -- but those bind DIFFERENT HASH CORES (nx_sha1.nx vs sha1.nx), so the 5// comparison changed TWO variables and its 52.7s-vs-3.6s gap is uninterpretable. 6// ★★★★★A CONTROL THAT CHANGES THE MODULE UNDER TEST IS NOT A CONTROL. I applied this correctly to the HKDF 7// triangulation hours earlier (the SHA-256 arm held fixed as an arm that MUST NOT MOVE) and then broke it. 8// 9// DESIGN: ONE process, ONE hash core (nx_sha1.nx), ONE input, TWO loops of identical count: 10// A: hmac_sha1(...) -- allocates 4 buffers per call (original) 11// B: hmac_sha1_into(...) -- allocates ZERO per call (the fix) 12// Everything else is held constant, so any difference is attributable to the single changed variable. 13// ★A WITHIN-SUBJECT DESIGN REMOVES THE CONFOUND THAT A BETWEEN-BINARY DESIGN INVITES. 14// 15// ⚠AND IT REPORTS RSS *DELTAS PER PHASE*, NOT maxresident. maxresident was shown to be useless here: it 16// read 4,800,000k for BOTH 100k and 200k iterations, with pagefault counts differing by ONE -- i.e. it was 17// reporting a FIXED RUNTIME ARENA, not consumption. ★★★★★A NUMBER IDENTICAL ACROSS TWO DIFFERENT WORKLOADS 18// IS MEASURING THE HARNESS, NOT THE WORKLOAD. 19// If the statm reader fails, this probe says UNMEASURED rather than printing a flattering 0. 20// license_tier: ORIGINAL expect_exit: 0 21import "nx_syscalls.nx" 22import "nx_hmac_sha1.nx" 23const K_MAGIC_50000: i64 = 50000 24 25func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 26 27func nn(v: i64) -> i64 { 28 var m: i64 = v 29 if m < 0 { w("-" as *u8); m = 0 - m } 30 let t: *u8 = sys_mmap(32) 31 var k: i64 = 0 32 if m == 0 { t[0] = 48 as u8; k = 1 } 33 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 34 let b: *u8 = sys_mmap(32) 35 var j: i64 = 0 36 while j < k { b[j] = t[k - 1 - j]; j = j + 1 } 37 sys_write(1, b, k) 38 return 0 39} 40 41// resident pages = field 2 of /proc/self/statm. Returns -1 if unreadable -- NEVER 0-on-failure. 42func rss_pages() -> i64 { 43 let lp: *i64 = sys_mmap(16) as *i64 44 lp[0] = 0 45 let b: *u8 = sys_read_file("/proc/self/statm\x00" as *u8, lp) 46 if lp[0] <= 0 { return 0 - 1 } 47 var p: i64 = 0 48 var sp: i64 = 0 - 1 49 while p < lp[0] { if b[p] == (32 as u8) { sp = p; p = lp[0] } else { p = p + 1 } } 50 if sp < 0 { return 0 - 1 } 51 var q: i64 = sp + 1 52 var v: i64 = 0 53 var any: i64 = 0 54 var done: i64 = 0 55 while done == 0 { 56 if q >= lp[0] { done = 1 } 57 else { 58 let c: i64 = b[q] as i64 59 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); any = 1; q = q + 1 } else { done = 1 } } 60 else { done = 1 } 61 } 62 } 63 if any == 0 { return 0 - 1 } 64 return v 65} 66 67func main() -> i64 { 68 let N: i64 = K_MAGIC_50000 69 w("nx_hmac_alloc_ab -- within-subject A/B, one core (nx_sha1.nx), N=" as *u8); nn(N); w(" per arm\n" as *u8) 70 71 let key: *u8 = "password\x00" as *u8 72 let msg: *u8 = sys_mmap(64) 73 var z: i64 = 0 74 while z < 20 { msg[z] = (z + 1) as u8; z = z + 1 } 75 let out: *u8 = sys_mmap(64) 76 77 let r0: i64 = rss_pages() 78 79 // ---- ARM A: allocating wrapper ---- 80 let ta0: i64 = sys_now_ms() 81 var i: i64 = 0 82 while i < N { hmac_sha1(key, 8, msg, 20, out); i = i + 1 } 83 let ta1: i64 = sys_now_ms() 84 let r1: i64 = rss_pages() 85 86 // ---- ARM B: hoisted scratch, zero allocations in the loop ---- 87 let scap: i64 = hmac_sha1_scratch_bytes(20) 88 let scratch: *u8 = sys_mmap(scap + 16) 89 let tb0: i64 = sys_now_ms() 90 i = 0 91 while i < N { hmac_sha1_into(scratch, scap, key, 8, msg, 20, out); i = i + 1 } 92 let tb1: i64 = sys_now_ms() 93 let r2: i64 = rss_pages() 94 95 w("\n arm A (hmac_sha1, allocating) elapsed_ms=" as *u8); nn(ta1 - ta0); w("\n" as *u8) 96 w(" arm B (hmac_sha1_into, hoisted) elapsed_ms=" as *u8); nn(tb1 - tb0); w("\n" as *u8) 97 98 if r0 < 0 { 99 w("\n rss reader UNAVAILABLE -- reporting TIME ONLY, no memory verdict.\n" as *u8) 100 w("nx_hmac_alloc_ab: verdict=UNMEASURED-MEMORY (time-only)\n" as *u8) 101 return 0 102 } 103 w(" rss growth arm A = " as *u8); nn((r1 - r0) * 4); w(" KB\n" as *u8) 104 w(" rss growth arm B = " as *u8); nn((r2 - r1) * 4); w(" KB\n" as *u8) 105 106 // The claim under test is narrow and falsifiable: arm B must not grow the resident set the way arm A 107 // does. It says nothing about correctness -- both arms compute the same MAC by construction. 108 let ga: i64 = (r1 - r0) * 4 109 let gb: i64 = (r2 - r1) * 4 110 w("\n ref=within-subject-AB probe=nx_hmac_alloc_ab\n" as *u8) 111 if gb * 4 < ga { 112 w("nx_hmac_alloc_ab: armA_kb=" as *u8); nn(ga); w(" armB_kb=" as *u8); nn(gb) 113 w(" verdict=GREEN -- hoisting measurably reduces resident growth\n" as *u8) 114 return 0 115 } 116 w("nx_hmac_alloc_ab: armA_kb=" as *u8); nn(ga); w(" armB_kb=" as *u8); nn(gb) 117 w(" verdict=RED -- NO measurable reduction; the allocation removal does not show up here\n" as *u8) 118 return 1 119}