@@ -86,6 +86,7 @@ bool Qwen35LayerSplitAdapter::init() {
8686 for (auto & slot : prefix_snapshots_) {
8787 slot.resize (shards_.size ());
8888 }
89+ draft_feature_snapshots_.resize (PREFIX_SLOTS );
8990
9091 return true ;
9192}
@@ -208,6 +209,10 @@ bool Qwen35LayerSplitAdapter::snapshot_save(int slot) {
208209 return false ;
209210 }
210211 }
212+ if (!snapshot_draft_features (slot)) {
213+ snapshot_free (slot);
214+ return false ;
215+ }
211216 return true ;
212217}
213218
@@ -216,6 +221,7 @@ void Qwen35LayerSplitAdapter::snapshot_free(int slot) {
216221 for (auto & snap : prefix_snapshots_[(size_t )slot]) {
217222 free_prefix_snapshot (snap);
218223 }
224+ free_draft_feature_snapshot (slot);
219225}
220226
221227bool Qwen35LayerSplitAdapter::snapshot_used (int slot) const {
@@ -225,6 +231,12 @@ bool Qwen35LayerSplitAdapter::snapshot_used(int slot) const {
225231 for (const auto & snap : snaps) {
226232 if (!snap.ctx ) return false ;
227233 }
234+ if (cfg_.run_dflash && cfg_.draft_path ) {
235+ if (draft_feature_snapshots_.size () != (size_t )PREFIX_SLOTS ) return false ;
236+ const auto & draft_snap = draft_feature_snapshots_[(size_t )slot];
237+ if (draft_snap.cur_pos <= 0 || draft_snap.n_tokens <= 0 ||
238+ draft_snap.data .empty ()) return false ;
239+ }
228240 return true ;
229241}
230242
@@ -243,6 +255,109 @@ bool Qwen35LayerSplitAdapter::snapshot_restore(int slot) {
243255 return false ;
244256 }
245257 }
258+ if (!restore_draft_features (slot)) return false ;
259+ return true ;
260+ }
261+
262+ bool Qwen35LayerSplitAdapter::snapshot_draft_features (int slot) {
263+ if (!cfg_.run_dflash || !cfg_.draft_path ) {
264+ free_draft_feature_snapshot (slot);
265+ return true ;
266+ }
267+ if (!snapshot_slot_valid (slot) ||
268+ draft_feature_snapshots_.size () != (size_t )PREFIX_SLOTS ) {
269+ return false ;
270+ }
271+
272+ const auto & snaps = prefix_snapshots_[(size_t )slot];
273+ if (snaps.empty () || !snaps.front ().ctx ) return false ;
274+ const int cur_pos = snaps.front ().cur_pos ;
275+ if (cur_pos <= 0 ) return false ;
276+ const int ring_cap = remote_draft_.active () ? remote_draft_.ring_cap () : feature_ring_.cap ;
277+ const int n_layers = remote_draft_.active () ? remote_draft_.n_target_layers ()
278+ : feature_ring_.n_target_layers ;
279+ const int hidden = remote_draft_.active () ? remote_draft_.hidden_size ()
280+ : feature_ring_.hidden_size ;
281+ if (ring_cap <= 0 || n_layers <= 0 || hidden <= 0 ) return false ;
282+ const int n_tokens = std::min (cur_pos, ring_cap);
283+ const int start_pos = cur_pos - n_tokens;
284+ if (n_tokens <= 0 ) return false ;
285+
286+ auto & snap = draft_feature_snapshots_[(size_t )slot];
287+ snap.cur_pos = cur_pos;
288+ snap.start_pos = start_pos;
289+ snap.n_tokens = n_tokens;
290+ snap.cap = ring_cap;
291+ snap.n_target_layers = n_layers;
292+ snap.hidden_size = hidden;
293+ snap.data .clear ();
294+ snap.data .resize ((size_t )n_tokens * (size_t )n_layers * (size_t )hidden);
295+
296+ if (remote_draft_.active ()) {
297+ return remote_draft_.get_feature_range (start_pos, n_tokens, snap.data );
298+ }
299+
300+ if (!feature_ring_.target_feat ) return false ;
301+ const int fc_in = n_layers * hidden;
302+ const size_t row_bytes = (size_t )fc_in * sizeof (float );
303+ const size_t src_stride = feature_ring_.target_feat ->nb [1 ];
304+ for (int i = 0 ; i < n_tokens; ++i) {
305+ const int ring_slot = (start_pos + i) % ring_cap;
306+ ggml_backend_tensor_get (feature_ring_.target_feat ,
307+ snap.data .data () + (size_t )i * (size_t )fc_in,
308+ (size_t )ring_slot * src_stride,
309+ row_bytes);
310+ }
311+ return true ;
312+ }
313+
314+ void Qwen35LayerSplitAdapter::free_draft_feature_snapshot (int slot) {
315+ if (slot < 0 || draft_feature_snapshots_.size () != (size_t )PREFIX_SLOTS ||
316+ slot >= (int )draft_feature_snapshots_.size ()) {
317+ return ;
318+ }
319+ draft_feature_snapshots_[(size_t )slot] = DraftFeatureSnapshot{};
320+ }
321+
322+ bool Qwen35LayerSplitAdapter::restore_draft_features (int slot) {
323+ if (!cfg_.run_dflash || !cfg_.draft_path ) return true ;
324+ if (slot < 0 || draft_feature_snapshots_.size () != (size_t )PREFIX_SLOTS ||
325+ slot >= (int )draft_feature_snapshots_.size ()) {
326+ return false ;
327+ }
328+
329+ const auto & snap = draft_feature_snapshots_[(size_t )slot];
330+ if (snap.cur_pos <= 0 || snap.start_pos < 0 || snap.n_tokens <= 0 ||
331+ snap.cap <= 0 || snap.n_target_layers <= 0 || snap.hidden_size <= 0 ||
332+ snap.data .empty ()) {
333+ return false ;
334+ }
335+
336+ if (remote_draft_.active ()) {
337+ if (snap.cap != remote_draft_.ring_cap () ||
338+ snap.n_target_layers != remote_draft_.n_target_layers () ||
339+ snap.hidden_size != remote_draft_.hidden_size ()) {
340+ return false ;
341+ }
342+ return remote_draft_.set_feature_range (snap.start_pos , snap.n_tokens , snap.data );
343+ }
344+
345+ if (!feature_ring_.target_feat ||
346+ snap.cap != feature_ring_.cap ||
347+ snap.n_target_layers != feature_ring_.n_target_layers ||
348+ snap.hidden_size != feature_ring_.hidden_size ) {
349+ return false ;
350+ }
351+ const int fc_in = snap.n_target_layers * snap.hidden_size ;
352+ const size_t row_bytes = (size_t )fc_in * sizeof (float );
353+ const size_t dst_stride = feature_ring_.target_feat ->nb [1 ];
354+ for (int i = 0 ; i < snap.n_tokens ; ++i) {
355+ const int ring_slot = (snap.start_pos + i) % snap.cap ;
356+ ggml_backend_tensor_set (feature_ring_.target_feat ,
357+ snap.data .data () + (size_t )i * (size_t )fc_in,
358+ (size_t )ring_slot * dst_stride,
359+ row_bytes);
360+ }
246361 return true ;
247362}
248363
@@ -378,6 +493,7 @@ void Qwen35LayerSplitAdapter::shutdown() {
378493 for (auto & snap : slot) free_prefix_snapshot (snap);
379494 }
380495 prefix_snapshots_.clear ();
496+ draft_feature_snapshots_.clear ();
381497 auto shard_metas = layer_split_shard_metas (shards_);
382498 free_layer_split_snapshot_backends (shard_metas, snapshot_backends_);
383499 if (draft_backend_owned_ && draft_backend_) {
0 commit comments