nx_natbw_mt.nx source
↩ module page · 95 lines · 3235 B
1// nx_natbw_mt.nx -- MULTI-THREAD memory-bandwidth probe (native-vs-WSL2).
2//
3// N threads each stream their OWN 64MB buffer (4-acc, BW_PASSES times) and set a
4// done-flag; main spins until all set (no join syscall -> works identically on
5// Linux clone AND native CreateThread). Aggregate traffic = N*PASSES*64MB.
6// Timing is EXTERNAL (Measure-Command / date). Threads spawn via sys_thread_create,
7// a NAMED shim the native-PE emitter PATCHES to a CreateThread thunk (Linux path =
8// nx_thread_spawn/clone). This is the decisive test: does native MT bandwidth scale
9// past WSL2's ~9.5 GB/s pool plateau toward gcc's ~39 GB/s?
10//
11// license_tier: ORIGINAL expect_exit: 0
12import "nx_syscalls.nx"
13import "nx_thread.nx"
14const BW_MAGIC_1048576: i64 = 1048576
15
16const BW_PER: i64 = 67108864 // 64MB per thread (> L3 slice -> DRAM-bound)
17const BW_PASSES: i64 = 24
18const BW_NT: i64 = 16 // threads (box has 20 logical cores)
19
20// worker ctx -> i64[3] = [buf_ptr, n8, done_addr]. Sets *done = nonzero when finished.
21func bw_worker(ctx: i64) -> i64 {
22 let c: *i64 = ctx as *i64
23 let p: *i64 = c[0] as *i64
24 let n8: i64 = c[1]
25 let done: *i64 = c[2] as *i64
26 var s0: i64 = 0
27 var s1: i64 = 0
28 var s2: i64 = 0
29 var s3: i64 = 0
30 var pass: i64 = 0
31 while pass < BW_PASSES {
32 var j: i64 = 0
33 while j + 3 < n8 {
34 s0 = s0 + p[j]
35 s1 = s1 + p[j + 1]
36 s2 = s2 + p[j + 2]
37 s3 = s3 + p[j + 3]
38 j = j + 4
39 }
40 pass = pass + 1
41 }
42 var r: i64 = s0 + s1 + s2 + s3
43 if r == 0 { r = 1 } // ensure nonzero so the done-spin sees completion
44 done[0] = r
45 return 0
46}
47
48// sys_thread_create(argptr): argptr -> i64[2] = [fn_addr, ctx_addr].
49// Linux: real clone via nx_thread_spawn. Native: emitter overwrites this entry
50// with `jmp -> CreateThread thunk` (the body below is then dead code).
51func sys_thread_create(argptr: i64) -> i64 {
52 let a: *i64 = argptr as *i64
53 return nx_thread_spawn(a[0], a[1], BW_MAGIC_1048576)
54}
55
56func main() -> i64 {
57 let n8: i64 = BW_PER / 8
58 let dones: *i64 = sys_mmap(BW_NT * 8) as *i64
59 var i: i64 = 0
60 while i < BW_NT {
61 let buf: *i64 = sys_mmap(BW_PER) as *i64
62 var f: i64 = 0
63 while f < n8 { buf[f] = f + 1; f = f + 1 }
64 let ctx: *i64 = sys_mmap(24) as *i64
65 ctx[0] = buf as i64
66 ctx[1] = n8
67 ctx[2] = (dones as i64) + i * 8
68 dones[i] = 0
69 let argp: *i64 = sys_mmap(16) as *i64
70 argp[0] = bw_worker as i64
71 argp[1] = ctx as i64
72 sys_thread_create(argp as i64)
73 i = i + 1
74 }
75 // spin until every worker's done-flag is nonzero (x86 aligned load = atomic).
76 var alldone: i64 = 0
77 while alldone == 0 {
78 alldone = 1
79 var k: i64 = 0
80 while k < BW_NT {
81 if dones[k] == 0 { alldone = 0 }
82 k = k + 1
83 }
84 }
85 var agg: i64 = 0
86 var m: i64 = 0
87 while m < BW_NT { agg = agg + dones[m]; m = m + 1 }
88 if agg < 0 { agg = 0 - agg }
89 let obuf: *u8 = sys_mmap(16)
90 obuf[0] = (48 + (agg - (agg / 10) * 10)) as u8
91 obuf[1] = 10 as u8
92 sys_write(1, obuf, 2)
93 sys_exit(0)
94 return 0
95}