@@ -169,6 +169,7 @@ def restored_specdecode(
169169def restored_specdecode_fused (
170170 adapter , drafter , verifier , aux_layer_ids , embed_fn , lm_head_fn ,
171171 prompt , gen_tokens , block_size , device , eos_ids ,
172+ block_rtt_ms : float = 0.0 ,
172173) -> Dict [str , Any ]:
173174 """FUSED spec-decode engine (A+B+C): per-block O(L).
174175
@@ -195,12 +196,21 @@ def restored_specdecode_fused(
195196
196197 generated : List [int ] = []
197198 accepts : List [int ] = []
198- t_draft = t_verify = t_extend = 0.0
199+ t_draft = t_verify = t_extend = t_network = 0.0
200+ rtt_s = max (0.0 , block_rtt_ms ) / 1000.0
199201 torch .cuda .synchronize (device )
200202 t0 = time .perf_counter ()
201203 while len (generated ) < gen_tokens :
202204 L = min (block_size , gen_tokens - len (generated ))
203205 cstart = adapter ._past_len # committed length at block start
206+ # Cross-host model: one proposer<->verifier round-trip per block
207+ # (proposer ships the draft block to the verifier host, gets the
208+ # accept/reject + bonus back). Injected as a per-block delay so the
209+ # measured decode throughput reflects the WAN penalty on real compute.
210+ if rtt_s :
211+ tn = time .perf_counter ()
212+ time .sleep (rtt_s )
213+ t_network += time .perf_counter () - tn
204214 bonus = int (adapter .next_token_logits .argmax ().item ())
205215 td = time .perf_counter ()
206216 drafts = drafter .draft_block_cached (
@@ -252,10 +262,12 @@ def restored_specdecode_fused(
252262 "drafter_cached" : round (t_draft , 3 ),
253263 "incremental_verify" : round (t_verify , 3 ),
254264 "ctx_kv_extend" : round (t_extend , 3 ),
265+ "network_rtt" : round (t_network , 3 ),
255266 },
256267 "blocks" : len (accepts ),
257268 "mean_accept_len" : round (sum (accepts ) / len (accepts ), 2 ) if accepts else 0.0 ,
258269 "decode_tokens" : len (generated ),
270+ "block_rtt_ms" : block_rtt_ms ,
259271 }
260272
261273
@@ -275,6 +287,11 @@ def main() -> int:
275287 help = "Skip the un-fused restored spec-decode baseline "
276288 "(already characterized; removes GPU contention for a "
277289 "clean fused-vs-AR steady-state measurement)." )
290+ ap .add_argument ("--rtt-sweep" , default = None ,
291+ help = "Comma-separated per-block RTT values (ms) to model a "
292+ "cross-host proposer<->verifier draft loop. When set, "
293+ "after the co-located run the fused path is re-timed on "
294+ "prompt[0] at each RTT — the WAN-penalty curve (Case 2)." )
278295 ap .add_argument ("--output" , default = None )
279296 args = ap .parse_args ()
280297
@@ -461,6 +478,41 @@ def recall(tokens, ans):
461478 fu_tps = report ["restored_specdecode_fused" ]["decode_tokens_per_s_mean" ]
462479 report ["restored_specdecode_fused" ]["speedup_over_ar_x" ] = (
463480 round (fu_tps / ar_mean , 2 ) if ar_mean else None )
481+
482+ # --- Case 2: cross-host proposer<->verifier WAN-penalty curve ---
483+ if args .rtt_sweep :
484+ rtts = [float (x ) for x in args .rtt_sweep .split ("," ) if x .strip ()]
485+ prompt0 = ids_list [0 ][0 ].tolist ()
486+ sweep = []
487+ for rtt in rtts :
488+ r = restored_specdecode_fused (
489+ adapter , drafter , verifier , aux_layer_ids , embed_fn , lm_head_fn ,
490+ prompt0 , args .max_new_tokens , args .block_size , device , eos_ids ,
491+ block_rtt_ms = rtt )
492+ tps = r ["decode_tokens_per_s" ]
493+ sweep .append ({
494+ "block_rtt_ms" : rtt ,
495+ "decode_tokens_per_s" : tps ,
496+ "vs_ar_x" : round (tps / ar_mean , 3 ) if ar_mean else None ,
497+ "blocks" : r ["blocks" ],
498+ "mean_accept_len" : r ["mean_accept_len" ],
499+ "network_s" : r ["time_breakdown_s" ]["network_rtt" ],
500+ "decode_s" : r ["decode_s" ],
501+ })
502+ print (f"[sd][rtt] { rtt :6.1f} ms/block -> { tps } tok/s "
503+ f"({ sweep [- 1 ]['vs_ar_x' ]} x AR, blocks={ r ['blocks' ]} )" ,
504+ file = sys .stderr , flush = True )
505+ # break-even: highest RTT still >= AR (vs_ar_x >= 1.0)
506+ over_ar = [s ["block_rtt_ms" ] for s in sweep if (s ["vs_ar_x" ] or 0 ) >= 1.0 ]
507+ report ["crosshost_rtt_sweep" ] = {
508+ "ar_baseline_tps" : ar_mean ,
509+ "colocated_fused_tps" : fu_tps ,
510+ "sweep" : sweep ,
511+ "max_rtt_ms_at_or_above_ar" : (max (over_ar ) if over_ar else 0.0 ),
512+ "note" : ("one proposer<->verifier round-trip per block; cloud<->desk "
513+ "RTT is typically 30-150 ms. Quantifies why the cross-host "
514+ "token-level draft data plane is WAN-infeasible." ),
515+ }
464516 out_path = Path (args .output ) if args .output else Path (
465517 f"results/research/k3_specdecode_gpu_bench_{ int (time .time ())} .json" )
466518 out_path .parent .mkdir (parents = True , exist_ok = True )
0 commit comments