nx_tensor_placement.nx source
↩ module page · 288 lines · 10636 B
1// nx_tensor_placement.nx -- multi-tier tensor home with promote/demote lifecycle.
2//
3// Per CARDINAL [[feedback-conductor-heterogeneous-compute-no-second-class-
4// resources]]: REFUSES CUDA's VRAM-funnel model. Tensors are first-class
5// multi-tier objects that live where they make sense; the substrate (not
6// the user) routes them between tiers based on cost.
7//
8// V1 ships the placement record (where + how big + last-touched), tier
9// classification + promotion/demotion gates, and aggregate accounting for
10// the dispatcher's cost model.
11
12import "nx_syscalls.nx"
13import "nx_tier.nx"
14
15// ===== Sealed enum: NxPlacementTier ===============================
16//
17// Ordered fast -> slow. Promotion = move to faster tier (higher index
18// number means colder). Demotion = move to slower tier.
19
20const NX_TP_TIER_VRAM_HOT: nx_int = 0 // GPU memory, active compute
21const NX_TP_TIER_VRAM_WARM: nx_int = 1 // GPU memory, idle
22const NX_TP_TIER_RAM_HOT: nx_int = 2 // CPU RAM, active
23const NX_TP_TIER_RAM_WARM: nx_int = 3 // CPU RAM, idle
24const NX_TP_TIER_NVME_MMAP: nx_int = 4 // mmap'd file (cold but local)
25const NX_TP_TIER_SSD_FILE: nx_int = 5
26const NX_TP_TIER_HDD_FILE: nx_int = 6
27const NX_TP_TIER_NETWORK_PEER: nx_int = 7 // colder than disk
28const NX_TP_TIER_N: nx_int = 8
29
30// ===== Sealed enum: NxPlacementVerdict ============================
31
32const NX_TP_V_OK: nx_int = 0
33const NX_TP_V_PROMOTED: nx_int = 1
34const NX_TP_V_DEMOTED: nx_int = 2
35const NX_TP_V_NO_BUDGET: nx_int = 3 // target tier exceeded budget
36const NX_TP_V_REFUSED_HOTLOCK: nx_int = 4 // tensor pinned to current tier
37const NX_TP_V_INVALID: nx_int = 5
38const NX_TP_V_NULL: nx_int = 6
39const NX_TP_V_N: nx_int = 7
40
41// ===== Struct: NxTensorPlacement ==================================
42
43struct NxTensorPlacement {
44 tensor_id: nx_int,
45 tier: nx_int,
46 bytes: nx_size,
47 last_touched_us: nx_size,
48 access_count: nx_int,
49 pinned: nx_int, // 1 = refuse demotion (e.g., active kernel)
50 home_address: nx_size, // opaque tier-specific handle
51}
52
53const NX_TP_P_BYTES: nx_int = 56
54
55struct NxPlacementRegistry {
56 placements: *u8,
57 n_placements: nx_int,
58 capacity: nx_int,
59 // Per-tier byte budgets. Index by tier.
60 vram_hot_budget: nx_size,
61 vram_warm_budget: nx_size,
62 ram_hot_budget: nx_size,
63 ram_warm_budget: nx_size,
64 nvme_budget: nx_size,
65}
66
67const NX_TP_BYTES: nx_int = 64
68
69// ===== Validators =================================================
70
71func nx_tp_tier_is_valid(t: nx_int) -> nx_int {
72 if t < 0 { return 0 }
73 if t >= NX_TP_TIER_N { return 0 }
74 return 1
75}
76
77func nx_tp_v_is_valid(v: nx_int) -> nx_int {
78 if v < 0 { return 0 }
79 if v >= NX_TP_V_N { return 0 }
80 return 1
81}
82
83func nx_tp_tier_is_gpu(t: nx_int) -> nx_int {
84 if t == NX_TP_TIER_VRAM_HOT { return 1 }
85 if t == NX_TP_TIER_VRAM_WARM { return 1 }
86 return 0
87}
88
89func nx_tp_tier_is_cpu_memory(t: nx_int) -> nx_int {
90 if t == NX_TP_TIER_RAM_HOT { return 1 }
91 if t == NX_TP_TIER_RAM_WARM { return 1 }
92 return 0
93}
94
95func nx_tp_tier_is_cold(t: nx_int) -> nx_int {
96 if t == NX_TP_TIER_NVME_MMAP { return 1 }
97 if t == NX_TP_TIER_SSD_FILE { return 1 }
98 if t == NX_TP_TIER_HDD_FILE { return 1 }
99 if t == NX_TP_TIER_NETWORK_PEER { return 1 }
100 return 0
101}
102
103// ===== Constructor ================================================
104
105func nx_tp_new(capacity: nx_int,
106 vram_hot_budget: nx_size,
107 vram_warm_budget: nx_size,
108 ram_hot_budget: nx_size,
109 ram_warm_budget: nx_size,
110 nvme_budget: nx_size) -> *NxPlacementRegistry {
111 if capacity <= 0 { return 0 as *NxPlacementRegistry }
112 let raw: *u8 = sys_mmap(NX_TP_BYTES)
113 let r: *NxPlacementRegistry = raw as *NxPlacementRegistry
114 r.placements = sys_mmap(capacity * NX_TP_P_BYTES)
115 r.n_placements = 0
116 r.capacity = capacity
117 r.vram_hot_budget = vram_hot_budget
118 r.vram_warm_budget = vram_warm_budget
119 r.ram_hot_budget = ram_hot_budget
120 r.ram_warm_budget = ram_warm_budget
121 r.nvme_budget = nvme_budget
122 return r
123}
124
125func _tp_at(r: *NxPlacementRegistry, idx: nx_int) -> *NxTensorPlacement {
126 if idx < 0 { return 0 as *NxTensorPlacement }
127 if idx >= r.n_placements { return 0 as *NxTensorPlacement }
128 let off: nx_int = idx * NX_TP_P_BYTES
129 return (r.placements + off) as *NxTensorPlacement
130}
131
132func nx_tp_register(r: *NxPlacementRegistry,
133 tensor_id: nx_int,
134 tier: nx_int,
135 bytes: nx_size,
136 home_address: nx_size,
137 now_us: nx_size) -> nx_int {
138 if (r as i64) == 0 { return NX_TP_V_NULL }
139 if nx_tp_tier_is_valid(tier) == 0 { return NX_TP_V_INVALID }
140 if tensor_id == 0 { return NX_TP_V_INVALID }
141 if r.n_placements >= r.capacity { return NX_TP_V_INVALID }
142 let off: nx_int = r.n_placements * NX_TP_P_BYTES
143 let p: *NxTensorPlacement = (r.placements + off) as *NxTensorPlacement
144 p.tensor_id = tensor_id
145 p.tier = tier
146 p.bytes = bytes
147 p.last_touched_us = now_us
148 p.access_count = 0
149 p.pinned = 0
150 p.home_address = home_address
151 r.n_placements = r.n_placements + 1
152 return NX_TP_V_OK
153}
154
155func nx_tp_find(r: *NxPlacementRegistry, tensor_id: nx_int) -> *NxTensorPlacement {
156 if (r as i64) == 0 { return 0 as *NxTensorPlacement }
157 var i: nx_int = 0
158 while i < r.n_placements {
159 let p: *NxTensorPlacement = _tp_at(r, i)
160 if p.tensor_id == tensor_id { return p }
161 i = i + 1
162 }
163 return 0 as *NxTensorPlacement
164}
165
166// ===== Pinning ====================================================
167
168func nx_tp_pin(r: *NxPlacementRegistry, tensor_id: nx_int) -> nx_int {
169 if (r as i64) == 0 { return NX_TP_V_NULL }
170 let p: *NxTensorPlacement = nx_tp_find(r, tensor_id)
171 if (p as i64) == 0 { return NX_TP_V_INVALID }
172 p.pinned = 1
173 return NX_TP_V_OK
174}
175
176func nx_tp_unpin(r: *NxPlacementRegistry, tensor_id: nx_int) -> nx_int {
177 if (r as i64) == 0 { return NX_TP_V_NULL }
178 let p: *NxTensorPlacement = nx_tp_find(r, tensor_id)
179 if (p as i64) == 0 { return NX_TP_V_INVALID }
180 p.pinned = 0
181 return NX_TP_V_OK
182}
183
184// ===== Tier accounting ============================================
185
186func nx_tp_bytes_in_tier(r: *NxPlacementRegistry, tier: nx_int) -> nx_size {
187 if (r as i64) == 0 { return 0 }
188 var sum: nx_size = 0
189 var i: nx_int = 0
190 while i < r.n_placements {
191 let p: *NxTensorPlacement = _tp_at(r, i)
192 if p.tier == tier { sum = sum + p.bytes }
193 i = i + 1
194 }
195 return sum
196}
197
198func nx_tp_count_in_tier(r: *NxPlacementRegistry, tier: nx_int) -> nx_int {
199 if (r as i64) == 0 { return 0 }
200 var count: nx_int = 0
201 var i: nx_int = 0
202 while i < r.n_placements {
203 let p: *NxTensorPlacement = _tp_at(r, i)
204 if p.tier == tier { count = count + 1 }
205 i = i + 1
206 }
207 return count
208}
209
210func _tp_budget_for_tier(r: *NxPlacementRegistry, tier: nx_int) -> nx_size {
211 if tier == NX_TP_TIER_VRAM_HOT { return r.vram_hot_budget }
212 if tier == NX_TP_TIER_VRAM_WARM { return r.vram_warm_budget }
213 if tier == NX_TP_TIER_RAM_HOT { return r.ram_hot_budget }
214 if tier == NX_TP_TIER_RAM_WARM { return r.ram_warm_budget }
215 if tier == NX_TP_TIER_NVME_MMAP { return r.nvme_budget }
216 // Cold-disk and network tiers treated as unbounded for V1
217 return 0xFFFFFFFFFFFFFFFF
218}
219
220// ===== Touch (track access for promote-from-warm decisions) =======
221
222func nx_tp_touch(r: *NxPlacementRegistry, tensor_id: nx_int, now_us: nx_size) -> nx_int {
223 if (r as i64) == 0 { return NX_TP_V_NULL }
224 let p: *NxTensorPlacement = nx_tp_find(r, tensor_id)
225 if (p as i64) == 0 { return NX_TP_V_INVALID }
226 p.last_touched_us = now_us
227 p.access_count = p.access_count + 1
228 return NX_TP_V_OK
229}
230
231// ===== Promote / Demote ===========================================
232//
233// Promote moves a tensor to a faster tier (lower tier index). Demote
234// moves to a slower tier (higher index). Pinned tensors refuse demotion.
235// Promotion checks that the target tier has remaining budget.
236
237func nx_tp_promote(r: *NxPlacementRegistry,
238 tensor_id: nx_int,
239 new_tier: nx_int,
240 new_home_address: nx_size,
241 now_us: nx_size) -> nx_int {
242 if (r as i64) == 0 { return NX_TP_V_NULL }
243 if nx_tp_tier_is_valid(new_tier) == 0 { return NX_TP_V_INVALID }
244 let p: *NxTensorPlacement = nx_tp_find(r, tensor_id)
245 if (p as i64) == 0 { return NX_TP_V_INVALID }
246 if new_tier >= p.tier { return NX_TP_V_INVALID } // not a promotion
247 let budget: nx_size = _tp_budget_for_tier(r, new_tier)
248 let used: nx_size = nx_tp_bytes_in_tier(r, new_tier)
249 if (used + p.bytes) > budget { return NX_TP_V_NO_BUDGET }
250 p.tier = new_tier
251 p.home_address = new_home_address
252 p.last_touched_us = now_us
253 return NX_TP_V_PROMOTED
254}
255
256func nx_tp_demote(r: *NxPlacementRegistry,
257 tensor_id: nx_int,
258 new_tier: nx_int,
259 new_home_address: nx_size,
260 now_us: nx_size) -> nx_int {
261 if (r as i64) == 0 { return NX_TP_V_NULL }
262 if nx_tp_tier_is_valid(new_tier) == 0 { return NX_TP_V_INVALID }
263 let p: *NxTensorPlacement = nx_tp_find(r, tensor_id)
264 if (p as i64) == 0 { return NX_TP_V_INVALID }
265 if new_tier <= p.tier { return NX_TP_V_INVALID } // not a demotion
266 if p.pinned == 1 { return NX_TP_V_REFUSED_HOTLOCK }
267 p.tier = new_tier
268 p.home_address = new_home_address
269 p.last_touched_us = now_us
270 return NX_TP_V_DEMOTED
271}
272
273// ===== Tier pressure (does any tier exceed its budget?) ===========
274
275func nx_tp_tier_pressure_q10(r: *NxPlacementRegistry, tier: nx_int) -> nx_int {
276 if (r as i64) == 0 { return 0 }
277 let budget: nx_size = _tp_budget_for_tier(r, tier)
278 if budget == 0 { return 0 }
279 let used: nx_size = nx_tp_bytes_in_tier(r, tier)
280 if used == 0 { return 0 }
281 // Q10 of used/budget, clamped at 1024 for visibility (use larger int
282 // intermediate to avoid overflow on big VRAM)
283 let used_q: nx_int = used as nx_int
284 let budget_q: nx_int = budget as nx_int
285 if budget_q <= 0 { return 0 }
286 let ratio: nx_int = (used_q * 1024) / budget_q
287 return ratio
288}