Skip to content

Commit f255637

Browse files
kmbandyclaude
andcommitted
feat(wp): reactive auto-break on pin-budget under WP_PAGED_BATCH (5b)
Bounds each batch range's pinned working set to 70% of the pool arena so a dense stretch with no routing boundary can't overflow the pins under eviction (alloc_slot -1 / page-in fault). Tracks s_range_pinned_bytes at pin sites, ends the range when it crosses the threshold. Auto-broken ranges return true, so they get an ask=false + release like routing-boundary ranges. Enables WP_PAGED_BATCH for the real partial-residency (evictions>0) target. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ZRfPpL8XFzk1hep9MMg9P
1 parent 2854c4f commit f255637

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

src/weight-pager/wp-eval-cb.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ WeightPager * s_prev_op_pager = nullptr;
136136
// scheduler guarantees runs only after that range's compute+sync.
137137
std::vector<int> s_range_pins;
138138
std::vector<int> s_range_pins_pending;
139+
// Bytes pinned in the current batch range — drives the reactive auto-break so a
140+
// range never tries to pin more than fits in the pool (essential once evictions
141+
// occur; a dense stretch has no routing boundary to bound it otherwise).
142+
size_t s_range_pinned_bytes = 0;
139143

140144
void release_async_op(PendingAsyncOp & op) {
141145
WeightPager * owner = op.pager;
@@ -158,6 +162,7 @@ void release_async_op(PendingAsyncOp & op) {
158162
std::vector<int> s_pinned_pages_prev_op;
159163
std::vector<int> s_range_pins;
160164
std::vector<int> s_range_pins_pending;
165+
size_t s_range_pinned_bytes = 0;
161166
#endif
162167
} // namespace
163168

@@ -172,6 +177,7 @@ void weight_pager_eval_cb_reset(WeightPager * pager) {
172177
s_range_pins.clear();
173178
s_range_pins_pending.clear();
174179
}
180+
s_range_pinned_bytes = 0;
175181
if (pager == nullptr || !pager->async_ensure_enabled()) {
176182
return;
177183
}
@@ -291,11 +297,20 @@ bool weight_pager_eval_cb(struct ggml_tensor * t, bool ask, void * user_data) {
291297
} else {
292298
end_range = true;
293299
}
300+
// 5b reactive auto-break: bound the range's pinned working set so it fits
301+
// the pool. Without this, a dense stretch (no routing boundary) grows until
302+
// a pin can't be satisfied under eviction -> alloc_slot -1 -> page-in fault.
303+
// Break at 70% of the arena, leaving headroom for the next range's pins.
304+
if (paged_batch && !end_range &&
305+
s_range_pinned_bytes >= (pager->pool_arena_bytes() / 10) * 7) {
306+
end_range = true;
307+
}
294308
if (paged_batch && end_range) {
295309
// Range ends after this op: hand its accumulated pins to the pending
296310
// set, released at the top of the next callback (post-sync).
297311
for (int p : s_range_pins) { s_range_pins_pending.push_back(p); }
298312
s_range_pins.clear();
313+
s_range_pinned_bytes = 0;
299314
}
300315
return end_range;
301316
};
@@ -648,6 +663,7 @@ bool weight_pager_eval_cb(struct ggml_tensor * t, bool ask, void * user_data) {
648663
// it. Unpinned in the NEXT eval_cb (above).
649664
pager->pin_page(sub_page_idx);
650665
(paged_batch ? s_range_pins : s_pinned_pages_prev_op).push_back(sub_page_idx);
666+
if (paged_batch) { s_range_pinned_bytes += pager->page_meta(sub_page_idx).size; }
651667
#if defined(GGML_USE_HIP)
652668
s_prev_op_pager = pager;
653669
#endif
@@ -1051,6 +1067,7 @@ bool weight_pager_eval_cb(struct ggml_tensor * t, bool ask, void * user_data) {
10511067
// eval_cb invocation.
10521068
pager->pin_page(page_idx);
10531069
(paged_batch ? s_range_pins : s_pinned_pages_prev_op).push_back(page_idx);
1070+
if (paged_batch) { s_range_pinned_bytes += pager->page_meta(page_idx).size; }
10541071
#if defined(GGML_USE_HIP)
10551072
s_prev_op_pager = pager;
10561073
#endif

src/weight-pager/wp-pager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ class WeightPager {
155155
bool is_routing_break(const struct ggml_tensor * t) const {
156156
return routing_break_tensors_.count(t) != 0;
157157
}
158+
// VRAM arena size in bytes — used by the WP_PAGED_BATCH reactive auto-break
159+
// to bound a batch range's pinned working set below what fits in the pool.
160+
size_t pool_arena_bytes() const { return pool_.pool_size(); }
158161
uint64_t sync_fallback_count() const { return stats_.sync_fallbacks; }
159162
int loaded_pages() const;
160163
int pending_prefetches() const { return prefetch_.pending(); }

0 commit comments

Comments
 (0)