Skip to content

Commit 2854c4f

Browse files
kmbandyclaude
andcommitted
feat(wp): per-range pin lifecycle under WP_PAGED_BATCH (release after range sync)
Under WP_PAGED_BATCH the eval-cb no longer unpins per-op; pins accumulate in s_range_pins across a batch range, move to s_range_pins_pending when the range ends, and release at the top of the next callback (post-sync). Safe for the resident case (evictions==0). Reactive auto-break on pin exhaustion and tighter split-final-range release are Task 5b (validated under real eviction on the paged gate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ZRfPpL8XFzk1hep9MMg9P
1 parent 0c9a6e1 commit 2854c4f

1 file changed

Lines changed: 47 additions & 12 deletions

File tree

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

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ std::vector<AsyncTransferEvent> s_async_events_prev_op;
129129
std::vector<PendingAsyncOp> s_pending_async_ops;
130130
WeightPager * s_prev_op_pager = nullptr;
131131

132+
// WP_PAGED_BATCH per-range pin lifecycle. Under paged-batch the callback does
133+
// NOT unpin per-op; pins accumulate in s_range_pins across the current batch
134+
// range, move to s_range_pins_pending when the range ends (eval_cb_op_return
135+
// returns true), and are released at the top of the next callback — which the
136+
// scheduler guarantees runs only after that range's compute+sync.
137+
std::vector<int> s_range_pins;
138+
std::vector<int> s_range_pins_pending;
139+
132140
void release_async_op(PendingAsyncOp & op) {
133141
WeightPager * owner = op.pager;
134142
for (int page_idx : op.pages) {
@@ -148,11 +156,22 @@ void release_async_op(PendingAsyncOp & op) {
148156
}
149157
#else
150158
std::vector<int> s_pinned_pages_prev_op;
159+
std::vector<int> s_range_pins;
160+
std::vector<int> s_range_pins_pending;
151161
#endif
152162
} // namespace
153163

154164
void weight_pager_eval_cb_reset(WeightPager * pager) {
155165
#if defined(GGML_USE_HIP)
166+
// WP_PAGED_BATCH: release any range pins still held at teardown (a range that
167+
// ended at a split boundary with no following callback, or reset mid-range).
168+
// Independent of async ensure, so do it before the async early-return.
169+
if (pager != nullptr && (!s_range_pins.empty() || !s_range_pins_pending.empty())) {
170+
for (int p : s_range_pins_pending) { pager->unpin_page(p); }
171+
for (int p : s_range_pins) { pager->unpin_page(p); }
172+
s_range_pins.clear();
173+
s_range_pins_pending.clear();
174+
}
156175
if (pager == nullptr || !pager->async_ensure_enabled()) {
157176
return;
158177
}
@@ -237,32 +256,48 @@ void weight_pager_eval_cb_print_profile() {
237256
bool weight_pager_eval_cb(struct ggml_tensor * t, bool ask, void * user_data) {
238257
// Only act on the pre-execution call. The post-execution call is
239258
// informational and would re-trigger the same lookups.
240-
if (!ask) return true;
241259
if (t == nullptr) return true;
242260
auto * pager = (WeightPager *) user_data;
243261
if (pager == nullptr) return true;
244-
const bool eval_debug = eval_debug_enabled();
245262
const bool batch_eval_cb = wp_batch_eval_cb_enabled();
263+
const bool paged_batch = batch_eval_cb && wp_paged_batch_enabled();
264+
// WP_PAGED_BATCH: release the previous range's pins now. The top of this
265+
// callback is guaranteed to run after that range's compute+sync (the
266+
// scheduler computes+syncs a range before it issues the next ask=true, and
267+
// before the ask=false on the range's last node).
268+
if (paged_batch && !s_range_pins_pending.empty()) {
269+
for (int p : s_range_pins_pending) { pager->unpin_page(p); }
270+
s_range_pins_pending.clear();
271+
}
272+
if (!ask) return true;
273+
const bool eval_debug = eval_debug_enabled();
246274
const uint64_t sync_fallbacks_before =
247275
batch_eval_cb ? pager->sync_fallback_count() : 0;
248276
bool routing_tls_set = false;
249-
const bool paged_batch = batch_eval_cb && wp_paged_batch_enabled();
250277
auto eval_cb_op_return = [&]() -> bool {
278+
bool end_range;
251279
// WP_PAGED_BATCH: break the batch range at every routing boundary
252280
// (each MUL_MAT_ID and its ids-producer). Ending the range here forces
253281
// the scheduler's compute+sync, so the router's ids are materialized
254282
// before the next range reads them, and the routing op runs isolated
255283
// (fixes the read-before-produce H3 + TLS take-steal H4 faults).
256284
if (paged_batch && pager->is_routing_break(t)) {
257-
return true;
285+
end_range = true;
286+
} else if (batch_eval_cb &&
287+
pager->batch_safe() &&
288+
!routing_tls_set &&
289+
pager->sync_fallback_count() == sync_fallbacks_before) {
290+
end_range = false;
291+
} else {
292+
end_range = true;
258293
}
259-
if (batch_eval_cb &&
260-
pager->batch_safe() &&
261-
!routing_tls_set &&
262-
pager->sync_fallback_count() == sync_fallbacks_before) {
263-
return false;
294+
if (paged_batch && end_range) {
295+
// Range ends after this op: hand its accumulated pins to the pending
296+
// set, released at the top of the next callback (post-sync).
297+
for (int p : s_range_pins) { s_range_pins_pending.push_back(p); }
298+
s_range_pins.clear();
264299
}
265-
return true;
300+
return end_range;
266301
};
267302

268303
// WP_PROFILE_EVAL: time the whole callback body (every return path past
@@ -612,7 +647,7 @@ bool weight_pager_eval_cb(struct ggml_tensor * t, bool ask, void * user_data) {
612647
// alloc_slot in this same eval_cb can't evict
613648
// it. Unpinned in the NEXT eval_cb (above).
614649
pager->pin_page(sub_page_idx);
615-
s_pinned_pages_prev_op.push_back(sub_page_idx);
650+
(paged_batch ? s_range_pins : s_pinned_pages_prev_op).push_back(sub_page_idx);
616651
#if defined(GGML_USE_HIP)
617652
s_prev_op_pager = pager;
618653
#endif
@@ -1015,7 +1050,7 @@ bool weight_pager_eval_cb(struct ggml_tensor * t, bool ask, void * user_data) {
10151050
// GPU is still reading from it. Unpinned at the top of the NEXT
10161051
// eval_cb invocation.
10171052
pager->pin_page(page_idx);
1018-
s_pinned_pages_prev_op.push_back(page_idx);
1053+
(paged_batch ? s_range_pins : s_pinned_pages_prev_op).push_back(page_idx);
10191054
#if defined(GGML_USE_HIP)
10201055
s_prev_op_pager = pager;
10211056
#endif

0 commit comments

Comments
 (0)