code wiki / (root) / nx_tensor_placement.nx

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}