Skip to content

Commit b4ce59b

Browse files
committed
fix(server): restore DFlash state for split targets
1 parent 8415f1e commit b4ce59b

8 files changed

Lines changed: 276 additions & 6 deletions

server/src/common/dflash_draft_ipc.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,64 @@ bool DFlashDraftIpcClient::propose(
122122
#endif
123123
}
124124

125+
bool DFlashDraftIpcClient::get_feature_range(int start_pos, int n_tokens,
126+
std::vector<float> & out) {
127+
#if defined(_WIN32)
128+
(void)start_pos; (void)n_tokens; (void)out;
129+
return false;
130+
#else
131+
FILE * cmd = process_.command_stream();
132+
const int stream_fd = process_.stream_fd();
133+
if (!active_ || !cmd || stream_fd < 0 || ring_cap_ <= 0 ||
134+
start_pos < 0 || n_tokens <= 0 || n_tokens > ring_cap_) return false;
135+
const size_t count =
136+
(size_t)n_tokens * (size_t)n_target_layers_ * (size_t)hidden_size_;
137+
std::fprintf(cmd, "get_feature_range %d %d\n", start_pos, n_tokens);
138+
std::fflush(cmd);
139+
int32_t status = -1;
140+
bool ok = read_exact_fd(stream_fd, &status, sizeof(status)) && status == 0;
141+
if (ok) {
142+
out.assign(count, 0.0f);
143+
ok = read_exact_fd(stream_fd, out.data(), out.size() * sizeof(float));
144+
}
145+
if (!ok) {
146+
std::fprintf(stderr, "draft-ipc get_feature_range failed status=%d\n", status);
147+
}
148+
return ok;
149+
#endif
150+
}
151+
152+
bool DFlashDraftIpcClient::set_feature_range(int start_pos, int n_tokens,
153+
const std::vector<float> & data) {
154+
#if defined(_WIN32)
155+
(void)start_pos; (void)n_tokens; (void)data;
156+
return false;
157+
#else
158+
FILE * cmd = process_.command_stream();
159+
const int stream_fd = process_.stream_fd();
160+
if (!active_ || !cmd || stream_fd < 0 || ring_cap_ <= 0 ||
161+
start_pos < 0 || n_tokens <= 0 || n_tokens > ring_cap_) return false;
162+
const size_t expected =
163+
(size_t)n_tokens * (size_t)n_target_layers_ * (size_t)hidden_size_;
164+
if (data.size() != expected) return false;
165+
const std::string path = process_.next_path("feature_range");
166+
if (!write_binary_file(path, data.data(), data.size() * sizeof(float))) {
167+
std::fprintf(stderr, "draft-ipc write feature range failed: %s\n", path.c_str());
168+
return false;
169+
}
170+
std::fprintf(cmd, "set_feature_range %d %d %s\n",
171+
start_pos, n_tokens, path.c_str());
172+
std::fflush(cmd);
173+
int32_t status = -1;
174+
const bool ok = read_exact_fd(stream_fd, &status, sizeof(status)) && status == 0;
175+
std::remove(path.c_str());
176+
if (!ok) {
177+
std::fprintf(stderr, "draft-ipc set_feature_range failed status=%d\n", status);
178+
}
179+
return ok;
180+
#endif
181+
}
182+
125183
void DFlashDraftIpcClient::close() {
126184
process_.close();
127185
active_ = false;

server/src/common/dflash_draft_ipc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class DFlashDraftIpcClient {
5555
const std::vector<float> & noise_embed,
5656
std::vector<float> & hidden_out);
5757

58+
bool get_feature_range(int start_pos, int n_tokens, std::vector<float> & out);
59+
bool set_feature_range(int start_pos, int n_tokens,
60+
const std::vector<float> & data);
61+
5862
bool active() const { return active_; }
5963
int ring_cap() const { return ring_cap_; }
6064
int hidden_size() const { return hidden_size_; }

server/src/common/dflash_draft_ipc_daemon.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,69 @@ int run_dflash_draft_ipc_daemon(const char * draft_path,
132132
stream_status(stream_fd, 0);
133133
continue;
134134
}
135+
if (cmd == "get_feature_range") {
136+
int start_pos = -1;
137+
int n_tokens = 0;
138+
iss >> start_pos >> n_tokens;
139+
if (start_pos < 0 || n_tokens <= 0 || n_tokens > feature_ring.cap) {
140+
std::fprintf(stderr, "[draft-ipc-daemon] bad get_feature_range: %s\n",
141+
line.c_str());
142+
stream_status(stream_fd, -1);
143+
continue;
144+
}
145+
const int fc_in = feature_ring.n_target_layers * feature_ring.hidden_size;
146+
const size_t row_bytes = (size_t)fc_in * sizeof(float);
147+
const size_t src_stride = feature_ring.target_feat->nb[1];
148+
std::vector<float> data((size_t)n_tokens * (size_t)fc_in);
149+
for (int i = 0; i < n_tokens; ++i) {
150+
const int slot = (start_pos + i) % feature_ring.cap;
151+
ggml_backend_tensor_get(feature_ring.target_feat,
152+
data.data() + (size_t)i * (size_t)fc_in,
153+
(size_t)slot * src_stride,
154+
row_bytes);
155+
}
156+
const size_t bytes = data.size() * sizeof(float);
157+
if (!stream_status(stream_fd, 0) ||
158+
!write_exact_fd(stream_fd, data.data(), bytes)) {
159+
std::fprintf(stderr, "[draft-ipc-daemon] feature range stream failed\n");
160+
break;
161+
}
162+
continue;
163+
}
164+
if (cmd == "set_feature_range") {
165+
int start_pos = -1;
166+
int n_tokens = 0;
167+
iss >> start_pos >> n_tokens;
168+
std::string path = read_line_tail(iss);
169+
if (start_pos < 0 || n_tokens <= 0 || n_tokens > feature_ring.cap ||
170+
path.empty()) {
171+
std::fprintf(stderr, "[draft-ipc-daemon] bad set_feature_range: %s\n",
172+
line.c_str());
173+
stream_status(stream_fd, -1);
174+
continue;
175+
}
176+
const int fc_in = feature_ring.n_target_layers * feature_ring.hidden_size;
177+
const size_t row_bytes = (size_t)fc_in * sizeof(float);
178+
const size_t dst_stride = feature_ring.target_feat->nb[1];
179+
const size_t bytes = (size_t)n_tokens * row_bytes;
180+
std::vector<float> data(bytes / sizeof(float));
181+
if (!read_binary_file_exact(path, data.data(), bytes)) {
182+
std::fprintf(stderr, "[draft-ipc-daemon] read feature range failed: %s\n",
183+
path.c_str());
184+
stream_status(stream_fd, -1);
185+
continue;
186+
}
187+
for (int i = 0; i < n_tokens; ++i) {
188+
const int slot = (start_pos + i) % feature_ring.cap;
189+
ggml_backend_tensor_set(feature_ring.target_feat,
190+
data.data() + (size_t)i * (size_t)fc_in,
191+
(size_t)slot * dst_stride,
192+
row_bytes);
193+
}
194+
ggml_backend_synchronize(backend);
195+
stream_status(stream_fd, 0);
196+
continue;
197+
}
135198
if (cmd == "propose") {
136199
int committed = -1;
137200
int ctx_len = 0;

server/src/common/layer_split_backend.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ GenerateResult LayerSplitBackend::run_from_state(const GenerateRequest & req,
101101
return result;
102102
}
103103
auto t_decode_start = std::chrono::steady_clock::now();
104-
const bool ok = (base_pos == 0 && adapter_->can_dflash_decode())
104+
const bool ok = adapter_->can_dflash_decode()
105105
? adapter_->decode_dflash(req.prompt, base_pos, last_tok, req.n_gen,
106106
result.tokens, out_io)
107107
: adapter_->decode_ar(last_tok, base_pos + (int)req.prompt.size(), req.n_gen,

server/src/qwen35/qwen35_layer_split_adapter.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

221227
bool 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_) {

server/src/qwen35/qwen35_layer_split_adapter.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ class Qwen35LayerSplitAdapter : public LayerSplitAdapter {
8181
private:
8282
bool load_draft();
8383
bool snapshot_slot_valid(int slot) const;
84+
bool snapshot_draft_features(int slot);
85+
void free_draft_feature_snapshot(int slot);
86+
bool restore_draft_features(int slot);
8487

8588
Qwen35LayerSplitAdapterConfig cfg_;
8689
std::vector<Qwen35LayerSplitShard> shards_;
@@ -96,6 +99,16 @@ class Qwen35LayerSplitAdapter : public LayerSplitAdapter {
9699
static constexpr int PREFIX_SLOTS = ModelBackend::kMaxSlots;
97100
std::vector<std::vector<PrefixSnapshot>> prefix_snapshots_;
98101
std::vector<ggml_backend_t> snapshot_backends_;
102+
struct DraftFeatureSnapshot {
103+
int cur_pos = 0;
104+
int start_pos = 0;
105+
int n_tokens = 0;
106+
int cap = 0;
107+
int n_target_layers = 0;
108+
int hidden_size = 0;
109+
std::vector<float> data;
110+
};
111+
std::vector<DraftFeatureSnapshot> draft_feature_snapshots_;
99112

100113
SamplerCfg sampler_;
101114
std::mt19937_64 sampler_rng_{std::random_device{}()};

server/src/server/http_server.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1508,7 +1508,8 @@ void HttpServer::worker_loop() {
15081508

15091509
// Confirm or abort the inline snapshot.
15101510
if (snap_prepared) {
1511-
if (completion_tokens > 0 && !client_disconnected) {
1511+
if (completion_tokens > 0 && !client_disconnected &&
1512+
backend_.snapshot_used(snap_slot)) {
15121513
prefix_cache_.confirm_inline_snap(snap_slot, snap_cut, effective_prompt);
15131514
// Track for shutdown save.
15141515
slot_tokens_[snap_slot] = std::vector<int32_t>(

server/test/test_server_unit.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,8 @@ struct MockLayerSplitAdapter : LayerSplitAdapter {
12411241
int current_last = -1;
12421242
std::vector<int> prefill_bases;
12431243
std::vector<int> prefill_sizes;
1244+
int dflash_base = -1;
1245+
int dflash_last = -1;
12441246
std::vector<int32_t> emitted_tokens;
12451247
bool dflash_enabled = false;
12461248
bool dflash_called = false;
@@ -1277,10 +1279,21 @@ struct MockLayerSplitAdapter : LayerSplitAdapter {
12771279
return true;
12781280
}
12791281
bool can_dflash_decode() const override { return dflash_enabled; }
1280-
bool decode_dflash(const std::vector<int32_t> &, int, int, int,
1281-
std::vector<int32_t> &, const DaemonIO &) override {
1282+
bool decode_dflash(const std::vector<int32_t> & prompt, int base_pos,
1283+
int last_tok, int n_gen, std::vector<int32_t> & out_tokens,
1284+
const DaemonIO & io) override {
1285+
(void)prompt;
12821286
dflash_called = true;
1283-
return false;
1287+
dflash_base = base_pos;
1288+
dflash_last = last_tok;
1289+
for (int i = 0; i < n_gen; ++i) {
1290+
int32_t tok = last_tok + i + 10;
1291+
out_tokens.push_back(tok);
1292+
emitted_tokens.push_back(tok);
1293+
io.emit(tok);
1294+
}
1295+
io.emit(-1);
1296+
return true;
12841297
}
12851298
void free_drafter() override {}
12861299
bool snapshot_save(int slot) override {
@@ -1350,12 +1363,14 @@ static void test_layer_split_backend_inline_snapshot_and_restore_delta() {
13501363
GenerateResult restored = backend.restore_and_generate(2, restore_req, io);
13511364

13521365
TEST_ASSERT(restored.ok);
1353-
TEST_ASSERT(!raw->dflash_called);
1366+
TEST_ASSERT(raw->dflash_called);
13541367
TEST_ASSERT(raw->restored_slot == 2);
13551368
TEST_ASSERT(!raw->reset_called);
13561369
TEST_ASSERT(raw->prefill_bases.size() == 1);
13571370
TEST_ASSERT(raw->prefill_bases[0] == 3);
13581371
TEST_ASSERT(raw->prefill_sizes[0] == 1);
1372+
TEST_ASSERT(raw->dflash_base == 3);
1373+
TEST_ASSERT(raw->dflash_last == 99);
13591374
}
13601375

13611376
static void test_layer_split_compress_nopark_uses_default_drafter_path() {

0 commit comments

Comments
 (0)