locality.nx source
↩ module page · 105 lines · 3553 B
1// locality.nx -- data-locality markers + cost estimates.
2//
3// EFFICIENCY_ROADMAP ยง1.1. Speed of light = 30 cm/ns. Modern
4// memory / network hierarchy looks like:
5// L1 cache ~1 ns reg-adjacent
6// L2 cache ~5 ns
7// L3 cache ~20 ns
8// DRAM (NUMA-local) ~100 ns
9// DRAM (remote) ~150 ns
10// NVMe SSD ~10,000 ns (10 us)
11// CXL tier-3 mem ~500 ns (emerging)
12// Network LAN ~100,000 ns (100 us)
13// Network WAN ~10,000,000 ns (10 ms)
14// Interplanetary ~minutes+ (light-delay bound)
15//
16// NishiLang today can't reason about ANY of this -- every deref
17// looks equal to the type system. Phase B lifts these constants
18// + markers into the type system so hot loops can be flagged
19// when they cross locality boundaries ('remote deref in hot
20// loop' warning, cost-aware codegen).
21//
22// Phase A: runtime descriptors so library code can attach
23// locality tags to its pointers + query / branch on them.
24//
25// Invariants:
26// LC1 Every LocalityTier constant carries a pessimistic-
27// upper-bound latency in nanoseconds.
28// LC2 Ordering is monotonic: tier N implies latency <= tier N+1.
29// LC3 `locality_worse(a, b)` returns the slower of two tiers
30// -- useful when composing operations.
31
32import "syscalls.nx"
33
34// Locality tiers (ns-upper-bound latency for a single read).
35const LOC_REGISTER: i64 = 1
36const LOC_L1: i64 = 2
37const LOC_L2: i64 = 10
38const LOC_L3: i64 = 30
39const LOC_DRAM_LOCAL: i64 = 100
40const LOC_DRAM_REMOTE: i64 = 200
41const LOC_CXL: i64 = 600
42const LOC_NVME: i64 = 15000
43const LOC_NET_LAN: i64 = 200000
44const LOC_NET_WAN: i64 = 50000000
45const LOC_DISTANT: i64 = 1000000000 // moon/mars/satellite
46
47// Latency ns for a tier. Returns the same constant values as
48// above; exposed as a function so cost calculations stay
49// abstract.
50func locality_latency_ns(tier: i64) -> i64 {
51 return tier
52}
53
54// Pick the slower (pessimistic-bound) of two tiers.
55func locality_worse(a: i64, b: i64) -> i64 {
56 if a > b { return a }
57 return b
58}
59
60// Is a given tier considered "hot-loop safe" (= L3 or faster)?
61func locality_hot_safe(tier: i64) -> i64 {
62 if tier <= LOC_L3 { return 1 }
63 return 0
64}
65
66// === locality-tagged pointer descriptor =============================
67//
68// Attach to any *u8 to track where it lives. Runtime check
69// only; compile-time enforcement is the Phase B upgrade.
70
71struct LocPtr {
72 ptr: *u8,
73 tier: i64,
74 bytes: i64, // accessible region length
75}
76
77func locptr_new(p: *u8, tier: i64, bytes: i64) -> *LocPtr {
78 let raw: *u8 = sys_mmap(32)
79 let lp: *LocPtr = raw as *LocPtr
80 lp.ptr = p
81 lp.tier = tier
82 lp.bytes = bytes
83 return lp
84}
85
86// Compute the cost of accessing `n_reads` through a LocPtr.
87func locptr_access_cost_ns(lp: *LocPtr, n_reads: i64) -> i64 {
88 return locality_latency_ns(lp.tier) * n_reads
89}
90
91// Compile-only smoke.
92func main() -> i64 {
93 // Sanity: DRAM remote is slower than L1.
94 if locality_worse(LOC_L1, LOC_DRAM_REMOTE) != LOC_DRAM_REMOTE {
95 return 1
96 }
97 if locality_hot_safe(LOC_L2) != 1 { return 2 }
98 if locality_hot_safe(LOC_DRAM_REMOTE) != 0 { return 3 }
99
100 let scratch: *u8 = sys_mmap(4096)
101 let lp: *LocPtr = locptr_new(scratch, LOC_L2, 4096)
102 if lp.tier != LOC_L2 { return 4 }
103 if locptr_access_cost_ns(lp, 1000) != 10000 { return 5 }
104 return 0
105}