Skip to content

Commit e536432

Browse files
committed
Harden DFlash reduced logits and replay guards
1 parent 6478051 commit e536432

6 files changed

Lines changed: 274 additions & 6 deletions

File tree

common/speculative.cpp

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414

1515
#include <algorithm>
1616
#include <cassert>
17+
#include <climits>
1718
#include <cstring>
1819
#include <iomanip>
20+
#include <limits>
1921
#include <map>
2022
#include <unordered_map>
2123
#include <cmath>
@@ -188,6 +190,24 @@ static bool common_dflash_argmax_token_valid(int32_t token_id, int n_vocab) {
188190
return token_id >= 0 && token_id < n_vocab;
189191
}
190192

193+
static int common_dflash_invalid_reduced_logits_next_streak(
194+
int current_streak,
195+
bool valid_draft) {
196+
if (valid_draft) {
197+
return 0;
198+
}
199+
200+
return current_streak < INT_MAX ? current_streak + 1 : current_streak;
201+
}
202+
203+
static bool common_dflash_invalid_reduced_logits_fail_closed(
204+
int invalid_streak,
205+
int threshold) {
206+
return threshold > 0 && invalid_streak >= threshold;
207+
}
208+
209+
static constexpr int DFLASH_INVALID_REDUCED_LOGITS_FAIL_CLOSED_STREAK = 4;
210+
191211
static bool common_dflash_argmax_shape_valid(
192212
const char * where,
193213
int rows_available,
@@ -2075,11 +2095,54 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
20752095
std::vector<int32_t> capture_layers;
20762096
bool target_capture_enabled = true;
20772097
bool gpu_capture_available = false;
2098+
int invalid_reduced_logits_streak = 0;
2099+
bool invalid_reduced_logits_fail_closed = false;
20782100

20792101
bool profile_enabled(uint32_t flags) const {
20802102
return dflash_profile_has(profile_flags, flags);
20812103
}
20822104

2105+
void reset_invalid_reduced_logits_state() {
2106+
invalid_reduced_logits_streak = 0;
2107+
invalid_reduced_logits_fail_closed = false;
2108+
}
2109+
2110+
void note_valid_reduced_logits_draft() {
2111+
reset_invalid_reduced_logits_state();
2112+
}
2113+
2114+
void note_invalid_reduced_logits(
2115+
const char * where,
2116+
int32_t token_raw,
2117+
int row,
2118+
int rows,
2119+
int top_k,
2120+
int committed,
2121+
int cross_len,
2122+
int spec_idx,
2123+
int offset,
2124+
float score) {
2125+
invalid_reduced_logits_streak = common_dflash_invalid_reduced_logits_next_streak(
2126+
invalid_reduced_logits_streak, false);
2127+
2128+
LOG_ERR("dflash: invalid reduced-logits token %d in %s at row=%d/%d "
2129+
"(top_k=%d committed=%d cross_len=%d ring_filled=%d ring_write_pos=%d "
2130+
"gpu_ring=%d cpu_ring_valid=%d invalid_reduced_logits_streak=%d score=%g spec=%d offset=%d)\n",
2131+
token_raw, where && where[0] ? where : "draft", row, rows, top_k,
2132+
committed, cross_len, ring_filled, ring_write_pos,
2133+
gpu_ring_handle ? 1 : 0, cpu_ring_valid ? 1 : 0,
2134+
invalid_reduced_logits_streak, (double) score, spec_idx, offset);
2135+
2136+
if (common_dflash_invalid_reduced_logits_fail_closed(
2137+
invalid_reduced_logits_streak,
2138+
DFLASH_INVALID_REDUCED_LOGITS_FAIL_CLOSED_STREAK)) {
2139+
invalid_reduced_logits_fail_closed = true;
2140+
LOG_ERR("dflash: fail-closed after %d consecutive invalid reduced-logits drafts; "
2141+
"skipping DFlash drafting until the cross-ring state is reset\n",
2142+
invalid_reduced_logits_streak);
2143+
}
2144+
}
2145+
20832146
void discard_cross_ring(const char * reason) {
20842147
if (reason && reason[0]) {
20852148
LOG_WRN("dflash: discarding cross-ring state: %s\n", reason);
@@ -2096,6 +2159,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
20962159
prefill_suffix_seen = false;
20972160
deferred_drafter_kv_tokens = 0;
20982161
ring_write_discarded = true;
2162+
reset_invalid_reduced_logits_state();
20992163
cross_buf.clear();
21002164
common_dflash_reset_drafter_seq_and_kv_cache(ctx_dft, seq_id, reason);
21012165
}
@@ -2556,6 +2620,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
25562620
if (committed_len == 0) {
25572621
return -1;
25582622
}
2623+
if (invalid_reduced_logits_fail_closed) {
2624+
return -1;
2625+
}
25592626

25602627
flush_deferred_drafter_kv_cache("batched draft");
25612628
return build_cross_data(ctx_dft_ext);
@@ -2582,6 +2649,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
25822649
prefill_flush_requested = 0;
25832650
prefill_flush_written = 0;
25842651
prefill_suffix_seen = false;
2652+
reset_invalid_reduced_logits_state();
25852653
llama_dflash_prefill_capture_end(ctx_tgt);
25862654
return;
25872655
}
@@ -2917,6 +2985,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
29172985
if (committed_len == 0) {
29182986
continue;
29192987
}
2988+
if (invalid_reduced_logits_fail_closed) {
2989+
continue;
2990+
}
29202991

29212992
const int64_t t0 = ggml_time_us();
29222993

@@ -2984,8 +3055,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
29843055
}
29853056
const int32_t token_raw = argmax[i * K_flat];
29863057
if (!common_dflash_argmax_token_valid(token_raw, n_vocab)) {
2987-
LOG_ERR("dflash: invalid reduced-logits token %d in %s at row=%d/%d (top_k=%d committed=%d cross_len=%d)\n",
2988-
token_raw, __func__, i, batch_len, K_flat, committed_len, cross_len);
3058+
const float score = argmax_probs ? argmax_probs[i * K_flat] : std::numeric_limits<float>::quiet_NaN();
3059+
note_invalid_reduced_logits(__func__, token_raw, i, batch_len, K_flat,
3060+
committed_len, cross_len, -1, 0, score);
29893061
if (dp.draft_log_probs) {
29903062
dp.draft_log_probs->clear();
29913063
}
@@ -3018,6 +3090,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
30183090
const int64_t t4 = ggml_time_us();
30193091

30203092
n_draft_last = (int) result.size();
3093+
if (!result.empty()) {
3094+
note_valid_reduced_logits_draft();
3095+
}
30213096

30223097
if (profile_enabled(DFLASH_PROFILE_SUMMARY)) {
30233098
const llama_perf_context_data perf_dft = llama_perf_context(ctx_dft);
@@ -3048,6 +3123,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
30483123
if (n_draft <= 0 || committed_len == 0) {
30493124
return;
30503125
}
3126+
if (invalid_reduced_logits_fail_closed) {
3127+
return;
3128+
}
30513129

30523130
const int64_t t0 = ggml_time_us();
30533131

@@ -3127,8 +3205,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
31273205
for (int d = 1; d <= depth_limit && d <= main_path_len && tree.n_nodes < tree_budget; ++d) {
31283206
const int32_t token_raw = argmax[d * K];
31293207
if (!common_dflash_argmax_token_valid(token_raw, n_vocab)) {
3130-
LOG_ERR("dflash tree: invalid reduced-logits token %d in %s at depth=%d/%d (top_k=%d)\n",
3131-
token_raw, __func__, d, depth_limit, K);
3208+
const float score = argmax_probs ? argmax_probs[d * K] : std::numeric_limits<float>::quiet_NaN();
3209+
note_invalid_reduced_logits(__func__, token_raw, d, depth_limit, K,
3210+
committed_len, cross_len, -1, 0, score);
31323211
break;
31333212
}
31343213

@@ -3225,6 +3304,10 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
32253304
}
32263305

32273306
// build visibility matrix [(n_nodes+1) × (n_nodes+1)]
3307+
if (tree.n_nodes > 0) {
3308+
note_valid_reduced_logits_draft();
3309+
}
3310+
32283311
int n = tree.n_nodes + 1;
32293312
tree.visibility.assign(n * n, false);
32303313
tree.visibility[0] = true; // root sees itself
@@ -3536,6 +3619,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
35363619
ring_write_pos = 0;
35373620
ring_filled = 0;
35383621
committed_len = 0;
3622+
reset_invalid_reduced_logits_state();
35393623
return;
35403624
}
35413625

@@ -3556,6 +3640,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
35563640
ring_write_pos = 0;
35573641
ring_filled = 0;
35583642
deferred_drafter_kv_tokens = 0;
3643+
reset_invalid_reduced_logits_state();
35593644
common_dflash_reset_drafter_seq_and_kv_cache(ctx_dft, seq_id, "capture target hiddens");
35603645
const int actual_written = ring_write(to_store, start_offset, true);
35613646
if (ring_write_discarded) {
@@ -4148,6 +4233,18 @@ bool common_dflash_tree_update_requires_cpu_hidden_for_test(
41484233
return !has_cpu_hidden && has_gpu_ring;
41494234
}
41504235

4236+
int common_dflash_invalid_reduced_logits_next_streak_for_test(
4237+
int current_streak,
4238+
bool valid_draft) {
4239+
return common_dflash_invalid_reduced_logits_next_streak(current_streak, valid_draft);
4240+
}
4241+
4242+
bool common_dflash_invalid_reduced_logits_fail_closed_for_test(
4243+
int invalid_streak,
4244+
int threshold) {
4245+
return common_dflash_invalid_reduced_logits_fail_closed(invalid_streak, threshold);
4246+
}
4247+
41514248
// ============================================================================
41524249
// Fork-specific init and dispatch functions
41534250
// ============================================================================
@@ -4540,8 +4637,10 @@ void common_speculative_draft_batch(
45404637
}
45414638
const int32_t token_raw = argmax[(offset + i) * K_flat];
45424639
if (!common_dflash_argmax_token_valid(token_raw, n_vocab)) {
4543-
LOG_ERR("dflash batch: invalid reduced-logits token %d in %s at spec=%d row=%d/%d (top_k=%d offset=%d)\n",
4544-
token_raw, __func__, rs.spec_idx, i, batch_len, K_flat, offset);
4640+
const float score = argmax_probs ? argmax_probs[(offset + i) * K_flat] : std::numeric_limits<float>::quiet_NaN();
4641+
auto * dfl = static_cast<common_speculative_impl_dflash *>(rs.impl);
4642+
dfl->note_invalid_reduced_logits(__func__, token_raw, i, batch_len, K_flat,
4643+
rs.draft_pos_base, rs.cross_len, rs.spec_idx, offset, score);
45454644
if (log_probs) {
45464645
log_probs->clear();
45474646
}
@@ -4579,6 +4678,7 @@ void common_speculative_draft_batch(
45794678
}
45804679
}
45814680
if (!result.empty()) {
4681+
dfl->note_valid_reduced_logits_draft();
45824682
rs.impl->n_gen_drafts++;
45834683
rs.impl->n_gen_tokens += result.size();
45844684
specs[rs.spec_idx]->curr_impl = rs.impl;

common/speculative.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ bool common_dflash_tree_update_requires_cpu_hidden_for_test(
136136
bool has_cpu_hidden,
137137
bool has_gpu_ring);
138138

139+
int common_dflash_invalid_reduced_logits_next_streak_for_test(
140+
int current_streak,
141+
bool valid_draft);
142+
143+
bool common_dflash_invalid_reduced_logits_fail_closed_for_test(
144+
int invalid_streak,
145+
int threshold);
146+
139147
struct common_speculative_deleter {
140148
void operator()(common_speculative * s) { common_speculative_free(s); }
141149
};

ggml/src/ggml-cuda/argmax.cu

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ static __global__ void argmax_f32(
203203
if (warp_id == 0 && lane_id == 0) {
204204
dst[row] = argmax;
205205

206+
if (argmax < 0 || argmax >= ncols) {
207+
const float invalid_score = -FLT_MAX;
208+
int32_t invalid_bits;
209+
memcpy(&invalid_bits, &invalid_score, sizeof(float));
210+
dst[nrows + row] = invalid_bits;
211+
return;
212+
}
213+
206214
if (output_logprob) {
207215
// log_prob = logits[argmax] * inv_temp - (logit_max + log(sum_exp))
208216
float log_prob = rowx[argmax] * inv_temp - logit_max - logf(sum_exp);

0 commit comments

Comments
 (0)